Canton | Keycloak Config
Keycloak Configuration Guide for Canton Validator
Section titled “Keycloak Configuration Guide for Canton Validator”Prerequisites
Section titled “Prerequisites”1. Run Keycloak2. Validator API base URL: https://validator-api.your-domain.example/api or use http://wallet.localhost/api3. Wallet UI: https://wallet.your-domain.example or use http://wallet.localhost4. CNS UI: https://cns.your-domain.example or use http://ans.localhost5. Your PARTY_HINTRealm Configuration
Section titled “Realm Configuration”1. Create Realm
Section titled “1. Create Realm”- Navigate to Admin Console → Manage realms → Create realm.- Set Realm name to canton (or your environment-specific name).- Click Create to initialize the realm.2. Config Session
Section titled “2. Config Session”- Realm Settings → Sessions.- Set Offline Session Max Limited to ON- Set Offline Session Max to 5,184,000 seconds (60 days; adjust as desired)- Set Offline Session Idle Timeout to 2,592,000 seconds (30 days; adjust as desired)Client Scopes Setup
Section titled “Client Scopes Setup”1. Create daml_ledger_api
Section titled “1. Create daml_ledger_api”- Client Scopes → Create- Name: daml_ledger_api- Protocol: OpenID Connect- Type: Default- Display on consent screen: OFF- Include in token scope: ONUser Client Role mapper:- Navigate to daml_ledger_api → Mappers → Create- Mapper Type: User Client Role- Name: daml_ledger_api_scope- Add to access token: ON- Add to ID token, Add to userinfo, Add to token introspection, and Multivalued: OFFAudience mapper:- Navigate to daml_ledger_api → Mappers → Create- Mapper Type: Audience- Name: audience- Included Custom Audience: https://canton.network.global- Add to access token and Add to token introspection: ON- Add to ID token and Add to lightweight access token: OFF2. Create open id
Section titled “2. Create open id”- Navigate to Client Scopes → Create- Name: openid- Protocol: OpenID Connect- Type: Default- Display on consent screen: OFF- Include in token scope: ONValidator API Audience mapper:- Navigate to openid → Mappers → Create- Mapper Type: Audience- Name: aud- Included Custom Audience: https://validator-api.your-domain.example/api or http://wallet.localhost/api- Add to access token and Add to token introspection: ON- Add to ID token and Add to lightweight access token: OFFValidator API User Property:- Navigate to openid → Mappers → Create- Mapper Type: User Property- Name: sub- Property: username- Token Claim Name: sub- Claim JSON Type: String- Add to lightweight access token: OFF- Add to ID token, Add to access token and Add to userinfo and Add to userinfo: ONClient Configuration
Section titled “Client Configuration”1. Create the ledger API resource client
Section titled “1. Create the ledger API resource client”- Navigate to Clients → Create client.- Client Type: OpenID Connect- Client ID: ledger-api- Click Next- In Capability config, set all toggles to OFF.- Click Next.- Leave all Login settings fields empty2. Create the validator app backend service account client
Section titled “2. Create the validator app backend service account client”- Navigate to Clients → Create client- Client Type: OpenID Connect- Client ID: validator-app-backend- Click Next
In Capability config:- Client authentication: ON- Service accounts roles: ON- Direct access grants: ON- all other options: OFF- Click Next- Leave Login settings fields empty and click Save.
Record the credentials:- From the Credentials tab, copy the Client Secret- From the Service account roles tab, click the service-account-validator-app-backend- Copy the user ID (UUID) from the browser URL (use as LEDGER_API_ADMIN_USER)
Fix sub claim:- Navigate to client, click validator-app-backend, From the Client scopes tab, click the validator-app-backend-dedicated- Add Mapper, by configuration, choice: Hardcoded claim- Mapper type: Hardcoded claim- Name: fix-sub-claim- Token Claim Name: sub- Claim value: UUID_LEDGER_API_ADMIN_USER (uuid service-account-validator-app-backend)- Claim JSON Type: String- Add to lightweight access token and Add to access token response: OFF- Add to ID token, Add to access token, Add to userinfo and Add to userinfo and Add to token introspection: ON3. Create the wallet web UI public client
Section titled “3. Create the wallet web UI public client”- Navigate to Clients → Create client- Client Type: OpenID Connect- Client ID: wallet-web-ui- Click Next
In Capability config:- Standard flow & Direct access grants: ON- Set all other options: OFF- Click Next
Configure Login settings:- Valid redirect URIs: https://wallet.your-domain.example/* or use http://wallet.localhost/*- Valid post logout redirect URIs: https://wallet.your-domain.example or use http://wallet.localhost- Web origins: https://wallet.your-domain.example or use http://wallet.localhost4. Create the CNS UI public client
Section titled “4. Create the CNS UI public client”- Navigate to Clients → Create client- Client Type: OpenID Connect- Client ID: cns-ui- Click Next
In Capability config:- Standard flow: ON- Set all other options: OFF- Click Next
Configure Login settings:- Valid redirect URIs: https://cns.your-domain.example/* or use http://ans.localhost/*- Valid post logout redirect URIs: https://cns.your-domain.example or use http://ans.localhost- Web origins: https://cns.your-domain.example or use http://ans.localhostUser Management
Section titled “User Management”1. Create the operator wallet user
Section titled “1. Create the operator wallet user”- Navigate to Users → Add userConfigure the user:
- Username: YOUR_PARTY_HINT- Set User Enabled: ON- Click Save.- Set a permanent password: Navigate to Credentials tab → Set password with Temporary set to OFF.- Copy the user ID (UUID) from the browser URL (use as WALLET_ADMIN_USER)Application Configuration / .env Config
Section titled “Application Configuration / .env Config”1. Configure your validator application using the following Keycloak-related environment variables
Section titled “1. Configure your validator application using the following Keycloak-related environment variables”# Participant & validator adminPARTICIPANT_IDENTIFIER=your_username_operator_walletPARTY_HINT=your_party_hint
# ValidatorCONTACT_POINT=email@yourdomain.xyz
# AuthenticationAUTH_URL="http://keycloak:8080/realms/canton"AUTH_JWKS_URL="http://keycloak:8080/realms/canton/protocol/openid-connect/certs"AUTH_WELLKNOWN_URL="http://keycloak:8080/realms/canton/.well-known/openid-configuration"
LEDGER_API_AUTH_AUDIENCE="https://canton.network.global"LEDGER_API_AUTH_SCOPE="daml_ledger_api"
VALIDATOR_AUTH_AUDIENCE="http://wallet.localhost/api"VALIDATOR_AUTH_CLIENT_ID="validator-app-backend"VALIDATOR_AUTH_CLIENT_SECRET="your_client_secret"
LEDGER_API_ADMIN_USER="UUID_LEDGER_API_ADMIN_USER (uuid service-account-validator-app-backend)"
WALLET_ADMIN_USER="your_username_operator_wallet"WALLET_UI_CLIENT_ID="wallet-web-ui"ANS_UI_CLIENT_ID="cns-ui"2. Config compose-disable-auth.yaml
Section titled “2. Config compose-disable-auth.yaml”services: participant: environment: - CANTON_PARTICIPANT_ADMIN_USER_NAME=UUID_LEDGER_API_ADMIN_USER (uuid service-account-validator-app-backend) - ADDITIONAL_CONFIG_DISABLE_NEW_TOPOLOGY_CLIENT=canton.participants.participant.topology.use-new-client = false
validator: environment: - SPLICE_APP_VALIDATOR_WALLET_USER_NAME=your_username_operator_wallet - SPLICE_APP_VALIDATOR_LEDGER_API_AUTH_USER_NAME=UUID_LEDGER_API_ADMIN_USER (uuid service-account-validator-app-backend)
wallet-web-ui: environment: - SPLICE_APP_UI_AUTH_URL=http://keycloak:8080/realms/canton
ans-web-ui: environment: - SPLICE_APP_UI_AUTH_URL=http://keycloak:8080/realms/cantonValidation and Testing
Section titled “Validation and Testing”YOUR_SECRET="CLIENT_SECRET"KEYCLOAK_URL="http://keycloak:8080"
echo "=============================="echo "Test Token M2M"echo "=============================="TOKEN=$(curl -s -X POST \ $KEYCLOAK_URL/realms/canton/protocol/openid-connect/token \ -d "client_id=validator-app-backend" \ -d "client_secret=$YOUR_SECRET" \ -d "grant_type=client_credentials" \ -d "scope=daml_ledger_api" \ | python3 -c "import sys,json,base64r=json.load(sys.stdin)t=r['access_token'].split('.')[1]t+='=='*(-len(t)%4)d=json.loads(base64.b64decode(t))print(d['access_token'] if 'access_token' in d else r['access_token'])" 2>/dev/null || curl -s -X POST \ $KEYCLOAK_URL/realms/canton/protocol/openid-connect/token \ -d "client_id=validator-app-backend" \ -d "client_secret=$YOUR_SECRET" \ -d "grant_type=client_credentials" \ -d "scope=daml_ledger_api" \ | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")
# Decode tokencurl -s -X POST \ $KEYCLOAK_URL/realms/canton/protocol/openid-connect/token \ -d "client_id=validator-app-backend" \ -d "client_secret=$YOUR_SECRET" \ -d "grant_type=client_credentials" \ -d "scope=daml_ledger_api" \ | python3 -c "import sys,json,base64r=json.load(sys.stdin)t=r['access_token'].split('.')[1]t+='=='*(-len(t)%4)d=json.loads(base64.b64decode(t))print('sub :', d.get('sub','?'))print('iss :', d.get('iss','?'))print('roles:', d.get('realm_access',{}).get('roles',[]))"#Expect result:sub : your uuid service-account-validator-app-backendiss : http://keycloak:8080/realms/cantonroles: ['default-roles-canton', 'offline_access', 'uma_authorization']
#If result ok, restart your validator and check validator logsTesting check balance
Section titled “Testing check balance”your_username=party-hintyour_password=passwordREFRESH_TOKEN=$(curl -s -X POST \ http://keycloak:8080/realms/canton/protocol/openid-connect/token \ -d "client_id=wallet-web-ui" \ -d "grant_type=password" \ -d "username=$your_username" \ -d "password=$your_password" \ | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['refresh_token'])")
TOKEN=$(curl -s -X POST \ http://keycloak:8080/realms/canton/protocol/openid-connect/token \ -d "client_id=wallet-web-ui" \ -d "grant_type=refresh_token" \ -d "refresh_token=$REFRESH_TOKEN" \ | grep -o '"access_token":"[^"]*"' | cut -d'"' -f4)
curl -s http://wallet.localhost/api/validator/v0/wallet/balance -H "Authorization: Bearer $TOKEN"lastUpdated: 2026-03-17