
This page describes the OpenID Provider: where it publishes its discovery document and its keys, how the keys are configured and rolled over, how the key that a message to a client is signed with is chosen, how the offered scopes, the supported claims and the authentication contexts are worked out from the authentication providers, how authentication requests are processed, how the code flow completes with the authorization code, the token endpoint, the ID token and the UserInfo endpoint, how to extend the discovery document, and how the OpenID Provider joins an OpenID Federation. The properties are described in Configuration.
Source links in this guide point to the main branch of the
spring-authentication-server repository.
The OpenID Provider is identified by its issuer, which defaults to the base URL. Its endpoints are placed under the
OIDC path, /oidc by default, see URL layout. With the base URL
https://op.example.com:
| What | URL |
|---|---|
| Issuer | https://op.example.com |
| Discovery document | https://op.example.com/.well-known/openid-configuration |
| JWKS | https://op.example.com/oidc/jwks |
| Authorization endpoint | https://op.example.com/oidc/authorize |
| Token endpoint | https://op.example.com/oidc/token |
| UserInfo endpoint | https://op.example.com/oidc/userinfo |
| OpenID Federation entity configuration, when federation is enabled | https://op.example.com/.well-known/openid-federation |
The discovery document is always published at the issuer followed by /.well-known/openid-configuration, as OpenID
Connect Discovery, Section 4, requires, and the entity configuration at the issuer followed by
/.well-known/openid-federation, as OpenID Federation 1.0, Section 9, requires. They are therefore not under the OIDC
path. An issuer with a path moves both documents along with it: with the issuer https://op.example.com/op1, the
discovery document is published at https://op.example.com/op1/.well-known/openid-configuration. The issuer must be
the base URL or begin with it, since the server can only serve paths under the base URL.
The OpenID Provider has two lists of keys: signing keys and decryption keys. Each key is a credential configured through the credentials-support library, together with a state. The keys are checked at startup, and the application does not start if a rule below is broken.
The OpenID Provider signs ID tokens, and UserInfo responses unless that is turned off. A signing key is:
Several signing keys may be active at the same time, for example one RSA key and one EC key, so that clients that only accept EC signatures are served too. Exactly one active key is the default key, which is used for a client that does not say which algorithm it wants. With only one active key, that key is the default key without being marked.
At least one active signing key is required, since ID tokens are always signed (OpenID Connect Core, Section 2).
The algorithms that a key can produce follow from the key:
| Key | Algorithms |
|---|---|
| RSA | RS256, RS384, RS512, PS256, PS384, PS512. RS256 is used when the client does not ask for one. |
| EC, P-256 | ES256 |
| EC, P-384 | ES384 |
| EC, P-521 | ES512 |
A key can be limited to one algorithm by assigning jose-alg in the credential metadata.
Clients may encrypt request objects to the OpenID Provider. A decryption key is:
An RSA key supports RSA-OAEP-256 and RSA-OAEP, and an EC key supports ECDH-ES, ECDH-ES+A128KW,
ECDH-ES+A192KW and ECDH-ES+A256KW. The algorithms of the active keys are published in the discovery document as
request_object_encryption_alg_values_supported. Decryption keys are optional.
Following Sweden Connect Security Requirements, Section 3, an RSA key must be at least 2048 bits, and an EC key must be on the P-256, P-384 or P-521 curve. Other keys stop the application from starting.
Every published key has a key ID, kid, as Section 4 of the same document requires. The key ID is the key-id of the
credential metadata when assigned, and otherwise the RFC 7638 thumbprint of the public key. Either way it stays the
same across restarts, so clients that cache the JWKS keep finding the key. The key IDs of the signing keys must be
unique, and so must those of the decryption keys.
The JWKS publishes the public keys only, with use set to sig or enc, and with the certificate chain when the
credential has one.
Section 4 of the security requirements says how a key is replaced without disrupting the clients.
A signing key must be published before it is used, at least as long before as the clients may cache the JWKS:
future. It is published but not used.active and the default key, and remove the old key.authn-server:
oidc:
keys:
signing:
- credential:
bundle: sign-2026
default-key: true
- credential:
bundle: sign-2027
state: future
A decryption key is the other way around: the old key is removed from the JWKS at once, so that clients stop encrypting with it, but it must still decrypt what was encrypted before the switch:
active, and change the old key to previous.authn-server:
oidc:
keys:
decryption:
- credential:
bundle: enc-2027
- credential:
bundle: enc-2026
state: previous
The key is chosen per client that a message is sent to, by SigningKeySelector. The ID token
uses the client’s ID token parameters, and the UserInfo response its UserInfo parameters.
| The client declares | The key and algorithm |
|---|---|
One algorithm, id_token_signed_response_alg or userinfo_signed_response_alg |
An active key that can produce the algorithm, the default key if it can. |
A list of accepted algorithms, id_token_signing_alg_values_supported or userinfo_signing_alg_values_supported of OpenID Connect Relying Party Metadata Choices |
The default key if it can produce one of them, otherwise another active key that can. The algorithm is the first one in the client’s list that the key can produce. |
| Nothing | The default key, with its preferred algorithm. |
A client that declares both gets its single algorithm. The value none in a list is ignored, since the message is
signed.
A client that declares nothing does not get the registration default of OpenID Connect (RS256 for ID tokens).
Sweden Connect OpenID Connect Metadata Requirements,
Section 4.1, leaves the choice to the OpenID Provider, so the default key decides.
If no active key can produce an algorithm that the client accepts, the message is not sent: this is an error, not a fallback to another algorithm. Future keys are never used.
UserInfo responses are signed by default, see The UserInfo endpoint.
What the OpenID Provider offers is worked out from what the authentication providers declare, see
What the provider declares. The result is computed at startup
by SupportedScopesAndClaims.
Claims. The claims of a provider are its attributes, getSupportedAttributes(), mapped to claims through the
OpenID Connect attribute mapping. For example, attribute.personal-identity-number gives
https://id.oidc.se/claim/personalIdentityNumber, and attribute.mobile-number gives both phone_number and
msisdn.
Scopes. A provider that declares scopes, getSupportedScopes(), offers exactly those. A provider that declares
none offers the scopes derived from its claims. A scope of the scope registry
is derived when all its essential claims are among the claims of the provider. In this check, the claims sub and
auth_time always count, since the OpenID Provider puts them in every ID token. A scope without essential claims, such
as profile, is derived when at least one of its claims is among them, and a scope without claims, such as
https://id.oidc.se/scope/signApproval, is only offered when it is declared.
:raised_hand: The scope https://id.oidc.se/scope/naturalPersonNumber has two essential claims, the personal identity
number and the coordination number. A provider that delivers only one of them does not get the scope derived. Declare
the scope, or configure the missing claim, if the provider should offer it.
The OpenID Provider offers the union of the scopes over all providers. The openid scope is always offered.
The settings of the OpenID Provider change this:
authn-server.oidc.scopes, when set, replaces the derived scopes. Every scope must be in the scope registry.authn-server.oidc.claims is added to the claims of the providers. The claims also count when scopes are derived,
and they are published in claims_supported.The supported claims are sub, the claims of the providers and the configured claims. The claims of the offered
scopes are not added, so claims_supported lists only the claims that the OpenID Provider can deliver. An OpenID
Provider that offers profile but only delivers names and date of birth publishes those claims, and not nickname,
picture or zoneinfo. A claim that should be published even though no provider delivers it is added with
authn-server.oidc.claims.
Authentication contexts. acr_values_supported is the union of the authentication context URIs of the providers,
getSupportedAuthnContextUris(). It is not configurable.
Authentication requests are received on the authorization endpoint, with GET or POST. The OpenID Provider follows
OpenID Connect Core, Section 3.1.2, and the
Swedish OpenID Connect Profile, Section 2. Only the
authorization code flow is supported, so response_type must be code.
This section covers the processing of the request, up to the protocol-neutral authentication requirements. What happens next is described in The code flow.
The request is processed in two parts, by
OidcAuthnRequestAuthenticationConverter and
OidcAuthnRequestAuthenticationProvider.
The first part finds out where a response can be sent:
client_id in the client registry. A client registered with
the token endpoint authentication method none is not accepted, see Clients.redirect_uri must be present and be one of the client’s registered redirect URIs, compared as strings.response_mode must be query, form_post or absent.From this point, a failure is sent to the redirect URI as an error response, in the requested response mode and with
the state of the request. The second part checks the request and builds the requirements:
response_type must be code.state must be present, unless that check is turned off.The clients are found in the client registry, through its OpenID Connect backends and in their configured order, see OpenID Connect: three backends.
Clients that the deployment configures itself are given with authn-server.oidc.clients, in JSON files or inline, see
Clients, and clients resolved through OpenID Federation with
authn-server.oidc.federation.clients, see Clients from OpenID Federation.
An OpenID Provider may use either, or both, in which case the configured clients are asked first. An OpenID Provider
without any client source does not start. A client repository, or a client source of the application’s own, is added
in an adapter:
@Bean
AuthnServerConfigurerAdapter oidcClients(final ClientRepository repository) {
return (http, configurer) -> configurer.clientRegistryBackend(new RepositoryClientBackend(repository));
}
What the OpenID Provider reads from the client metadata when it processes a request:
| Client metadata | Used for |
|---|---|
redirect_uris |
The accepted values of redirect_uri. |
request_uris |
The only values of request_uri that are fetched. |
jwks or jwks_uri |
Verifying signed request objects and signature requests. |
request_object_signing_alg |
When set, request objects must be signed with this algorithm. |
token_endpoint_auth_method |
The method the client must authenticate with at the token endpoint, see Client authentication. Public clients, with none, are not supported. |
token_endpoint_auth_signing_alg |
When set, the algorithm that client assertions must be signed with. |
client_secret |
The client secret, for the secret-based client authentication methods. |
default_acr_values |
The authentication contexts, as voluntary values, when the request asks for none. |
subject_type and sector_identifier_uri |
How the sub is computed, see Identifying the user. |
id_token_signed_response_alg |
The algorithm the ID token is signed with, see Choosing the signing key for a client. |
id_token_encrypted_response_alg and id_token_encrypted_response_enc |
Whether and how the ID token is encrypted, see The ID token. |
An unknown client and a client registry that fails end at the OpenID Provider, and are logged differently: the first
at INFO, since it is a normal outcome, and the second at ERROR, since a dependency is not working.
A client registered with the token endpoint authentication method none, a public client, is a client configuration
error. The request ends at the OpenID Provider, without a redirect, and the error is logged at WARN, since the
client registration needs to be corrected.
A request object may be passed by value, in request, or by reference, in request_uri. Its parameters replace
those of the request with the same name, as OpenID Connect Core, Section 6.3.3, states. client_id must still be
given as a plain parameter.
request_uri is only fetched when it is one of the client’s registered request_uris, so a
client that has registered none cannot use request_uri. A fragment is ignored in the comparison and is not sent.
The fetched request object is processed exactly like one passed by value. The default fetcher uses HTTP GET with a
timeout of 5 seconds and accepts at most 100 KB. Replace it with
OidcProviderConfigurer.authnRequestProcessor(p -> p.requestUriFetcher(...)).jwks or jwks_uri in its metadata. The
accepted algorithms are RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384 and ES512. A signed
request object must hold iss, which must be the client_id, and aud, which must be the issuer or the URL of the
authorization endpoint.alg none, is accepted by default. It is rejected when
authn-server.oidc.authorization-request.require-signed-request-object is true, and when the client has registered
request_object_signing_alg. A client that has registered an algorithm must sign with that algorithm.For every request object, client_id and iss, when present, must be the client_id of the request, and
response_type, when also given as a plain parameter, must match it, as OpenID Connect Core, Section 6.1, requires. A
request object whose exp has passed, or whose nbf has not been reached, is rejected.
The iat of a signed request object is optional. When present, it must not be in the future, and when
authn-server.oidc.max-jwt-age is set, the request object must not be older than that. There is no maximum age by
default, since a client may sign a request object once and reuse it through request_uri. The iat of an unsigned
request object is not checked. The clock skew of the OpenID Provider applies to all of these checks.
If a request object cannot be fetched or decoded, there may be no redirect URI to answer to, since the request object may be what holds it. The error is sent to the client when the plain parameters of the request hold a registered redirect URI and a supported response mode, and otherwise ends at the OpenID Provider.
PKCE (RFC 7636) is optional by default. With authn-server.oidc.authorization-request.require-pkce set to
true it is required. The only supported method is S256. A request with the method plain, or with a
code_challenge and no method, which means plain (RFC 7636, Section 4.3), is always answered with
invalid_request.
state is required by default, as the Swedish OpenID Connect Profile, Section 2.1, requires. A request without it
is answered with invalid_request. With authn-server.oidc.authorization-request.require-state set to false, such a
request is accepted, and the response carries no state.
Response modes. query, the default for the code flow, and form_post are supported. With form_post, the
response is posted to the client by a page that the browser submits by itself, like the SAML response page. Replace
the page with OidcProviderConfigurer.authnRequestProcessor(p -> p.responsePage(...)). Any other response_mode,
such as fragment, is answered with HTTP status 400 and no redirect, as OpenID Connect Core, Section 3.1.2.6, says.
Once the redirect URI is established and the request has been parsed, the
requester acceptance decides whether the client may use the OpenID
Provider. By default every known client is accepted. The rules for OpenID Connect are set under
authn-server.oidc.requester-acceptance: a whitelist of client_ids, and groups of required trust mark types, see
Configuration. A trust mark type that the client does not hold is asked
for through the client registry before the client is rejected, see
Trust marks on demand.
A client that is not accepted gets the error unauthorized_client.
The result is the protocol-neutral authentication requirements, as an
OidcAuthenticationRequirements, which adds what only OpenID Connect has.
| Request | Requirement |
|---|---|
prompt=login |
Force authentication. |
prompt=none |
Passive authentication. none together with another value is invalid_request. |
prompt=consent |
Consent required. |
No prompt |
Force authentication, as the OpenID Connect Profile for Sweden Connect, Section 2.2.1, requires. Single sign-on is then only used when the request holds prompt without login. With authn-server.oidc.authorization-request.login-without-prompt set to false, a request without prompt may be answered with single sign-on. |
max_age |
The maximum authentication age. max_age=0 is the same as prompt=login. |
scope |
The requested scopes that the OpenID Provider offers. Other scopes are ignored, as OpenID Connect Core says for scopes that are not understood. |
scope and claims |
The requested attributes. The offered scopes are expanded into their claims, the claims parameter is merged in, and the claims are mapped to generic attributes, see What a request asks for. |
acr_values, acr in claims, or the client’s default_acr_values |
The authentication contexts, in the client’s order of preference, and whether they are voluntary or required. |
login_hint |
The login hint. |
ui_locales |
The preferred languages of the user interface. |
id_token_hint |
The subject of the ID token. |
Authentication contexts. The requirements tell voluntary authentication contexts from required ones, see Which authentication contexts are acceptable.
acr_values requests the acr claim as a voluntary claim (OpenID Connect Core, Sections 3.1.2.1 and 5.5.1.1).
The supported values are used in the client’s order of preference, and if none is supported, the user is
authenticated as if no value had been requested. Such a request never fails because of its acr_values.acr in the claims parameter wins over acr_values, and the Swedish profile says a client should not send both.
Values that are not essential are voluntary, as above.acr requested as essential are required. The values that no authentication provider supports are left
out, and if none is left, the request is answered with unmet_authentication_requirements, as the Swedish profile,
Section 2.2, requires.acr_values nor acr in the claims parameter, the client’s registered
default_acr_values are used, as voluntary values. A request that has either ignores the defaults.id_token_hint must be an ID token issued by this OpenID Provider: signed by one of its signing keys, with the
issuer as iss, and with the client in aud. An expired token is accepted. Otherwise the request is answered with
invalid_request.
The login hint and the subject of the ID token hint may be personal data, and are never logged.
The data needed to answer the request and issue tokens, such as the redirect URI, state, nonce and the PKCE code
challenge, is kept as the protocol request data of the request, an
OidcAuthnRequestData.
The parameters of Authentication Request Parameter Extensions for the Swedish OpenID Connect Profile and the Signature Extension for OpenID Connect are read, as plain parameters or from a request object.
https://id.oidc.se/param/userMessage. Read only when user messages are supported for OpenID
Connect, authn-server.oidc.supports-user-message, and otherwise ignored. The MIME types text/plain and
text/markdown are supported. An invalid message, or another MIME type, is answered with invalid_request.https://id.oidc.se/param/authnProvider. Becomes the requested authentication
provider.https://id.oidc.se/param/signRequest. Read when the scope https://id.oidc.se/scope/sign
or https://id.oidc.se/scope/signApproval is requested and offered. Without one of these scopes, a signature request
is answered with invalid_request, as Section 5.1 of the extension requires. With one of them, it must be present,
either as a JWT of its own signed with the client’s key, which may also be encrypted, or as a JSON object in a signed
request object. A JWT of its own must hold iss, which must be the client_id, and aud, which must be the issuer
or the URL of the authorization endpoint, the same rules as for a signed request object (Section 3.1.2 of the
extension). Its exp, nbf and iat are checked as for a signed request object, including
the maximum age. prompt must hold both login and consent. For the sign scope, tbs_data must be present, and
for sign approval only, it must not be. The sign message becomes a sign message that must be shown, with the data
to be signed. A signature request that breaks a rule is answered with invalid_request.Until the client and the redirect URI are known, there is nowhere safe to send a response, and OAuth 2.0 (RFC 6749),
Section 4.1.2.1, forbids redirecting the user to a redirect URI that is missing, invalid or not registered, or for a
client that is missing or invalid. Failures up to that point end at the OpenID Provider as an
UnrecoverableErrorException, with one of the errors of OidcUnrecoverableError. The user is
shown the server’s error page, never a redirect, with the HTTP status of the error: the response is sent with
sendError, with the exception as the jakarta.servlet.error.exception request attribute, so the error page is chosen
the same way as for the unrecoverable errors of SAML. Each is audited as an authn_unrecoverable_error event, with the
client_id as the request names it. After that point, a failure is sent to the redirect URI as an error response, with
an error_description meant for the client’s logs.
| Failure | Outcome |
|---|---|
client_id is missing |
Error page, 400 (INVALID_AUTHN_REQUEST) |
| The client is not known | Error page, 400 (UNKNOWN_CLIENT) |
The client is registered with the token endpoint authentication method none |
Error page, 500 (INVALID_CLIENT_CONFIGURATION) |
| A client source fails when the client is looked up, for example a federation resolver that cannot be reached, so the OpenID Provider cannot tell whether the client exists | Error page, 503 (CLIENT_LOOKUP_FAILED) |
redirect_uri is missing or not registered |
Error page, 400 (INVALID_REDIRECT_URI) |
response_mode is not query or form_post |
Error page, 400 (UNSUPPORTED_RESPONSE_MODE) |
| The request object cannot be fetched or decoded, and the plain parameters give no redirect URI | Error page, 400 (INVALID_AUTHN_REQUEST) |
| The client asks for an encrypted ID token with algorithms that are not allowed, or has no key for them | Error page, 500 (INVALID_CLIENT_CONFIGURATION) |
request_uri is not registered, or cannot be fetched |
invalid_request_uri |
| The request object is invalid, not signed when it must be, or signed with the wrong algorithm | invalid_request_object |
A signed request object has an iat in the future, or is older than authn-server.oidc.max-jwt-age |
invalid_request_object |
response_type is not code |
unsupported_response_type |
A parameter is missing or invalid, such as scope without openid, state, prompt, max_age or id_token_hint |
invalid_request |
PKCE is missing when required, or uses plain, also by leaving out code_challenge_method |
invalid_request |
| The client is not accepted | unauthorized_client |
| The client registry fails during the acceptance check | Error page, 503 (CLIENT_LOOKUP_FAILED) |
None of the essential acr values is supported |
unmet_authentication_requirements |
| The user message or the signature request is invalid | invalid_request |
A signature request JWT lacks iss or aud, has the wrong iss or aud, has an iat in the future, or is older than authn-server.oidc.max-jwt-age |
invalid_request |
The errors that the authentication step reports are mapped as described in Errors.
Once the authentication request has been processed, the code flow continues:
state.The processed request goes to the authentication providers in the same way as a SAML request: the provider is chosen from the requested authentication contexts, a previous authentication may be reused for single sign-on, and a provider with pages of its own redirects the user and hands back on its resume path, see Writing an authentication module. The single sign-on policy of the provider wins over the OpenID Connect policy, which wins over the shared one.
When the user has been authenticated, and before the code is issued:
value and values of the claims parameter are compared with the user. Essential
values that do not match give access_denied, see
When the user does not match the requested values.sub of the user for the client is computed, public or pairwise, see
Identifying the user. If the client asked for a sub with a value in the
claims parameter, or with id_token_hint, it must be the same. Otherwise the client gets access_denied and no
code.The code is a random value that may only be used once. It is bound to the client, the redirect URI, the PKCE code
challenge, the nonce, the requirements of the request, the authenticated user and the released claims. It is valid
for 1 minute by default, authn-server.oidc.tokens.authorization-code-lifetime. A lifetime above 10 minutes, the
maximum that RFC 6749, Section 4.1.2, recommends, is logged as a warning at startup, and the application still starts.
Every error after the request was accepted, such as a failed or cancelled authentication or a failed attribute
release, is sent to the redirect URI as an error response, in the response mode of the request and with state. The
errors are mapped as described in Errors. A failed authentication removes the
authentication from the session, as for SAML.
The token endpoint accepts POST requests with the authorization_code grant, as OpenID Connect Core, Section 3.1.3,
and RFC 6749, Section 4.1.3, describe. No refresh tokens are issued, and offline_access is not offered. A request is
checked in this order:
grant_type must be authorization_code.redirect_uri must be identical to the one of the authentication request.code_challenge, code_verifier must be present and match it (RFC 7636, Section
4.6). A code_verifier sent for a request without a challenge is rejected.A code that is used a second time is rejected, and the access token that was issued for it is revoked, as RFC 6749,
Section 4.1.2, says. Every response, also an error response, carries Cache-Control: no-store.
| Failure | Error |
|---|---|
| No client authentication, an unknown client, a wrong secret or signature, a method that is not enabled or not the registered one, or an invalid or reused client assertion | invalid_client (HTTP status 401) |
A client assertion with an iat in the future, or older than authn-server.oidc.max-jwt-age |
invalid_client (HTTP status 401) |
| More than one client authentication method | invalid_request |
grant_type other than authorization_code |
unsupported_grant_type |
grant_type or code missing |
invalid_request |
| The code is unknown, expired, used before or issued to another client | invalid_grant |
redirect_uri missing or not the one of the authentication request |
invalid_grant |
code_verifier missing, not matching, or sent without a challenge |
invalid_grant |
| The ID token cannot be encrypted for the client | invalid_client |
| The client registry fails | server_error (HTTP status 500) |
A client must authenticate with the method it has registered as token_endpoint_auth_method. When it has registered
none, the default of OpenID Connect Dynamic Client Registration, client_secret_basic, applies. none is not
supported, so there are no public clients.
| Method | Enabled by default | How the client is verified |
|---|---|---|
private_key_jwt |
Yes | The client assertion is verified with the keys of the client, from jwks or jwks_uri. |
client_secret_basic |
No | The client secret, in the Authorization header. |
client_secret_post |
No | The client secret, as the client_secret parameter. |
client_secret_jwt |
No | The client assertion is verified with the client secret, with HS256, HS384 or HS512. |
The methods are enabled with authn-server.oidc.client-authentication-methods, see
Configuration:
authn-server:
oidc:
client-authentication-methods:
- private_key_jwt
- client_secret_basic
A client assertion, for private_key_jwt and client_secret_jwt, must hold:
iss and sub equal to the client_id,aud holding the URL of the token endpoint or the issuer, as the Swedish OpenID Connect Profile, Section 3.1.1,
recommends,exp, which has not passed,iat, if present, which is not in the future, and not older than authn-server.oidc.max-jwt-age when that is set.
It is optional, as OpenID Connect Core, Section 9, says,jti, which has not been used before. The value is remembered until the assertion expires.The clock skew of the OpenID Provider applies. A private_key_jwt assertion may be signed with RS256, RS384,
RS512, PS256, PS384, PS512, ES256, ES384 or ES512. A client may have several keys, for example an RSA
key and an EC key, or an old and a new key during a rollover; the kid of the assertion selects the key, and without a
kid every matching key is tried. A client that has registered token_endpoint_auth_signing_alg must sign with that
algorithm.
With authn-server.oidc.clients, the client secret is given next to the client metadata, never in it, see
Clients. In an OidcClientRecord, the secret is kept as the
client_secret field of the client metadata, the name that OpenID Connect Dynamic Client Registration uses. A client
resolved through OpenID Federation never has a secret:
metadata.setCustomField(OidcClientRecord.CLIENT_SECRET, secret);
The access token is an opaque random value. It reveals nothing about the user, and the OpenID Provider keeps what the
UserInfo endpoint needs together with it: the client, the sub, the scopes and the claims to deliver from the UserInfo
endpoint.
| Setting | Default |
|---|---|
authn-server.oidc.tokens.access-token-lifetime |
5 minutes |
authn-server.oidc.tokens.access-token-single-use |
true, so that the first UserInfo call uses up the token |
With single use off, the token may be used at the UserInfo endpoint until it expires or is revoked.
The ID token follows the Swedish OpenID Connect Profile, Section 3.2.1:
| Claim | Value |
|---|---|
iss |
The issuer. |
sub |
The sub of the user for the client, public or pairwise. |
aud |
The client_id. |
exp and iat |
The expiry and the issuance time. |
auth_time |
The time the user was authenticated. When single sign-on was used, the time of the original authentication. |
nonce |
The nonce of the request, when it had one. |
acr |
The authentication context that was used. It is always present, also when acr was not requested and when requested voluntary values could not be met. The latter is logged at INFO. |
Identity claims are only included when they were requested, following Section 4.2 of the profile:
claims parameter goes where the parameter says, in the ID token or from the UserInfo endpoint.claims parameter, which is also covered by a requested scope delivered
from the UserInfo endpoint, is delivered in both places.A request with only the openid scope and no claims parameter gets no identity claims.
Lifetime. 5 minutes by default, authn-server.oidc.tokens.id-token-lifetime. The profile does not allow more than
5 minutes, so a longer lifetime is logged as a warning at startup, and the application still starts.
Signing. The ID token is always signed, with the key chosen for the client, see Choosing the signing key for a client.
Encryption. When the client has registered id_token_encrypted_response_alg, the signed ID token is encrypted
for the client (nested JWT). The key is taken from the client’s jwks or jwks_uri: an encryption key of the type
the algorithm needs. Following
Sweden Connect Security Requirements, Section
3.2, the algorithms are:
id_token_encrypted_response_alg: RSA-OAEP, RSA-OAEP-256 or ECDH-ES.id_token_encrypted_response_enc: A128CBC-HS256, which is the default, A256CBC-HS512, A128GCM or A256GCM.A client that asks for other algorithms, or has no usable key, is a client configuration error: its authentication
request ends at the OpenID Provider, and the error is logged at WARN.
The UserInfo endpoint, /oidc/userinfo by default, gives the client the claims that belong in UserInfo, as the
Swedish OpenID Connect Profile, Section 4.1, requires. It accepts GET and POST.
Presenting the access token. The token is accepted in two ways, following RFC 6750, Section 2:
Authorization header: Authorization: Bearer <token>.access_token parameter of a form-encoded POST body.The URI query parameter of RFC 6750, Section 2.3, is not accepted. A request that carries the token in more than one way is rejected.
Single use. By default an access token may only be used once: the first successful call uses it up, and a second
call with the same token gets invalid_token. A call that fails does not use up the token. See
The access token for how to turn this off.
What the response holds. The response holds sub, the same value as in the ID token, and the claims whose
delivery target includes UserInfo, following Section 4.2 of the profile:
userinfo in the claims parameter,claims parameter, which is also covered by a requested scope
delivered from the UserInfo endpoint.A request with only the openid scope gets a response with only sub. The claims are those released when the
authentication completed, and kept with the access token. The release is not run again when the endpoint is called.
Signing. Responses are signed by default, authn-server.oidc.sign-user-info. A signed response is a JWT, signed
with the key chosen for the client, see Choosing the signing key for a client,
and it also holds iss and aud. The content type is then application/jwt.
sign-user-info |
The client has registered userinfo_signed_response_alg |
Response |
|---|---|---|
true (default) |
Yes or no | Signed JWT |
false |
No | Plain JSON (application/json) |
false |
Yes | Signed JWT |
Encryption. When the client has registered userinfo_encrypted_response_alg, the response is encrypted for the
client, with the same algorithms and key rules as the ID token, see The ID token. The content
encryption is given by userinfo_encrypted_response_enc, A128CBC-HS256 by default. A signed response is signed and
then encrypted. When signing is off and the client has not asked for it, the response is encrypted only, as OpenID
Connect Core, Section 5.3.2, allows: the encrypted payload is the JSON claims set.
A client whose declared algorithms cannot be used, or that has no usable encryption key, gets the error
invalid_client_metadata (HTTP status 400) and no claims. This is logged at WARN as a client configuration error.
Errors. Errors follow RFC 6750, Section 3, including the WWW-Authenticate header. Every response, also an error
response, carries Cache-Control: no-store.
| Failure | Response |
|---|---|
| No access token | HTTP status 401, WWW-Authenticate: Bearer without an error code |
| The token is sent in the URI query, in more than one way, or is malformed | invalid_request (HTTP status 400) |
| The token is unknown, expired, revoked or already used | invalid_token (HTTP status 401) |
| The response cannot be signed or encrypted for the client | invalid_client_metadata (HTTP status 400) |
The request processing is found in UserInfoRequestProcessor.
Authorization codes, access tokens and the jti values of used client assertions are kept in stores:
AuthorizationCodeStore, AccessTokenStore and
ClientAssertionReplayCache. The defaults keep them in memory, which only serves the
node they run on. A deployment with several nodes keeps them in Redis, by setting authn-server.storage.type, or the
setting of each store, to redis, see Running several nodes. Otherwise it
needs sticky sessions, so that the authentication request, the token request and the UserInfo request of a client
reach the same node.
The Redis stores give the same results as the in-memory ones, also when several nodes use them at once: a code is redeemed, a single use access token is used, and a client assertion is accepted, on one node only.
A store bean of the application, for example an AccessTokenStore, replaces the store that the settings choose.
Without Spring Boot, stores are assigned on the configurer:
oidc.authnRequestProcessor(p -> p
.authorizationCodeStore(codes)
.accessTokenStore(tokens)
.clientAssertionReplayCache(assertions));
The discovery document holds:
| Parameter | Value |
|---|---|
issuer |
The issuer. |
jwks_uri |
The URL of the JWKS. |
id_token_signing_alg_values_supported |
The algorithms the active signing keys can produce, those of the default key first. |
request_object_encryption_alg_values_supported |
The algorithms of the active decryption keys. Left out without decryption keys. |
scopes_supported |
The offered scopes. |
claims_supported |
The supported claims. |
acr_values_supported |
The authentication context URIs of the providers. |
subject_types_supported |
The subject types of the subject generator factory, by default public and pairwise. |
ui_locales_supported |
The configured UI locales. Left out when none are configured. |
authorization_endpoint |
The URL of the authorization endpoint. |
response_types_supported |
code. |
response_modes_supported |
query and form_post. |
claims_parameter_supported |
true. |
request_parameter_supported |
true. |
request_uri_parameter_supported |
true. |
require_request_uri_registration |
true. |
request_object_signing_alg_values_supported |
The algorithms that signed request objects are accepted with, and none when unsigned request objects are accepted. |
code_challenge_methods_supported |
S256. |
https://id.oidc.se/disco/userMessageSupported |
true when user messages are supported for OpenID Connect, see Authentication Request Parameter Extensions for the Swedish OpenID Connect Profile. Left out otherwise. |
https://id.oidc.se/disco/userMessageSupportedMimeTypes |
text/plain and text/markdown, when user messages are supported. Left out otherwise. |
https://id.oidc.se/disco/authnProviderSupported |
true. |
token_endpoint |
The URL of the token endpoint. |
grant_types_supported |
authorization_code. |
token_endpoint_auth_methods_supported |
The enabled client authentication methods. |
token_endpoint_auth_signing_alg_values_supported |
The algorithms accepted for client assertions: those of private_key_jwt and of client_secret_jwt, when enabled. Never none. Left out when neither method is enabled. |
id_token_encryption_alg_values_supported |
RSA-OAEP-256, RSA-OAEP and ECDH-ES. |
id_token_encryption_enc_values_supported |
A128CBC-HS256, A256CBC-HS512, A128GCM and A256GCM. |
userinfo_endpoint |
The URL of the UserInfo endpoint. |
userinfo_signing_alg_values_supported |
The algorithms the active signing keys can produce, as for the ID token. |
userinfo_encryption_alg_values_supported |
RSA-OAEP-256, RSA-OAEP and ECDH-ES. |
userinfo_encryption_enc_values_supported |
A128CBC-HS256, A256CBC-HS512, A128GCM and A256GCM. |
Parameters are added through configuration, as a map of name to value. A value may be a string, a boolean, a list or a map:
authn-server:
oidc:
discovery:
additional-parameters:
service_documentation: https://op.example.com/docs
op_policy_uri: https://op.example.com/policy
"[https://example.com/disco/features]":
- one
- two
Names that hold a : or a / must be given within brackets. An additional parameter cannot replace a parameter that
the OpenID Provider sets, and the application does not start if it tries to, or if a value is invalid for a
parameter that OpenID Connect defines.
To change the document in ways the properties cannot express, assign a customizer in an
adapter. It gets the built document, including the
additional parameters, as a Nimbus OIDCProviderMetadata, and may change anything:
@Bean
AuthnServerConfigurerAdapter discoveryAdjustments() {
return (http, configurer) -> configurer.protocol(OidcProviderConfigurer.class, oidc -> oidc
.discoveryEndpoint(discovery -> discovery.providerMetadataCustomizer(
metadata -> metadata.setCustomParameter("https://example.com/disco/flag", true))));
}
A change that should appear in the discovery document belongs here, also when the OpenID Provider is a member of an OpenID Federation: the entity configuration is built from the discovery document, so both documents get the change. See Changing the entity configuration.
The OpenID Provider can be a member of an OpenID Federation, such as the Sweden Connect federation, where an OpenID Provider is registered from its published entity configuration. This section is about the OpenID Provider’s own membership. Clients resolved through a federation are described in The federation backend.
Federation is off by default. To join a federation, enable it, and give the federation keys and the authority hints:
authn-server:
oidc:
federation:
enabled: true
authority-hints:
- https://fed.swedenconnect.se/intermediate
keys:
- credential:
bundle: federation-2026
The application does not start when federation is enabled and the keys or the authority hints are missing. It does
not start either when the descriptive metadata lacks what the Sweden Connect federation
requires: at least one e-mail address in contacts, and a logo_uri that is an HTTPS URL.
All properties are listed in Configuration. In code, the same settings are made
with OidcProviderConfigurer.federation(...), see OidcFederationConfigurer.
The federation keys are separate from the OpenID Connect signing keys, as OpenID Federation 1.0, Section 3.1.1, recommends. They are configured as a list, like the signing keys, and a key is:
jwks of the entity configuration and used for signing, orThe first active key signs the entity configuration. The key requirements and the key IDs follow the same rules as for
the OpenID Connect keys, see Key requirements and key IDs. The signing algorithm is
RS256 for an RSA key and the ES algorithm of the curve for an EC key, which is what both the Swedish OpenID
Federation profile, Section 6, and Sweden Connect Security Requirements, Section 3, allow.
A federation key is rolled over in the same way as a signing key, following Section 4 of the security requirements:
future. It is published in the entity configuration but not used.active and remove the old key.authn-server:
oidc:
federation:
keys:
- credential:
bundle: federation-2026
- credential:
bundle: federation-2027
state: future
The entity configuration is a signed JWT with the type entity-statement+jwt, served with the content type
application/entity-statement+jwt. It holds, following OpenID Federation 1.0, Section 3:
| Claim | Value |
|---|---|
iss and sub |
The entity identifier, which is the issuer. |
iat and exp |
When it was signed, and when it expires. The lifetime is 1 day by default, authn-server.oidc.federation.entity-configuration-lifetime. |
jwks |
The federation keys, active and future. |
authority_hints |
The configured authority hints. |
trust_marks |
The trust marks of the OpenID Provider, when it has any, see The trust marks of the OpenID Provider. |
metadata |
The openid_provider metadata only. The OpenID Provider is a leaf entity, and never publishes federation_entity metadata (Swedish OpenID Federation profile, Section 2.2). |
The openid_provider metadata is the discovery document, as it is after the additional
parameters and the customizer of the discovery document have been applied, with these parameters added unless the
discovery document already has them:
display_name#sv, organization_name#sv, logo_uri and
contacts,client_registration_types_supported with the value automatic, since clients are resolved through the
federation.How it is kept fresh. The signed entity configuration is kept in memory and served from there. Each time it is asked for, the OpenID Provider checks whether it is still fresh, and builds and signs it again only when needed:
The OpenID Connect keys are part of the discovery document. They, like the federation keys, are read at startup, so a change of key state takes effect when the application is restarted, and the entity configuration is then built anew.
The descriptive information about the service and its organization is given once for all protocols, under
authn-server.entity-information, see Entity information. The same values
are then published in the SAML metadata and in the OpenID Provider metadata, and a protocol may override any of them.
The information is mapped to the parameters of Sweden Connect OpenID Connect Metadata Requirements, Section 3:
| Parameter | Taken from |
|---|---|
display_name |
The UI display names, one parameter per language, such as display_name#sv and display_name#en. |
description |
The UI descriptions, one parameter per language. |
organization_name |
The organization names, one parameter per language. |
organization_uri |
The first organization URL. |
organization_identifier |
urn:glue:iso6523:0007:<number> when the organization number is ten digits, and otherwise left out. |
logo_uri |
The first logo without a language, or else the first logo. A logo given as a path is relative to the base URL. |
contacts |
The e-mail addresses of the technical and support contact persons, without duplicates. A mailto: prefix is removed. |
Each parameter may also be given directly under authn-server.oidc.entity-information, which wins over the mapped
value, see Entity information for OpenID Connect. In code, the overrides
are OidcProviderConfigurer.entityInformation(...) and OidcProviderConfigurer.entityMetadata(...), and the resulting
values are given by OidcProviderConfigurer.getEntityMetadata(), see OidcEntityMetadata.
The parameters are published in the entity configuration and not in the discovery document. When the discovery document holds one of them, for example set by its customizer, the value of the discovery document is used in both documents, so that they never disagree.
The trust marks that the OpenID Provider holds, such as the level of assurance trust marks of the Sweden Connect federation, are published in its entity configuration. They are configured per trust mark type, separately from the trust marks that are required of clients, since the OpenID Provider may hold other marks than those it requires:
authn-server:
oidc:
federation:
trust-marks:
- type: https://id.swedenconnect.se/loa/loa3
issuer: https://fed.swedenconnect.se/tmi-loa
jwks: file:/opt/config/tmi-loa-jwks.json
trust-mark-cache-directory: /var/op/trust-marks
Each trust mark is fetched from the trust mark endpoint of its issuer (OpenID Federation 1.0, Section 8.6) when the
application starts, and again when three quarters of its lifetime have passed. A trust mark without exp is kept as
it is and never fetched again. The trust mark endpoint is the configured endpoint, or else the
federation_trust_mark_endpoint that the issuer publishes in its entity configuration, verified with the issuer’s
keys. An issuer that cannot be reached for its entity configuration is a failed fetch, handled as below.
The check. A fetched trust mark is only published when it passes the check: its signature is verified with the
configured keys of the issuer (jwks), typ must be trust-mark+jwt, iss must be the issuer, sub must be the
entity identifier of the OpenID Provider, trust_mark_type must be the configured type, and it must not have expired.
The trust chain of the issuer is not validated.
Failures. A trust mark that cannot be fetched, or that fails the check, never stops the OpenID Provider, at startup or later:
authn-server.oidc.federation.trust-mark-retry-interval.The state of each trust mark type, whether a valid trust mark is published and whether the latest attempt failed, is
recorded in ProviderTrustMarks. With Spring Boot it is a bean, and the oidc-trust-marks
health indicator reads it with getStates(), see Monitoring and managing the server.
The cache directory. When a cache directory is given, every accepted trust mark is written there, and the stored
trust marks are read at startup. This means that the OpenID Provider publishes its trust marks at once after a
restart, also when an issuer cannot be reached. A stored trust mark that has expired, or that fails the check, is not
used. With Spring Boot the directory is oidc/trust-marks in authn-server.cache-directory, see
State that survives a restart, or
trust-mark-cache-directory when that is set. Without either, nothing is stored.
Several nodes. The trust marks and their state are kept in a ProviderTrustMarkStore,
in memory by default. When they are kept in Redis, by authn-server.storage.type or
authn-server.oidc.storage.trust-marks, one node at a time fetches and renews them, every node publishes them, and
getStates() gives the same answer on every node. The cache directory is then not used. See
Background jobs and trust marks.
There are two ways to change what the OpenID Provider publishes, and they differ in which documents they reach:
authn-server.oidc.discovery.additional-parameters and the discovery customizer, see
Extending the document. The entity configuration is built from the discovery document, so
a change made here reaches both documents. A change that only concerns the OpenID Provider metadata belongs
here.authn-server.oidc.federation.additional-parameters and the entity configuration
customizer. They are applied last and reach only the entity configuration. Use them for the claims of the
entity statement, such as trust_anchor_hints, and for metadata that only makes sense in a federation.The additional parameters of the entity configuration are set as claims. A metadata parameter is merged into the
metadata, per entity type and parameter:
authn-server:
oidc:
federation:
additional-parameters:
trust_anchor_hints:
- https://fed.swedenconnect.se/trust-anchor
The customizer gets the claims as a mutable map, where metadata and its openid_provider entry are mutable maps
too, and may change anything but iss, sub, iat and exp, which the OpenID Provider sets. It runs each time the
entity configuration is built. Publishing federation_entity metadata stops the application from starting.
@Bean
AuthnServerConfigurerAdapter entityConfigurationAdjustments() {
return (http, configurer) -> configurer.protocol(OidcProviderConfigurer.class, oidc -> oidc
.federation(federation -> federation.entityConfigurationCustomizer(claims -> {
@SuppressWarnings("unchecked")
final Map<String, Object> op =
(Map<String, Object>) ((Map<String, Object>) claims.get("metadata")).get("openid_provider");
op.put("https://example.com/federation-only", true);
})));
}
Copyright © 2026, Sweden Connect. Licensed under version 2.0 of the Apache License.