Skip to content

Fortified IDDocs

Client authentication

Configuration reference for JWT client authentication, verification keys and replay protection in Access

Access authenticates a client before processing its token request. When Access acts as the OpenID Provider (OP), configure the allowed methods and the client's verification keys on its Relying Party configuration.

token_endpoint_auth_methods is an optional, non-empty list. Supported methods are client_secret_basic, client_secret_post, client_secret_jwt and private_key_jwt.

When the list is omitted, existing Basic/POST authentication and public authorization-code clients using PKCE retain their existing behavior. An explicit list requires one of those authentication methods even when the request includes a PKCE verifier. Authentication never replaces grant validation, redirect URI validation or PKCE validation.

Requests must use one authentication method. Do not send an Authorization header or client_secret together with a JWT assertion. Do not combine Basic authentication with a POST secret. Duplicate authentication parameters are rejected.

The initial JWT profile supports HS256 for client_secret_jwt and RS256 for private_key_jwt. token_endpoint_auth_signing_alg_values defaults to the algorithms required by the selected methods. Other algorithms are rejected.

client_secret_jwt verifies the assertion using the client's shared secret. Use a randomly generated secret of at least 32 UTF-8 bytes. Protect the secret using the existing Access secret configuration.

{
"client_id": "service-client",
"client_secret": "${secrets.service_client_secret}",
"token_endpoint_auth_methods": ["client_secret_jwt"],
"token_endpoint_auth_signing_alg_values": ["HS256"]
}

These keys verify assertions from the client. The OP's own signing keys, configured through sign_ref, continue to sign tokens issued by Access.

Send a form-encoded POST to the advertised token endpoint with the normal grant parameters and:

  • client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
  • client_assertion=<signed JWT>

The JWT must contain:

  • iss and sub: both equal the configured client_id. If client_id is also sent as a form parameter, it must match.
  • aud: contains the exact externally advertised token endpoint URL. Proxy headers do not change the expected audience.
  • exp: expires within five minutes. Access allows 30 seconds of clock skew around expiry.
  • jti: a unique, non-empty identifier of at most 256 characters. Generate a new identifier for every request, including retries.

Optional iat and nbf must not be more than 30 seconds in the future. If iat is supplied, the interval from iat to exp must not exceed five minutes. Use kid for asymmetric signatures; omission is allowed only when exactly one usable verification key exists. Headers that supply keys or URLs (jwk, jku, x5c, x5u) and unsupported critical headers are rejected.

JWT authentication is available for authorization code, refresh token, client credentials and device code requests at the token endpoint. It does not introduce a JWT bearer grant and does not change authentication on introspection or device authorization endpoints.

Access fetches keys on first use, shares concurrent fetches for the same registered URL within an OP, and caches successful results for five minutes. After the first fetch, background refresh starts shortly before cache expiry. Normal requests using a known key read the cache.

An unknown kid can trigger an earlier refresh. Fetches for a URL are limited to one attempt per 30 seconds, including failed attempts. During an outage, already cached keys remain usable only until their existing expiry. Expired keys are never accepted. Fetches have a five-second deadline, a 256 KiB response limit and a maximum of 32 keys.

Publish both the current and next public keys before switching the client's signing key. Allow time for the cache to refresh. Retain the previous key while assertions signed with it can still arrive. For verify_ref, configure both entries and apply the updated configuration before switching the client; remove the old reference after the overlap period.

Only public network destinations are allowed by default. If a client hosts JWKS on a private network, explicitly allow its exact hostname on the OP:

{
"client_assertion": {
"jwks_private_hosts": ["client.internal.example.org"]
}
}

This permission does not disable TLS verification. JVM trust configuration must trust the endpoint's certificate chain.

Access reserves a replay marker after validating the signature and claims and before continuing the grant. The marker is scoped by OP issuer, client ID and jti, and retained until the assertion expiry plus clock skew, subject to the selected store's restart behavior. Storage failures reject authentication.

For one Access node, replay protection uses process-local memory by default. No replay storage configuration or directory is required. You can also select it explicitly with client_assertion.replay_store.type set to memory.

Markers are shared across providers in the same JVM and survive configuration reloads. They are lost when Access restarts, so a previously used assertion can be accepted again after restart if it is still valid. Choose file or jdbc when replay protection must survive restarts.

The memory store holds at most 100,000 active markers across the process. Expired markers are removed on subsequent requests. When full, it rejects new assertions until space becomes available; it never evicts an unexpired marker to accept a new assertion.

Select file explicitly to retain markers across restarts:

{
"client_assertion": {
"replay_store": {
"type": "file"
}
}
}

The file store uses ${system.customer_home}/data/oidc-client-assertions. An optional client_assertion.replay_store.directory overrides it with an absolute path. The directory must be writable and retained across restarts and configuration reloads. Do not delete it while assertions may still be valid. It stores hashed identifiers and expiry times, with a capacity of 100,000 active markers per directory.

For multiple nodes serving the same OP, configure all nodes with the same issuer and shared JDBC destination:

{
"client_assertion": {
"replay_store": {
"type": "jdbc",
"destination": "shared-oidc-database"
}
}
}

The destination refers to an existing platform JDBC configuration. schema optionally selects an existing database schema. Access creates fid_oidc_client_assertion if it does not exist. The database account needs create-table permission for initial setup, then select, insert and delete permissions. A primary key arbitrates concurrent requests across nodes. Expired entries are cleaned up periodically. Clustered Vert.x deployments reject memory and file modes; independently deployed Access nodes must also use shared JDBC storage.

Deploy matching Access, management-access and Management Center builds that support this configuration before enabling JWT authentication. Older Access builds do not enforce the new policy fields. Do not roll back only Access while leaving a JWT policy active; restore a previously approved client configuration and matching component versions together.

The provider's Client authentication settings are used only for RPs that enable client_secret_jwt or private_key_jwt. Configure allowed methods and verification keys on each RP.

  1. Open the relying party and select Client authentication.
  2. Select Choose allowed methods, then the required methods. Existing configurations initially show their existing behavior.
  3. For shared-secret methods, enter the client secret. For private_key_jwt, choose Keystore certificates or Remote JWKS URL. Add overlapping certificate references with their external key IDs when needed.
  4. On the provider's Client authentication tab, keep Memory (one node, default) or select a persistent directory or shared database as required. Configure any explicitly permitted private JWKS hosts on the same tab.
  5. Save and apply the configuration. Reload the relying party to confirm the selected methods and key source. Management Center protects shared secrets and keystore passwords when saving.

Changing to private-key-only authentication removes the unused shared secret. Switching the verification source removes the previous source. Keep an overlap of allowed methods only while migrating the client.

Authentication failures return invalid_client without exposing assertions or credentials. Check client ID, selected method, algorithm, key ID, audience, clock synchronization and replay storage. Each retry needs a new assertion and jti. Malformed forms, duplicate parameters and missing grant parameters return invalid_request.

Discovery advertises the union of the configured RP authentication methods and JWT algorithms. JWT methods are not advertised when no RP enables them. OIDC trace logging omits raw request bodies, credential-bearing headers, token responses and redirect locations.