Skip to content

Fortified IDDocs

OIDC signing keys

Configuration and behavior for OIDC signing keys, JWKS publication, and key rotation

This page describes how FortifiedID Access selects OIDC signing keys, publishes keys in JWKS, and supports overlapping key rotation for OIDC tokens.

OIDC signing keys are used for:

  • signing ID tokens
  • signing JWT access tokens when use_jwt_access_tokens is enabled
  • publishing public key material through the JWKS endpoint

Access supports both:

  • a legacy single-key configuration based on keystore
  • a multi-key configuration based on sign_ref

Use sign_ref when you want overlapping key rotation.

The recommended way to configure OIDC signing keys is sign_ref.

Example:

"sign_ref": [
{
"keystore": {
"path": "/fortified_test/keys/oidc-2026-q1.p12",
"password": "secret",
"key_password": "secret",
"alias": "oidc-2026-q1",
"type": "PKCS12"
}
},
{
"keystore": {
"path": "/fortified_test/keys/oidc-2026-q2.p12",
"password": "secret",
"key_password": "secret",
"alias": "oidc-2026-q2",
"type": "PKCS12"
}
}
]

Each sign_ref entry contains its own keystore configuration. Alias and passwords are resolved from that specific keystore entry.

Legacy single-key configuration is still supported.

Example:

"keystore": {
"path": "/fortified_test/keystore.p12",
"password": "secret",
"type": "PKCS12"
},
"sign_jwt_keystore_alias": "jwt",
"sign_jwt_keystore_password": "secret"

This remains useful for environments that do not yet need overlapping rotation.

When multiple OIDC signing keys are configured, Access selects the active signing key using certificate validity dates.

The rule is:

  • consider only keys that are valid at the current time
  • among those keys, choose the key with the earliest notBefore
  • if multiple keys still tie, use a deterministic tie-breaker based on alias
  • if a tie still remains, use kid
  • if no configured key is currently valid, use the first configured signing key in sign_ref

This makes key selection deterministic while still providing a predictable fallback when no certificate is currently valid.

OIDC signing key selection happens at signing time, not only during startup.

This means:

  • a future-dated key can be published in JWKS before it becomes active
  • Access can continue signing with the current key during the overlap period
  • Access keeps signing with the older still-valid key during overlap
  • Access automatically moves to the next key only when the older key is no longer valid
  • if no configured key is currently valid, Access falls back to the first configured key
  • no restart is required for the actual switch

The JWKS endpoint publishes all configured OIDC signing keys from sign_ref.

This allows:

  • clients to verify tokens signed with the current key
  • clients to continue verifying tokens signed with the previous key while those tokens are still valid
  • administrators to pre-publish the next signing key before it starts being used
  • administrators to decide when an old key should be removed from the configuration

For JWKS details, see JWKS endpoint.

Access sets the JWT header kid from the signing key that was actually used for that token.

This applies to:

  • ID tokens
  • JWT access tokens

The kid value is expected to match one of the keys published in JWKS.

Access validates OIDC signing keys at startup.

Startup fails when:

  • the configured alias, keystore password, or key password does not resolve usable key material
  • sign_ref is configured but no signing key can be resolved from it

Startup does not fail only because all configured certificates are expired or future-dated.

This preserves runtime rotation and the configured first-key fallback while still failing clearly for unusable key material.

For safe rotation:

  • add the next signing key to sign_ref
  • keep the previous key in sign_ref during the overlap period
  • wait at least max token lifetime + JWKS cache TTL before removing the old key
  • remove the old key only when you no longer need clients to verify tokens signed with it

Assume:

  • Key A: notBefore=2026-06-01, notAfter=2027-06-01
  • Key B: notBefore=2026-08-01, notAfter=2027-08-01

Behavior:

  • on 2026-07-15, Access signs with Key A
  • on 2026-08-10, both keys are valid, and Access still signs with Key A because it is the oldest certificate that remains valid
  • after 2027-06-01, Key A is no longer valid, and Access signs with Key B
  • before 2026-06-01, neither key is valid yet, and Access falls back to the first configured signing key

Both keys may still be published in JWKS during the overlap period.