Skip to content

OpenID Connect

OpenID Connect adds sign-in to OAuth 2.0. Plain OAuth gives an application access to an account, but it does not tell the application who the user is. OpenID Connect fills that gap with the ID token.

An application that includes the openid scope in its authorization request receives an ID token together with the access token. The ID token is a signed JSON Web Token (JWT), a compact JSON document with a signature that anyone holding the right key can verify. It states which account signed in, and it is issued for that one application. Applications use it to identify the user, for example to let the users of a self-hosted service sign in with their mail account.

The built-in OAuth server is also an OpenID Connect provider. It needs no configuration for this role beyond the signing key generated at installation.

This page describes Stalwart as a provider, the side that issues ID tokens. To let Stalwart accept sign-ins from an external provider instead, configure an OpenID Connect directory.

Relying parties, the applications that accept the ID tokens, trust a token only when its signature verifies. ID tokens are signed with the algorithm in signatureAlgorithm and the private key in signatureKey. Both are fields of the OidcProvider singleton (found in the WebUI under Settings › Authentication › OIDC Provider).

Fresh installations use es256 with a generated P-256 key. The schema default of the field, hs256, applies only when the field has never been set.

The supported algorithms fall into two groups. The asymmetric algorithms sign with a private key and are verified with a public key:

  • ECDSA: es256, es384
  • RSASSA-PSS: ps256, ps384, ps512
  • RSASSA-PKCS1-v1_5: rs256, rs384, rs512

With an asymmetric algorithm, the server publishes the public key in the JSON Web Key Set at /auth/jwks.json. Relying parties fetch the key from there and verify the signature on their own.

The symmetric HMAC algorithms (hs256, hs384, hs512) remain supported, but they sign and verify with the same secret. The key set is then empty, because publishing the verification key would publish the secret. A relying party can verify the token only if it is given the secret. An HMAC algorithm with an empty key signs with a random key that changes at every restart.

If the configured key cannot be loaded, the server reports the error and signs with a temporary ES256 key until the key is corrected. The temporary key is published in the key set, so relying parties can still verify tokens in the meantime.

Changing the signing key or the algorithm invalidates the ID tokens already issued. Access and refresh tokens stay valid, because they are protected by a different key.

The following update configures ES256 with a key given directly in the object. The key shown is an example only and must not be reused:

{
"signatureAlgorithm": "es256",
"signatureKey": {
"@type": "Text",
"secret": "-----BEGIN PRIVATE KEY-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQggybcqc86ulFFiOon\nWiYrLO4z8/kmkqvA7wGElBok9IqhRANCAAQxZK68FnQtHC0eyh8CA05xRIvxhVHn\n0ymka6XBh9aFtW4wfeoKhTkSKjHc/zjh9Rr2dr3kvmYe80fMGhW4ycGA\n-----END PRIVATE KEY-----\n"
}
}

To keep the key out of the configuration, use the EnvironmentVariable or File variant of signatureKey. These variants read the PEM from an environment variable or a file.

An ID token is issued in the token response of an authorization code or device grant, when the granted scope contains openid. It is never issued in the response to a refresh. The token is valid for the time set in idTokenExpiry, and it is signed but never encrypted. It carries these claims:

ClaimValue
issThe issuer identifier, the server’s public https URL
subThe account id
audThe client id of the application
iat, nbf, expIssue time, start and end of validity
nonceThe nonce of the authorization request, when one was sent
preferred_usernameThe account name
emailThe account name, which is its primary email address
descriptionThe account description, when set

The token does not contain auth_time. The discovery document lists in id_token_signing_alg_values_supported only the algorithm currently configured, for example ["ES256"] on a default installation.

The UserInfo endpoint /auth/userinfo gives an application a second way to learn who the user is. It returns the claims of the account that owns the presented access token. Any valid access token can reach it, including a token whose only scope is openid.

The discovery document lists sub, name, preferred_username, email and email_verified in claims_supported, and public in subject_types_supported.

A relying party that signs a user out sends the browser to the end-session endpoint /auth/logout, defined by OpenID Connect RP-Initiated Logout 1.0. The request carries id_token_hint or client_id, and optionally post_logout_redirect_uri and state.

The endpoint returns the browser to the relying party only when the URI is registered for the client. Otherwise it shows a signed-out page. Endpoints and metadata gives the matching rules.

The endpoint revokes no tokens and ends no session on the server. A relying party that signs a user out must therefore also revoke its tokens at the revocation endpoint.