---
title: "Authentication Service"
description: "Reference for the authentication-service.conf configuration file."
stack: "Community Stack (open source) — version 0.21.0"
stack_version: "0.21.0"
stack_comparison: https://docs.walt.id/community-vs-enterprise.md
canonical_url: https://docs.walt.id/community-stack/issuer2/configurations/config-files/authentication-service
generated: 2026-07-08
---
# 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.

**Note:**

**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](#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](https://docs.walt.id/community-stack/issuer2/credential-offers/create-offer.md#authorization-code-flow)) 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`](https://docs.walt.id/community-stack/issuer2/configurations/config-files/issuer-profiles.md#id-token-claim-mapping) 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.

**Warning:**

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](#example-configurations)) before going live.

## Configuration Options

Every option has a built-in default that points at the [hosted test provider](#default-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&lt;String&gt; | 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.

```hocon
name = "keycloak"
```

### authorizeUrl

OAuth authorization endpoint URL. The user is redirected here to authenticate.

```hocon
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.

```hocon
accessTokenUrl = "https://keycloak.example.com/realms/myrealm/protocol/openid-connect/token"
```

### clientId

OAuth client ID registered with the provider.

```hocon
clientId = "issuer_api"
```

### clientSecret

OAuth client secret.

```hocon
clientSecret = "secret-from-your-provider"
```

**Error:**

Never commit secrets to version control. Use environment variables or a secret manager.

### defaultScopes

OAuth scopes requested during authorization. Common scopes:

- `openid` – required for OIDC
- `profile` – access to name and similar profile claims
- `email` – access to the email address

```hocon
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).

```hocon
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`](https://docs.walt.id/community-stack/issuer2/configurations/config-files/issuer-service.md)):

```
<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

```hocon
# 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)

```hocon
# 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

```hocon
# 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](https://docs.walt.id/community-stack/issuer2/configurations/config-files/issuer-profiles.md#id-token-claim-mapping).

## Related Configuration

- [Issuer Service](https://docs.walt.id/community-stack/issuer2/configurations/config-files/issuer-service.md) – Sets the `baseUrl` the redirect URI is built from.
- [Issuer Profiles](https://docs.walt.id/community-stack/issuer2/configurations/config-files/issuer-profiles.md) – Maps ID token claims to credential fields.
- [Credential Offers](https://docs.walt.id/community-stack/issuer2/credential-offers/create-offer.md) – Create authorization-code-flow offers.
