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.
Choose allowed methods
Section titled “Choose allowed methods”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.
Configure a JWT method
Section titled “Configure a JWT method”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"]}For private_key_jwt, verify_ref is a list of certificate references. Each entry requires a unique external kid, a keystore path and a certificate alias. RSA keys must have at least 2048 bits. Certificates are checked for validity when an assertion is verified.
The alias must select a trusted certificate entry containing the client's public key. Private-key entries are rejected. kid matches the identifier used by the client in its JWT header; it is independent of the alias. Access does not need the client's private key or a key_password.
{ "client_id": "service-client", "token_endpoint_auth_methods": ["private_key_jwt"], "verify_ref": [ { "kid": "client-2026-current", "keystore": { "path": "${system.customer_home}/resources/client-certificates.p12", "password": "${secrets.client_keystore_password}", "alias": "current" } }, { "kid": "client-2026-next", "keystore": { "path": "${system.customer_home}/resources/client-certificates.p12", "password": "${secrets.client_keystore_password}", "alias": "next" } } ]}Configure jwks_uri to retrieve the client's public keys. The URL must use HTTPS without credentials or a fragment. Access verifies the TLS certificate and hostname and does not follow redirects. The endpoint must return public asymmetric keys; private or symmetric material is rejected.
{ "client_id": "service-client", "token_endpoint_auth_methods": ["private_key_jwt"], "jwks_uri": "https://client.example.org/.well-known/jwks.json"}Use exactly one of verify_ref and jwks_uri. There is no fallback between them. A private-key-only client does not need client_secret.
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 an assertion
Section titled “Send an assertion”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-bearerclient_assertion=<signed JWT>
The JWT must contain:
issandsub: both equal the configuredclient_id. Ifclient_idis 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.
Cache and rotate remote keys
Section titled “Cache and rotate remote keys”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.
Configure replay protection
Section titled “Configure replay protection”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.
Memory (default)
Section titled “Memory (default)”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.
Persistent directory (one node)
Section titled “Persistent directory (one node)”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.
Shared database (multiple nodes)
Section titled “Shared database (multiple nodes)”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.
Configure through Management Center
Section titled “Configure through Management Center”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.
- Open the relying party and select Client authentication.
- Select Choose allowed methods, then the required methods. Existing configurations initially show their existing behavior.
- 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. - 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.
- 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.
Troubleshoot authentication
Section titled “Troubleshoot authentication”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.