> For the complete documentation index, see [llms.txt](https://docs.fortifiedid.se/access/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.fortifiedid.se/access/key-components/modules/oidc/signing-keys.md).

# 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.

## Overview

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.

## Recommended configuration

The recommended way to configure OIDC signing keys is `sign_ref`.

Example:

```json
"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

Legacy single-key configuration is still supported.

Example:

```json
"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.

{% hint style="info" %}
If `sign_ref` is configured, Access uses `sign_ref` for OIDC signing keys. The legacy `keystore` configuration is then treated only as backward-compatible fallback.
{% endhint %}

## Key selection rule

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.

## Runtime rotation

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

## JWKS behavior

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/key-components/modules/oidc/jwks-endpoint.md).

## `kid` behavior

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.

## Startup validation

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.

## Operational guidance

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

## Example overlap

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.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.fortifiedid.se/access/key-components/modules/oidc/signing-keys.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
