Use mock-oauth2-server with Canton Ledger API

Background

The Ledger API can be configured to require a JWT for authorization purposes. See the docs here and here.

In a production deployment, you would point Canton to a service like Auth0. But what if you want to just play with it locally? Or what if you would like to setup automated integration tests, including JWT tokens with a variety of claims?

This demo configures an instance of mock-oauth2-server
as the authorization service for a Canton participant in a Docker Compose network.

This demo will enable you to:

  • Experiment with various auth-related Canton configuration options.
  • Experiment with the relationship between JWT tokens and Canton User Management.
  • Examine the response of the Ledger API endpoints with and without various JWT claims.
  • Consider testing JWT-dependent scripts in a local environment.
  • Consider building JWT-dependent, automated integration tests.

Setup

Download the code for the demo:

git clone https://github.com/wallacekelly-da/daml-public-demos.git \
    --branch mock-oauth2-docker-compose \
    mock-oauth2-docker-compose

Demo Steps

  1. Peruse the Canton configuration.

    • mydomain.conf, participant1.conf, and participant2.conf
    • Notice that participant1 does not require a JWT; participant2 does require a JWT.
    • bootstrap.canton allocates three parties and three users per participant node.
  2. Peruse the mock-oauth2-server configuration in mockauth.json. Notice the four mappings based on mock_token_type.

  3. Start the Docker Compose network.

    Using the open-source images:

    docker compose up --detach participant1 participant2
    

    Or using the enterprise images:

    docker compose \
       --file docker-compose.yaml \
       --file docker-compose.enterprise.yaml \
       up --detach participant1 participant2
    
  4. Start a console for running the demo’s remaining commands.

    docker compose run --rm --build console
    
  5. Within the Docker Compose-hosted console, create a variety of tokens:

    Audience-based token, for user wallace:

    curl -s http://mockauth/mockissuer/token \
       -d grant_type=client_credentials \
       -d client_id=ignored \
       -d client_secret=ignored \
       -d mock_token_type=audience \
       -d participant=`cat "./configs/participant2.id"` \
       -d sub=wallace \
       | jq -r '.access_token' \
       > audience.token
    

    Scope-based token, for user david:

    curl -s http://mockauth/mockissuer/token \
       -d grant_type=client_credentials \
       -d client_id=ignored \
       -d client_secret=ignored \
       -d mock_token_type=scope \
       -d participant=`cat "./configs/participant2.id"` \
       -d sub=david \
       | jq -r '.access_token' \
       > scope.token
    

    Custom claims token, for the operator party:

    curl -s http://mockauth/mockissuer/token \
       -d grant_type=client_credentials \
       -d client_id=ignored \
       -d client_secret=ignored \
       -d mock_token_type=custom \
       -d actAs=`cat "./configs/participant2-operator.id"` \
       -d readAs=`cat "./configs/participant2-operator.id"` \
       | jq -r '.access_token' \
       > custom.token
    

    Custom admin claims token:

    curl -s http://mockauth/mockissuer/token \
       -d grant_type=client_credentials \
       -d client_id=ignored \
       -d client_secret=ignored \
       -d mock_token_type=custom-admin \
       | jq -r '.access_token' \
       > admin.token
    
  6. View the JWTs:

    For example, to view the audience-based token:

    cat audience.token
    

    To view the token claims:

    cat audience.token \
       | jq -R 'split(".") | .[0], .[1] | @base64d | fromjson'
    
  7. Use the tokens with Daml Assistant:

    The following will fail, without a JWT:

    daml ledger list-parties --host participant2 --port 4001
    

    The following will succeed, with a JWT:

    daml ledger list-parties --host participant2 --port 4001 \
       --access-token-file audience.token
    
  8. Use the tokens with grpcurl to call the Ledger API:

    The following will fail, for user david:

    TOKEN=$(cat scope.token)
    
    grpcurl --plaintext \
       -H "Authorization: Bearer ${TOKEN}" \
       participant2:4001 \
       com.daml.ledger.api.v1.admin.UserManagementService/ListUsers \
       | jq '.users[].id'
    

    The following will succeed, for user wallace:

    TOKEN=$(cat audience.token)
    
    grpcurl --plaintext \
       -H "Authorization: Bearer ${TOKEN}" \
       participant2:4001 \
       com.daml.ledger.api.v1.admin.UserManagementService/ListUsers \
       | jq '.users[].id'
    
  9. Use the tokens with Daml Script:

    The following will fail, when acting as the operator party:

    daml ledger upload-dar .daml/dist/console-1.0.0.dar \
      --host participant2 --port 4001
      --access-token-file custom.token
    

    The following will succeed, when presenting an admin token:

    daml ledger upload-dar .daml/dist/console-1.0.0.dar \
      --host participant2 --port 4001 \
      --access-token-file admin.token
    

    The following will fail, without an actAs claim:

    daml script --dar .daml/dist/console-1.0.0.dar \
       --script-name Main:createContracts \
       --input-file configs/participant2-operator.json \
       --ledger-host participant2 \
       --ledger-port 4001
       --access-token-file admin.token
    

    The following will succeed, when acting as the operator party:

    daml script --dar .daml/dist/console-1.0.0.dar \
       --script-name Main:createContracts \
       --input-file configs/participant2-operator.json \
       --ledger-host participant2 \
       --ledger-port 4001 \
       --access-token-file custom.token
    

References

5 Likes