Obtaining a Ledger API Token

Authorisation Tokens

ITE1 is a single environment and requires all access to be authenticated and authorised. To meet this requirement, Ledger API authentication and authorisation will be introduced and mandatory in ITE1.

To ensure access to the ledger is restricted to authorised organisations that have specific DAML party level access rights, multi-factor authentication and standards based OAuth 2.0 access tokens are levearaged. Refer to OAuth 2.0 — OAuth for further details.

Tokens have a time-based expiry, applications will need to request a new token prior to the expiry of any current tokens (based on expiry within the current token).

Ledger API users will be required to securely request an authorisation token using a standards based API.

The authorisation token:

  • is signed by the ASX;

  • describes the DAML party level rights;

  • can be inspected by the user (not opaque); and

  • must be used in all Ledger API calls.

The following provides details on how to obtain and use a ledger API OAuth 2.0 access token in order to use the ledger API channel.

First, the Daml (ledger API client) application connects to ASX (the token issuer) to get an access token. The ASX verifies the identity of the requesting (ledger API) user, looks up the privileges of the user, and generates a signed access token describing those privileges.

Then, the Daml application sends the access token along with each ledger API request. The Daml ledger verifies the signature of the token (to make sure it was issued by the expected token issuer and has not been tampered with), and then checks that the privileges described in the token authorize the given ledger API request.

For further general background information on how authorisation works on DAML based ledgers refer to Authorization — Daml SDK documentation and JWT.IO - JSON Web Tokens Introduction.

Multi-Factor Authentication and Authorisation

The below table describes the security controls that restrict access to the ledger to authorised organisations.

Control Type

Control Description

Control Type

Control Description

Network Access

Access to the service is over a private network operated by ASX called ASX Net. ASX Net must be permissioned to allow the customer to see the service end-points required for the ledger API service.

Authentication - Factor 1

The customer must establish a mutual TLS session with the service endpoint. The customer must use the TLS certificate previously downloaded from the ASX CSAM portal. ASX will validate that the TLS certificate used for the connection is authorised for the originating ASX Net address.

Authentication - Factor 2

The customer must use their Ledger API Signing certificate previously downloaded from the ASX CSAM portal to sign a token that is then used as the credential to authenticate to the token issuing service and obtain a ledger API access token from ASX. The access token obtained contains the party level rights and is used to subsequently access the ledger.

Authorisation

The customer must provide their access token as part of each ledger API command submitted to the via the ledger API.

The access token is issued with an expiry time. A new access token must be acquired in order to continue making ledger API commands past the expiry time.

The format must include the word ‘Bearer’ immediately before the token as follows:

1 Authorization: Bearer <long-token-string>

Obtaining a Ledger API Access Token

Prerequisites

As a pre-requisite the Ledger API user must have been created and configured prior to following the below information. If this is not the case, please contact CTS@asx.com.au.

Prerequisite

Description

Prerequisite

Description

Client ID

This is the unique ID associated with your ledger API user that is created when you onboard for a Ledger API Signing service account in the ASX CSAM portal.

It is also equal to the UID field within the subject of the Ledger API Signing certificate, for example; 6f3ff824-b100-49c0-91c5-ff279af61f49

This Client ID is permissioned by ASX with the required DAML party level authorisations.

Ledger API Signing Certificate

This is the digital certificate downloaded from the ASX CSAM portal associated with your ledger API user service account with the Client ID from above. The “Ledger API Signing Certificate” is in PEM format, it contains both the private and public key. The first certificate is the private key, the second certificate is the public key. Save each key into a separate text files, they will be used to create the JWT token.

Overview

There are two steps required to obtain a Ledger API access token from the ASX.

  1. Creation of a signed “Client ID” JWT (JSON Web Token). This ”Client ID” token will be used as the credential to obtain the ledger API access token. The Client ID token contains the Client ID signed by the ledger API Signing private key. The JWT token is created using the private and public key extracted from the “Ledger API Signing Certificate”

  2. Posting the signed JWT to the ASX token issuing end-point. The signed Client ID token is posted to the token issuing endpoint to obtain a Ledger API access token.

The ledger API access token obtained will entitle the bearer to access the ledger with the associated DAML party access rights. As such, you should treat the token securely and not share with anyone. To reduce the risks of token loss or theft, your access token will have a limited lifespan (this may vary between environments but is expected to be in the order of 15 minutes). As your existing token nears expiry you should obtain a new one.

Creating a “Client ID” Token

A signed JWT includes three sections, header, payload and signature.

Creating the Header

The JWT header must have the following fields and values:

Field

Populated with

Description

Field

Populated with

Description

x5c

Public Key

The x509 certificate chain. Set to public key of your ledger API signing certificate

alg

RS256

The specific cryptographic algorithm used with the key. In this case, it is populated with a fixed value of “RS256”.

typ

JWT

The media type of the token. In this case, it is populated with a fixed value of “JWT”.

An example of a working header:

1 2 3 4 5 6 7 { "x5c": [ "MIIDbTCCAlWgAwIBAgIEWRMRKjANBgkqhkiG9w0BAQsFADBnMQswCQYDVQQGEwJhdTEMMAoGA1UECBMDbnN3MQ8wDQYDVQQHEwZzeWRuZXkxDDAKBgNVBAoTA2FzeDEUMBIGA1UECxMLZGV2ZWxvcG1lbnQxFTATBgNVBAMTDERMVCBTb3V0aW9uczAeFw0yMTAxMTQwMzMyMDdaFw0yMTA0MTQwMzMyMDdaMGcxCzAJBgNVBAYTAmF1MQwwCgYDVQQIEwNuc3cxDzANBgNVBAcTBnN5ZG5leTEMMAoGA1UEChMDYXN4MRQwEgYDVQQLEwtkZXZlbG9wbWVudDEVMBMGA1UEAxMMRExUIFNvdXRpb25zMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoMRVegh31Vq+9ZfKIRy+gjJxmC4TeRWYY9Vif4m9hScHWBkGyA9so019L5JzkNtdqjDM5x7X9e9zyPOogFqoVrtxVO96HdOGdnXZXJpOFsTrYEPPjBH+O8VyQq2srXf+9aeAgr//3qSoPHreUel7vD9SzFXvdMQ5yIl12oNu5AyAZMPfxOIXOCEszh1cW2nskiddkMATGYn8UzOT/IYjLbB6CatFi+gm7zQArVGDmyyXXuUTDBC+rQYbJV3TL4JvUCqFwuLvrXOTBcrMPvdwju4vJbYhynGJm72ho8kPgnL9LY0y6Wl+mb56CUkhQmvmituFpaopo2ilE8OVsbFbYQIDAQABoyEwHzAdBgNVHQ4EFgQUksVR9wcUjNlP+7ZXDUziMmPWdq8wDQYJKoZIhvcNAQELBQADggEBAF8jesTIx8SB8FicJFjQLan3nwV2LBdDXS50oDMf6EQ2b5Vwk6MCeVJTEN8fQe8GhoKVQjI6JAw2nIWlLraWbJJmAHdrTn16Tqho8tZVXdu9zPsOL56drlvSjCC14zM1TnPaIWTWz2yFtzxPshDS9puDqbYO4bm2tOT7xkKt7XPPSAVLMITbfj6KPYa9qkwPGTT6gxWZchF23q8V967hfat67zpYHacBmvokWYWswBWxoalICEXwYs4kXFQ6BoM/53h2GsJEq6NNpdy3EJ4dO7JhhVZqt51FNARSAHxkMVajB8Es9tcDw0uIbyY92zks01wtqZUz2AoClAwuTPueSpw=" ], "alg": "RS256", "typ": "JWT" }

Creating the Payload

The JWT payload must have the following fields and values:

Field

Populated with

Description

Field

Populated with

Description

aud

ITE1:

https://ite1-dlt-iam.asx.com.au:443/openam/oauth2/dlt

The audience (“aud”) identifies the recipients that the JWT is intended for.

sub

<client>

The subject (“sub”) identifies the Ledger API user that is the subject of the JWT.

resource

Empty String

Field not required.

scope

com.daml.ledger.api

The scope identifies the data/API requested.

iss

<client>

The "iss" (issuer) identifies the user that issued the JWT.

exp

JWT Claim Expiration Time (epoch UTC time in seconds)

It is recommended that this is set to be less than 120 seconds from claim issuing time.

The Expiry Time (exp) identifies how long the JWT claim is valid for. It represents the time on or after which the JWT must not be accepted for processing.

The processing of the exp requires that the current date/time must be before the expiration date/time listed.

iat

JWT Claim Issuing Time (epoch UTC time in seconds)

The Issued At (“iat”) identifies the time at which the JWT was issued. It can be used to determine the age of the JWT.

jti

Submitter generated Unique Id

The JWT ID (“jti”) provides a unique identifier for the JWT created by the submitter. It can be used to prevent the JWT from being reused.

An example of a working payload:

1 2 3 4 5 6 7 8 9 10 { "aud": "https://ite1-dlt-iam.asx.com.au:443/openam/oauth2/dlt", "sub": "6f3ff824-b100-49c0-91c5-ff279af61f49", "resource": "", "scope": "com.daml.ledger.api", "iss": "6f3ff824-b100-49c0-91c5-ff279af61f49", "exp": 1627351860, "iat": 1627351740, "jti": "63e58899-f4d8-4f2b-aeff-17c9fee5a26e" }

Creating the Signature

Use the private key part the Ledger API Signing certificate to sign the token. For further information regarding creating the signature, refer to the Obtaining a Ledger API Token Java-Example below.

Posting the JWT to the Token Issuer (OAuth 2.0) Endpoint

Use HTTP POST method to send the signed JWT created above to the token issuing OAuth 2.0 endpoint as per the below table:

Environment

Token Issuer OAuth 2.0 Endpoint

Environment

Token Issuer OAuth 2.0 Endpoint

ITE1

https://ite1-dlt-iam.asx.com.au:6005/api/iam/v1/lautoken

The endpoint will authenticate the request and return the Ledger API access token with the DAML party level rights associated with the specified Client ID.

Tools that support HTTP POST such as Postman and Curl can be used to do it manually.

This should only be used for testing and debugging purposes.

Below is an example using Curl command.

1 2 3 4 5 6 7 curl --location --request POST ‘<IAM oauth endpoint>’ \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'scope=com.daml.ledger.api' \ --data-urlencode 'grant_type=client_credentials' \ --data-urlencode 'client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer' \ --data-urlencode 'client_assertion='<Signed JWT>' --cacert <cacert-file>

The cacert-file should be provided by CTS (e.g. ASXEnterpriseCA.certchain.pem).

A successful response from the token issuer will contain the Ledger API access token. It will look similar to the below:

1 {"access_token":"eyJ0eXAiOiJKV1QiLCJraWQiOiJ3VTNpZklJYUxPVUFSZVJCL0ZHNmVNMVAxUU09IiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJhbXFwYWRhcHRlcjEiLCJjdHMiOiJPQVVUSDJfU1RBVEVMRVNTX0dSQU5UIiwiYXVkaXRUcmFja2luZ0lkIjoiYTZlMWQxZGYtZjZmMC00YWNiLTgxZmYtYTkzZDJkNTZhMDg2LTUwMDU1NiIsImlzcyI6Imh0dHBzOi8vYWNjZXNzLmRldi5jaWFtLmRsdC5hc3guaWRhYXMueHl6OjQ0My9vcGVuYW0vb2F1dGgyIiwidG9rZW5OYW1lIjoiYWNjZXNzX3Rva2VuIiwidG9rZW5fdHlwZSI6IkJlYXJlciIsImF1dGhHcmFudElkIjoiMTN1V0JJQlFrVzRFNXJoUnVoMy1VNk1NcjZrIiwiYXVkIjoiYW1xcGFkYXB0ZXIxIiwibmJmIjoxNjI3MzUxNzM3LCJncmFudF90eXBlIjoiY2xpZW50X2NyZWRlbnRpYWxzIiwic2NvcGUiOlsiY29tLmRhbWwubGVkZ2VyLmFwaSJdLCJhdXRoX3RpbWUiOjE2MjczNTE3MzcsInJlYWxtIjoiL2RsdCIsImV4cCI6MjU3MzQzMTczNywiaWF0IjoxNjI3MzUxNzM3LCJleHBpcmVzX2luIjo5NDYwODAwMDAsImp0aSI6InB3TVhkX0xOV2tJdXphelFwV245U3pUSzN2TSIsImh0dHBzOi8vZGFtbC5jb20vbGVkZ2VyLWFwaSI6eyJtZXRhIjp7Im9yZ2FuaXphdGlvbklkIjpudWxsLCJhdXRob3JpemVkSXAiOiIxMjcuMC4wLjEifSwiYXBwbGljYXRpb25JZCI6IkluZ3Jlc3MtQk1XIiwicGFydGljaXBhbnRJZCI6bnVsbCwibGVkZ2VySWQiOm51bGwsImFkbWluIjpmYWxzZSwiYWN0QXMiOlsiMjAwMDQiLCIxOTAwMSIsIjAxMTExIiwiMDEwMTEiLCIxOTAwNSIsIjE5MzIxIiwiMDExMTUiLCIwMzUyMCIsIjIxMDEzIiwiMDExMDYiLCIwMTExMyIsIjAxMTAzIiwiMTkwOTkiLCIwMTEwMSIsIjAxMTE2IiwiMDMzNzEiLCIyMDAwMSIsIjIwMDA1IiwiMDExMjAiLCIyMDAwMyIsIjAxMTEwIiwiMDEwMTIiLCIwMTEwOSIsIjE5MzIwIiwiMDM1MjEiLCIwMTExNCIsIjAxMTA3IiwiMDExMDUiLCIwMTExMiIsIjExMDc4IiwiMDExMDQiLCIwMTEwMCIsIjAxMTE3IiwiMDExMTkiLCIwMDAxMiIsIjAzMzcwIiwiMTAwMDEiLCIyMDAwMCIsIjIwMDAyIl19fQ.OAUjpzkYdITRjlB9jnVjqHa8ndzhpgiklwz9fw_ulUuM51oCRpGnvWXteoRRpkVoyVKTTrFmJjIt4VhUpcF6s-kzORGOjGuDdCN6lqKR9ILmieu9Fy4pmcBPsgui0bbSV1FI_PFP7YVdN7GFvcgpZAuStckH7QfCMFIwwBH_bt-feGQi1aPp-bqPST_ReuWF6cdMQAHArgkw3VQ9xJi4lle4azGLCwPhHy2oqYbOq1_UAD5u9Rv42q3quUDbUfUjoAmVUkUEiq-mz1-wD7y873esI92cpNkC26BuLJvf-SyVK8wxOK2lER36wQrUP_3nlihLyLBJ4E9CFvhrc17Udw","scope":"com.daml.ledger.api","token_type":"Bearer","expires_in":946079999}

For further information regarding the Ledger API token, refer to Understanding the Ledger API Token.

Error Code Handling

When an issue occurs, errors are returned with a status code of 400 accompanied by a reason phrase. The reason phrase is composed of a description of the error and summary.

1 2 3 4 status code: 400, reason phrase: {"error_description":"Invalid JWT audience","error":"invalid_request"} status code: 400, reason phrase: {"error_description":"Client authentication failed","error":"invalid_client"} status code: 400, reason phrase: {"error_description":"Invalid JWT issuer","error":"invalid_request"} status code: 400, reason phrase: {"error_description":"Unknown/invalid scope(s): [com.daml.ledger.api1]","error":"invalid_scope"}

Java Example

The below example uses Java to demonstrate how to obtain a token from IAM. The following sample Java code addresses the following two areas:

  1. Creating the initial JWT token using the private and public key (obtained from “Ledger API Signing Certificate”); and

  2. Sending the request to the IAM service to obtain the Ledger token.

Using Java to Obtain a Token

Dependency

The following library is used in the sample code to obtain a token:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>fluent-hc</artifactId> <version>4.5.13</version> </dependency> <dependency> <groupId>com.nimbusds</groupId> <artifactId>nimbus-jose-jwt</artifactId> <version>9.10.1</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.10.3</version> </dependency> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcpkix-jdk15on</artifactId> <version>1.68</version> </dependency>

Configurations and properties

The configurations are used in the sample code:

  • subject (Client Id)

  • issuer (Client Id)

  • resource (Blank)

  • audience (value provided by ASX)

  • scope (constant, always set to com.daml.ledger.api)

  • oauth2tokenendpoint (endpoint for posting the JWT claim, value provided by ASX)

  • publickey (X.509 certificate)

  • privatekey (PKCS8 format)

1 2 3 4 5 6 7 8 resource= audience=https://ite1-dlt-iam.asx.com.au:443/openam/oauth2/dlt issuer=<Client ID> subject=<Client ID> oauth2tokenendpoint=https://ite1-dlt-iam.asx.com.au:6005/api/iam/v1/lautoken scope=com.daml.ledger.api publickey=<PEM file location - the file should be located on the java classpath> privatekey=<Private key file location - the file should be located on the java classpath>

Import Required Libraries

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.nimbusds.jose.JOSEException; import com.nimbusds.jose.JWSAlgorithm; import com.nimbusds.jose.JWSHeader; import com.nimbusds.jose.JWSSigner; import com.nimbusds.jose.crypto.RSASSASigner; import com.nimbusds.jose.util.Base64; import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.SignedJWT; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.client.HttpResponseException; import org.apache.http.client.fluent.Form; import org.apache.http.client.fluent.Request; import org.apache.http.client.fluent.Response; import org.apache.http.util.EntityUtils; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.pkcs.RSAPrivateKey; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.util.io.pem.PemObject; import org.bouncycastle.util.io.pem.PemReader; import java.io.*; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.security.*; import java.security.KeyFactory; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.RSAPrivateKeySpec; import java.time.Clock; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.UUID;

Write a Function to Create Signed JWT

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 private String createSignedJwt(X509Certificate cert, PrivateKey privateKey) throws CertificateEncodingException, JOSEException { List<com.nimbusds.jose.util.Base64> certChain = new ArrayList<>(); certChain.add(Base64.encode(cert.getEncoded())); JWSHeader jwtHeader = new JWSHeader.Builder(JWSAlgorithm.RS256) .x509CertChain(certChain) .build(); JWTClaimsSet claims = new JWTClaimsSet.Builder() .audience(audience) .subject(subject) .claim("resource", "") .issuer(issuer) .claim("scope", scope) .jwtID(UUID.randomUUID().toString()) // Must be unique for each grant .issueTime(new Date(Clock.systemUTC().millis())) // Use UTC time! .expirationTime(new Date(Clock.systemUTC().millis() + 120000)) // Expiration time is 120 sec. .build(); JWSSigner signer = new RSASSASigner(privateKey); SignedJWT signedJWT = new SignedJWT(jwtHeader, claims); signedJWT.sign(signer); return signedJWT.serialize(); }

Post the JWT to IAM Endpoint to Get Token

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 //Loading certificate using pem file File publicKeyPem = new File("public_cert.pem"); CertificateFactory fact = CertificateFactory.getInstance("X.509"); FileInputStream is = new FileInputStream(publicKeyPem); X509Certificate cer = (X509Certificate) fact.generateCertificate(is); PublicKey key = cer.getPublicKey(); is.close(); //This part of code works for PKCS#8 format key File privateKeyFile = new File("private_key_file_name.key"); KeyFactory kf = KeyFactory.getInstance("RSA"); Security.addProvider(new BouncyCastleProvider()); InputStream inputStream = new FileInputStream(privateKeyFile); PemReader pemReader = new PemReader(new InputStreamReader(inputStream)); PemObject pemObject = pemReader.readPemObject(); byte[] content = pemObject.getContent(); pemObject.getType(); PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(content); PrivateKey privateKey_pkcs8 = kf.generatePrivate(spec); String signedJwt = createSignedJwt(cert, privatekey); List body = Form.form() .add("grant_type", "client_credentials") .add("scope", scope) .add("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer") .add("client_assertion", signedJwt) .build(); Response response = null; response = Request.Post(oauth2TokenEndpoint) .bodyForm(body) .execute(); HttpResponse httpResponse = response.returnResponse(); HttpEntity e = httpResponse.getEntity(); activeToken = EntityUtils.toString(e); int statusCode = httpResponse.getStatusLine().getStatusCode(); if (statusCode != HttpStatus.SC_OK) { throw new HttpResponseException(statusCode, activeToken); } ObjectMapper mapper = new ObjectMapper(); JsonNode actualObj = mapper.readTree(activeToken); activeToken = actualObj.get("access_token").asText("");

Understanding the Ledger API Token

Expiry (exp)

The format of token expiry (exp) is provided in a Unix Timestamp Format. This count starts at the Unix Epoch on January 1st, 1970 at UTC and represents the number of seconds between a particular date and the Unix Epoch.

Using the Expiry (exp) Field

The exp value can be used to determine when a token will expire. Applications should note this time and ensure they request a replacement token as this nears expiry noting that applications can have several active tokens at the same time (i.e. getting a new one does not invalidate the old one). This is possible by:

  • converting the exp to a timestamp to workout when a new token is required; or

  • leveraging auth_time to workout when the token was issued and expires_in to work out when it will expire.

auth_time represents the epoch UTC time in seconds of time that token was created

expires_in represents the number of seconds the token will be valid for after creation

Token Payload

This table lists the valid fields, allowed values and how the field is validated.

  • Scope is constant and always set to com.daml.ledger.api;

  • The token payload field are encapsulated within "https://daml.com/ledger-api", as demonstrated in the below example;

  • All payload fields are optional for forward/backward compatibility reasons;

  • Extra fields are ignored when reading; and

  • Null values and missing fields map to ‘None’ or a safe default value (if there is one), as demonstrated in the below example.

1 2 3 4 5 6 7 8 9 10 11 { "https://daml.com/ledger-api": { "ledgerId": "aaaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", "participantId": null, "applicationId": null, "admin": true, "actAs": ["Alice"], "readAs": ["Bob"] }, "exp": 1300819380 }

For further information, refer to Sandbox Authentication — Daml SDK documentation.

 Field

Description

Valid Values

ASX Validation

 Field

Description

Valid Values

ASX Validation

ledgerId

If set, the claims will only be valid on the given ledger.

 

If provided, it must contain the UUID of the ledger or null (refer example below table)

If provided and if not null checks value matches the UUID of the ledger the Client Node is a member of

 

participantId

If set, the claims will only be valid on the given participant node

 

If provided, it must contain the UUID of the Participant node or null (refer example below table)

If provided and if not null checks value matches the UUID of the Client Node

applicationId

If set, the token is only valid for the given application ID.

 

If provided, it must contain the application ID provided in the ledger API command or null

If provided and if not null checks value matches the application ID provided in the ledger API command

admin

Whether the token bearer is authorized to use admin endpoints of the ledger API.

true or false (refer example below table)

Must be true to use ledger API services that require admin permissions. For further information, refer to Authorization — Daml SDK documentation.

actAs

List of parties the token bearer can act as.

A list of DAML parties

Matching party must be present when attempting to do ledger API operations requiring actAs permission

readAs

List of parties the token bearer can read data for

A list of DAML parties

Matching party must be present when attempting to do ledger API operations requiring readAs permission

exp

If set, the token is only valid before the given instant.

This is a registered claim in JWT

A JWT expiration timestamp

If provided, the expiration timestamp must be greater than or equal to the current time

This value can be used to determine when the token will expire. Applications should note this time and ensure they request a replacement token as this nears expiry noting that applications can have several active tokens at the same time (i.e. getting a new one does not invalidate the old one).

Example Ledger API Token

Following the above steps will generate a Ledger API token. The string returned from the Java code and Curl command contains encoded JWT token. The following is an example of how this appears:

1 eyJ0eXAiOiJKV1QiLCJraWQiOiJ3VTNpZklJYUxPVUFSZVJCL0ZHNmVNMVAxUU09IiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJUZXN0S2l0NTIiLCJjdHMiOiJPQVVUSDJfU1RBVEVMRVNTX0dSQU5UIiwiYXVkaXRUcmFja2luZ0lkIjoiNTViNWE5Y2YtMmExNS00MWU3LWE2MmItYWU2NGIzNDM2N2JlLTExOTciLCJpc3MiOiJodHRwczovL2dhdGV3YXkuaXRlMS5jaWFtLmRsdC5nbGItcWEuYXN4LmNvbS5hdTo0NDMvb3BlbmFtL29hdXRoMi9kbHQiLCJ0b2tlbk5hbWUiOiJhY2Nlc3NfdG9rZW4iLCJ0b2tlbl90eXBlIjoiQmVhcmVyIiwiYXV0aEdyYW50SWQiOiJTMHRHaWY0Zl9jVDZINGd3cVhINy1haE92bkUiLCJhdWQiOiJUZXN0S2l0NTIiLCJuYmYiOjE2MzY2Nzc1NDMsImdyYW50X3R5cGUiOiJjbGllbnRfY3JlZGVudGlhbHMiLCJzY29wZSI6WyJjb20uZGFtbC5sZWRnZXIuYXBpIl0sImF1dGhfdGltZSI6MTYzNjY3NzU0MywicmVhbG0iOiIvZGx0IiwiZXhwIjoxNjM2NzYzOTQzLCJpYXQiOjE2MzY2Nzc1NDMsImV4cGlyZXNfaW4iOjg2NDAwLCJqdGkiOiJvUmczUzhsOVFobUNiT0RfYWduR0FsU2UtcGciLCJodHRwczovL2RhbWwuY29tL2xlZGdlci1hcGkiOnsibWV0YSI6eyJvcmdhbml6YXRpb25JZCI6bnVsbCwiYXV0aG9yaXplZElwIjoiTm90IEtub3duIn0sImFwcGxpY2F0aW9uSWQiOm51bGwsInBhcnRpY2lwYW50SWQiOm51bGwsImxlZGdlcklkIjpudWxsLCJhZG1pbiI6ZmFsc2UsImFjdEFzIjpbIjAxNTIxIiwiMDE1MjIiLCIwMTUyMCIsIjE1NTIwIiwiMTU1MjEiLCIwMzUyMSIsInB1YmxpYyIsIjIwNTIyIiwiMjA1MjEiLCIyMDUyMCIsIjA0NTIxIiwiMDQ1MjAiLCIwMTUyMyJdfX0.sxkF3Ql6crdBxlxuOyiodHLMUbXnRghUOxajszMAekP5k1CJ5qTTvMiIt1LLz8YOT2KVjMmkQ5ihpgN4YsE5ib3Kz73wwJ73HG2iCYfbqjYK9da93AMekuV_M0wXz7EzW-lp9m3oqLROr2D1wPJITaV14PYJ9dox2E2jUKcpNk-czNmY5t4iPzUXEy7rPJC2yEToOVTSOoyeUzAl5h74zmhJXvULbXjlK7m8zu-llkpf4_hVgdMYmX8sjzg91Q3WEPMT6kr8awloz4o4O5IVwTMDeszKmUkPhqK17mlq-rRRowsFFWA4a12Gy1QIF0wQQ0IL2I_w-qQS4qpyY4tRpw

The token is also in a JWT compliant format. The token can be parsed in JWT.IO.

Alternative the token can be parsed using this sample Java code below.

1 2 3 4 5 6 7 private String decodeToken(String token){ String[] chunks = token.split("\\."); java.util.Base64.Decoder decoder = java.util.Base64.getDecoder(); String header = new String(decoder.decode(chunks[0])); String payload = new String(decoder.decode(chunks[1])); return header + payload; }

Header:

1 2 3 4 5 { "typ": "JWT", "kid": "wU3ifIIaLOUAReRB/FG6eM1P1QM=", "alg": "RS256" }

Payload:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 { "sub": "TestKit52", "cts": "OAUTH2_STATELESS_GRANT", "auditTrackingId": "55b5a9cf-2a15-41e7-a62b-ae64b34367be-1197", "iss": "https://gateway.ite1.ciam.dlt.glb-qa.asx.com.au:443/openam/oauth2/dlt", "tokenName": "access_token", "token_type": "Bearer", "authGrantId": "S0tGif4f_cT6H4gwqXH7-ahOvnE", "aud": "TestKit52", "nbf": 1636677543, "grant_type": "client_credentials", "scope": [ "com.daml.ledger.api" ], "auth_time": 1636677543, "realm": "/dlt", "exp": 1636763943, "iat": 1636677543, "expires_in": 86400, "jti": "oRg3S8l9QhmCbOD_agnGAlSe-pg", "https://daml.com/ledger-api": { "meta": { "organizationId": null, "authorizedIp": "172.27.231.999" }, "applicationId": null, "participantId": null, "ledgerId": null, "admin": false, "actAs": [ "01521", "01522", "01520", "15520", "15521", "03521", "public", "20522", "20521", "20520", "04521", "04520", "01523" ] } }

Using the Ledger API token in Java

The token string can be used in the command submission directly without decoding. The below sample code demonstrated how to use the API token to retrieve ledger ID.

1 2 3 4 5 6 DamlLedgerClient.Builder builder = DamlLedgerClient.newBuilder("Localhost", 6865); builder.withAccessToken(token); DamlLedgerClient client = builder.build(); client.connect(); String ledgerId = client.getLedgerId(); System.out.println(ledgerId);