Authentication Service Configuration
The authentication-service.conf file configures the external OAuth2 / OIDC provider (such as Keycloak, Microsoft Entra ID or Auth0) used for authorization-code-flow credential issuance. During that flow the issuer sends the user to this provider to log in, and then receives the user's claims (e.g. email, name) in return.
To use authorization code flow you configure your own OAuth/OIDC provider here — its endpoints, the client credentials the issuer uses to talk to it, and the scopes to request.
Just want to try it out? You don't need to set anything up. If you don't provide an authentication-service.conf, Issuer2 falls back to a hosted walt.id test identity provider with ready-made login accounts — see Default Test Provider below. Configure your own provider only when you move towards production.
File Location
waltid-services/waltid-issuer-api2/config/authentication-service.conf
When This Is Used
This configuration only matters when issuing credentials via authorization code flow. In that flow the issuer sends the user to the provider configured here to log in. The provider returns the user's claims (e.g. email, name) in an ID token, which can then be mapped into the credential.
For pre-authorized code flow (PIN-based issuance), this configuration is not used.
Default Test Provider
To let you try the authorization code flow immediately — without registering your own Keycloak, Entra, or Auth0 tenant — Issuer2 ships with built-in defaults that point at a hosted walt.id test identity provider (a Keycloak realm at keycloak.demo.walt.id). These defaults are used automatically whenever a value isn't set in authentication-service.conf, so if you never create that file, the authorization code flow works out of the box against this test provider.
You can then create an AUTHORIZED offer (see Create an Offer) and, when the wallet sends you to log in, sign in with one of the accounts below.
Test Users
| Username | Password | Claims returned in the ID token |
|---|---|---|
jane@walt.id | jane | given_name, family_name |
max@walt.id | password | clearanceLevel, employeeId, position, department, startDate |
These claims are what the provider returns after login. To actually see them land in an issued credential, the credential profile needs an idTokenClaimsMapping that maps them into credential fields.
Try it end to end with jane: the ready-made identityCredentialSdJwt profile already maps given_name and family_name from the ID token. Create an AUTHORIZED offer for that profile, open it in a wallet, log in as jane@walt.id / jane, and the issued credential's given_name / family_name will be filled in from Jane's login instead of the profile's placeholder values.
max@walt.id returns a richer set of workplace claims (clearanceLevel, employeeId, position, department, startDate). No default profile maps these, so use max when you want to practise writing your own idTokenClaimsMapping against real ID-token claims.
The default test provider and these shared accounts are for local testing and demos only. They are public and unauthenticated for anyone running Issuer2, so never rely on them — or the built-in client secret — in production. Configure your own provider (see Example Configurations) before going live.
Configuration Options
Every option has a built-in default that points at the hosted test provider. You only need to set these when pointing Issuer2 at your own provider — at which point authorizeUrl, accessTokenUrl, clientId, and clientSecret are effectively required (the test-provider defaults won't match your setup).
| Property | Type | Required for own provider | Default | Description |
|---|---|---|---|---|
authorizeUrl | String | Yes | Test provider | OAuth authorization endpoint. The user is sent here to authenticate. |
accessTokenUrl | String | Yes | Test provider | Token endpoint where the issuer exchanges the authorization code for an access token and ID token. |
clientId | String | Yes | Test provider | OAuth client ID registered with the provider. |
clientSecret | String | Yes | Test provider | OAuth client secret. |
name | String | No | "keycloak" | Provider name. Informational — passed through to the OAuth client settings. |
defaultScopes | Array<String> | No | ["openid", "profile"] | OAuth scopes requested during authorization. |
forwardIssuerStateToAuthorizationServer | Boolean | No | false | Whether to forward the issuer's internal issuer_state to the authorization server. |
name
Provider name. Informational only — it is passed through as the OAuth client's name and does not change behaviour.
name = "keycloak"
authorizeUrl
OAuth authorization endpoint URL. The user is redirected here to authenticate.
authorizeUrl = "https://keycloak.example.com/realms/myrealm/protocol/openid-connect/auth"
accessTokenUrl
Token endpoint URL where the issuer exchanges the authorization code for an access token and ID token.
accessTokenUrl = "https://keycloak.example.com/realms/myrealm/protocol/openid-connect/token"
clientId
OAuth client ID registered with the provider.
clientId = "issuer_api"
clientSecret
OAuth client secret.
clientSecret = "secret-from-your-provider"
Never commit secrets to version control. Use environment variables or a secret manager.
defaultScopes
OAuth scopes requested during authorization. Common scopes:
openid– required for OIDCprofile– access to name and similar profile claimsemail– access to the email address
defaultScopes = ["openid", "profile"]
forwardIssuerStateToAuthorizationServer
Controls whether the issuer's internal issuer_state value is forwarded to the authorization server as an extra issuer_state query parameter on the authorize request. Defaults to false.
This is only needed for custom integrations where the external authorization server has to receive the issuer state. When true, the issuer extracts issuer_state from the internal login request and appends it to the outgoing authorize URL (it is skipped if the value is missing, blank, or already present).
forwardIssuerStateToAuthorizationServer = false
Leave this false unless your provider specifically requires the forwarded state.
Redirect URI
The issuer's OAuth callback is derived from baseUrl (set in issuer-service.conf):
<baseUrl>/openid4vci/external/oauth/callback
Register this exact URL as an allowed redirect URI in your OAuth provider, otherwise the provider rejects the login. For example, with baseUrl = "https://issuer.example.com" the redirect URI is https://issuer.example.com/openid4vci/external/oauth/callback.
Example Configurations
Keycloak
# authentication-service.conf
name = "keycloak"
authorizeUrl = "https://keycloak.example.com/realms/myrealm/protocol/openid-connect/auth"
accessTokenUrl = "https://keycloak.example.com/realms/myrealm/protocol/openid-connect/token"
clientId = "issuer_api"
clientSecret = "secret-from-keycloak"
defaultScopes = ["openid", "profile", "email"]
forwardIssuerStateToAuthorizationServer = false
Microsoft Entra ID (Azure AD)
# authentication-service.conf
name = "entra"
authorizeUrl = "https://login.microsoftonline.com/TENANT_ID/oauth2/v2.0/authorize"
accessTokenUrl = "https://login.microsoftonline.com/TENANT_ID/oauth2/v2.0/token"
clientId = "client-id-from-azure"
clientSecret = "client-secret-from-azure"
defaultScopes = ["openid", "profile", "email"]
forwardIssuerStateToAuthorizationServer = false
Auth0
# authentication-service.conf
name = "auth0"
authorizeUrl = "https://YOUR_DOMAIN.auth0.com/authorize"
accessTokenUrl = "https://YOUR_DOMAIN.auth0.com/oauth/token"
clientId = "client-id-from-auth0"
clientSecret = "client-secret-from-auth0"
defaultScopes = ["openid", "profile", "email"]
forwardIssuerStateToAuthorizationServer = false
Mapping ID Token Claims to Credentials
After successful authorization, the issuer receives an ID token with user claims. Use idTokenClaimsMapping in your credential profile to map those claims into credential fields.
See Issuer Profiles – ID Token Claim Mapping.
Related Configuration
- Issuer Service – Sets the
baseUrlthe redirect URI is built from. - Issuer Profiles – Maps ID token claims to credential fields.
- Credential Offers – Create authorization-code-flow offers.
