In Part 1 of this blog, we described how to set up a PKI infrastructure and configure the DAML Ledger Server to use secure TLS connections and mutual authentication. This protects data in transit and only authorised clients can connect.
An application will need to issue DAML commands over the secure connection and retrieve the subset of contract data that it is authorised to see. To enable this, the Ledger Server uses HTTP security headers (specifically “Authorization” Bearer tokens) to receive an authorization token from the application that describes what it is authorised to do.
The user or application is expected to authenticate against an Identity Provider and in return receive an authorization token for the Ledger. This is presented on every API call.
What are JWT & JWKS?
Java Web Tokens (JWT) is an industry standard way to transmit data between two parties. Full details on JWT can be found at the JWT: Introduction and the associated JWT Handbook. Here we will provide a summary of the specification and how the Ledger Server uses custom claims to define allowed actions of an application.
JWTs are a JSON formatted structure with a HEADER, a PAYLOAD and a SIGNATURE. The Header defines the algorithm used to process the payload, in particular the algorithm used to sign (or encrypt) the payload. The payload contains the details of the authorization given to the application and the signature is over the structure to ensure it has not been tampered in transit. Each section is then base64 encoded with a dot separator between the sections. This is placed in the Authorization HTTP header to pass as part of each HTTP request.
An end-user or application will obtain the token by first authenticating to an Identity Provider and being issued an access token. oAuth protocol defines several means to achieve this including web users (3-way handshake that also ask for consent from human end-user) and applications (2-step Client Credentials Flow, which uses a client_id and client_secret for machine accounts). The Identity Provider will validate the provided credentials and issue a signed token for the request service or API.
So how does Ledger Server get the public key of the signer so it can validate the signature and trust the token. This is where JSONWeb Key Sets (JWKS) comes in. Each Identity Provider publishes a well known URL and we configure the Ledger Server to query this to retrieve the JKWS structure. This contains the public key of the signer and some additional metadata.
In the previous blog post you may have noticed a parameter to the Ledger Server as follows:
This tells Ledger Server to trust tokens generated from, in this case, a specific Auth0 tenant and to use the URL to get the Auth0 JWKS. It also enforces a key signing using RSA keys - RSA keys with a SHA-256 hash function.
JWT can also use other algorithms including Elliptic Curve (ES256 - EC P-256 cipher with SHA-256) and shared secret (HS256). We do not recommend using HS256 for anything more than development / testing as it is open to bruteforce attack of the shared password.
In-depth JWT Example
To give some more detail, an authenticated application submits an Ledger API command over HTTPS (GRPC or JSON) and provides a security header
The token string is of the format:
HEADER.PAYLOAD.SIGNATURE
If we separate out the sections of the JWT token you would get
This is the header, payload and signature encoded as base64 with each section separated by a dot. This is normally a single string.
After decoding (using the provided script ./decode-jwt.sh <filename>) or via the JWT Debugger (https://jwt.io/), the JWT becomes the Header and Payload portions as follows.
These represent the header and payload sections. What does this tell us?
The header shows that the JWT was signed using RS256 and used a specific key (kid value). An identity provider may have multiple signing keys described in the JKWS and this selects which one to use to verify the JWT.
The payload contains many standard attributes (the three letter combinations) and one custom claim ("https://daml.com/ledger-api"). The standard attributes include:
Tag | Description |
alg | Algorithm used for signing, here RS256 [checked by API] |
aud | Audience for token |
azp | Authorized Party |
exp | Expiry in Epoch seconds [checked by API] |
gty | Grant type |
iat | Issued at in Epoch seconds |
iss | Issuer |
sub | Subject (i.e account name) |
The custom claim ("https://daml.com/ledger-api") details a variety of capabilities for this application:
Tag | Description |
admin | Is the application allowed to call to administrative API functions (true/false) |
actAs | An array of Ledger Party IDs that the application is allowed to submit DAML Commands as |
readAs | An array of Ledger Party IDs that the application is allowed to read contracts for |
ApplicationId | A unique ID for the application. If set then Ledger Server will validate that the submitted commands also have this AppID set |
LedgerId | The Ledger ID of the Ledger that the application is trying to connect. |
The authorizing Identity Management provider is expected to set these to appropriate values for the application that is requesting access.
Full details of the API and exposed service endpoints is available in the DAML Documentation. Details of the API and associated permissions is summarised in the Core Concepts section of the sample repo:
Public services are available to any application connecting to a ledger (Mutual TLS may restrict this but a valid token, with minimally not admin and no parties, is still required). Administrative services are expected to be used by specific applications or operational tooling. The remaining Contracts, Command and Transaction services are restricted to the set of parties the application is authorised for.
JWKS (JSON Web Key Sets)
The final piece of the puzzle is JSON Web Key Sets (JWKS) which an identity provider exposes to distribute its public key to allow signature verification.
An example JWKS format is:
The details for each key, the algorithm being used (RS256), the key type (RSA), use (signatures), the key ID (kid value for which Auth0 uses the key fingerprint x5t), the public key (x5c) and some RSA key parameters (n and e fields. Other fields will be seen for EC keys). JWKS supports the distribution of private keys with additional fields for the private key but this is not used here.
A receiving service (in this case the Ledger Server API) will use this to validate the signed JWT to validate that it was issued by the trusted provider and is unaltered.
Using an example Identity Provider - Auth0
So now we have described JWT and JWKS, how do we use these standards? The following builds on the previous post Easy authentication for your distributed app with DAML and Auth0 that focused on end-user authentication and authorisation. You may want to read this first.
In the reference sample, we provide two options:
Auth0
The full detailed steps and scripts are described in the reference documentation. To use Auth0 you will need to do the following:
Rule: "Onboard user to ledger"
Rule: "Add Ledger API Claims"
User Metadata
Once this is in place, you can then update the following:
The Auth0 environment is now ready for use.
Local JWT Provider
Since depending on third party services is complicated for automated testing environments, we implemented a sample JWT provider that uses code signing certificates issued from the local PKI.
In particular, you can set an option in the env.sh script to require the environment to use a local signer. In this model the following is used:
The steps are in the following scripts:
The tokens are issued with an expiry of one day.
Summary and Next Steps
In this post we reviewed the JWT and JWKS Standards to allow an application to request an authorization token from an Identity Provider and submit with Commands to a Ledger. We showed how to use a sample identity provider (in this case Auth0) to allow end-user and service account authentication and get appropriate authorization tokens.
Next step is to run the sample environment and execute some tests against the environment. This is the topic for the final part of this series.If you want to see the first part on “PKI and certificates” please check here:
Read the first part on PKI and certificates