Disclaimer: These are planning documents. The functionalities described here may be unimplemented, partially implemented, or implemented differently than the original design.

Cylinder JWT Authentication

Summary

Cylinder JWT authorization for Splinter’s REST APIs provides secure authentication for non-browser and non-user Splinter clients. This authentication method will be used by the Splinter CLIs and integration components.

Motivation

Splinter’s REST API provides routes for the Admin Service, Scabbard, Biome, and other Splinter components. Because it is desirable that the REST API be accessible to a variety of applications, it is important to have a robust authentication and authorization system implemented. While OAuth2 support provides standards-based authentication for browser applications, Cylinder JWT support will enable non-user and non-browser clients to easily authenticate with the REST API.

Cylinder JWTs are convenient because Cylinder is already an important part of the Splinter ecosystem; it is the library used to sign and verify signatures by the Splinter daemon itself as well as the Splinter CLIs. This makes it a prime candidate for REST API authentication. Additionally, Cylinder JWTs require no logins, handshakes, or other setup processes. They are self-signed and independently verifiable, which makes them both versatile and easy-to-use.

Guide-level explanation

The Cylinder library provides all the tools necessary to create and verify Cylinder JWTs.

Creating a Cylinder JWT

Splinter clients will create JWTs with the following steps:

  1. Load a Cylinder PrivateKey from the filesystem or another source
  2. Initialize a signing Context (such as the Secp256k1 context) that matches the context used by the Splinter REST API
  3. Create a Signer using the private key and context
  4. Initialize a JsonWebTokenBuilder
  5. Build and sign the JWT using the Signer

The output of this process is a Cylinder JWT signed by the private key. This JWT can then be used in any request to the Splinter REST API by providing it in the Authorization HTTP header in the format: Authorization: Bearer Cylinder:<jwt>.

Verifying a Cylinder JWT

The Splinter REST API will verify the Cylinder JWT and extract the signer’s public key through the following process:

  1. Parse the Cylinder JWT from the Authorization header when the Bearer Cylinder: prefix is present
  2. Initialize a signing Context for the same algorithm used to sign the JWT
  3. Create a signature Verifier using the context
  4. Initialize a JsonWebTokenParser
  5. Use the parser to parse the JWT and verify the signature
  6. Get the issuer from the parsed/verified JWT, which is the public key of the JWT signer

The public key is then used as the client’s identity throughout the Splinter REST API’s authorization system.

Reference-level explanation

Cylinder JWT Format

Cylinder JWTs are a custom implementation of the JWT standard. At a minimum all Cylinder JWTs contain the following data:

  • alg header - the name of the algorithm used to sign the JWT. This is automatically set based on the type of signer used to sign the JWT. The splinterd REST API and its clients use the secp256k1 algorithm.
  • typ header - the type of JWT. This is automatically set to the string cylinder+jwt by the JWT builder.
  • iss claim - the public key of the JWT’s signer, formatted as a hex string. This is automatically set by the JWT builder.

The JWT builder allows setting additional headers and claims, but these are not used by the Splinter REST API.

For more on the Cylinder JWT format, see the Cylinder JWT design.

Identity Provider

The Splinter REST API guards itself such that only properly authorized clients’ requests are accepted. This functionality is described in the REST API Authorization design.

An implementation of the IdentityProvider trait (defined in the REST API authorization design) will be added for authenticating clients that use Cylinder JWTs. This implementation will be defined in the splinter::rest_api::auth::identity::cylinder module as the CylinderKeyIdentityProvider.

The CylinderKeyIdentityProvider will require that the Authorization header specified with REST API requests has the value Bearer Cylinder:<jwt>, where <jwt> is a valid Cylinder JWT. The token type (Cylinder) is required to inform the Splinter REST API what type of token is being provided; this differentiates Cylinder JWTs from the other authentication types supported by Splinter (Biome and OAuth). If the token type is not provided or if the authorization header is otherwise malformed, the Splinter REST API will respond with 401 Unauthorized.

This identity provider will use the JsonWebTokenParser to verify the JWT and extract the signer’s public key as described in the Verifying a Cylinder JWT section of this document.

This identity provider will be configured for the REST API, which will call it for each request to get the client’s identity.

Configuration

To configure Cylinder JWT authentication for the Splinter REST API, the library user will need to provide a Verifier for verifying JWT signatures. This verifier is created as described in the Verifying a Cylinder JWT section of this document. The Splinter daemon (splinterd) uses the secp256k1 algorithm and verifier, but any algorithm can be used as long as there is a Verifier implementation for it.