Skip to content

Canton | Keycloak Config

Keycloak Configuration Guide for Canton Validator

Section titled “Keycloak Configuration Guide for Canton Validator”
1. Run Keycloak
2. Validator API base URL: https://validator-api.your-domain.example/api or use http://wallet.localhost/api
3. Wallet UI: https://wallet.your-domain.example or use http://wallet.localhost
4. CNS UI: https://cns.your-domain.example or use http://ans.localhost
5. Your PARTY_HINT
- Navigate to Admin Console → Manage realms → Create realm.
- Set Realm name to canton (or your environment-specific name).
- Click Create to initialize the realm.
- 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 → Create
- Name: daml_ledger_api
- Protocol: OpenID Connect
- Type: Default
- Display on consent screen: OFF
- Include in token scope: ON
User 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: OFF
Audience 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: OFF
- Navigate to Client Scopes → Create
- Name: openid
- Protocol: OpenID Connect
- Type: Default
- Display on consent screen: OFF
- Include in token scope: ON
Validator 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: OFF
Validator 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: ON
- 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 empty
2. 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: ON
- 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.localhost
- 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.localhost
- Navigate to Users → Add user
Configure 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)
Section titled “1. Configure your validator application using the following Keycloak-related environment variables”
# Participant & validator admin
PARTICIPANT_IDENTIFIER=your_username_operator_wallet
PARTY_HINT=your_party_hint
# Validator
CONTACT_POINT=email@yourdomain.xyz
# Authentication
AUTH_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"
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/canton
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,base64
r=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 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,base64
r=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-backend
iss : http://keycloak:8080/realms/canton
roles: ['default-roles-canton', 'offline_access', 'uma_authorization']
#If result ok, restart your validator and check validator logs
your_username=party-hint
your_password=password
REFRESH_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