SurePassID Identity Provider
Signing-Key Rotation Guide - OIDC & SAML2
This guide describes how to rotate the RSA signing
certificate that SurePassID uses to sign tokens (OIDC) and assertions
(SAML2), for both the general-purpose OIDC provider
(oidc/{domain}/*) and the SAML2 IdP
(login/{domain}, meta/{domain}). It also
covers the Entra EAM slice
(eam/{domain}/*), which shares the same signing
infrastructure.
The golden rule for every protocol is identical:
Never switch which key you sign with until every relying party already trusts the new key.
Both protocols implement this with a two-key overlap window: publish the new key alongside the old one, wait for propagation, then promote the new key to primary. The difference is how relying parties learn about the new key — OIDC relying parties re-fetch it live (easy), while many SAML SPs cache it at configuration time (needs coordination).
These setting are specified in the SurePassID Admin portal used by the SurePassID Identity Provider.
1. Shared model: primary + secondary key slots
Every partner (tenant) stores its signing material per tenant in
PartnerSSOSettings:
| Slot | Private key (encrypted) | Public cert | Password |
|---|---|---|---|
| Primary (current) | [SSO Cert] |
[SSO Cert Public] |
[SSO PW] |
| Secondary (rotation) | [SSO Cert Secondary] |
[SSO Cert Public Secondary] |
[SSO PW Secondary] |
The IdP always signs with the primary slot. The secondary slot is publish-only: it is advertised to relying parties (JWKS / metadata) so they will accept signatures from either key, but it is not used to sign until it is promoted into the primary slot.
Two helper operations drive the whole lifecycle
(InterfacePartnerSsoSettings,
SurePassClassLib/AuthServerAPI/Database/Interfaces/Partner/interfacePartnerSSOSettings.cs):
| Helper | Effect |
|---|---|
AddSecondaryKey(keyManagement, domainName, partnerSsoSettings) |
Generates a fresh signing cert and writes it into the secondary slot. Does not touch the primary. Caller persists the row. |
PromoteSecondaryKey(partnerSsoSettings) |
Copies secondary → primary and clears the secondary slot, completing the rotation. Caller persists the row. |
Because both protocols read from the same slots, one rotation covers OIDC, SAML2, and EAM at once for a given tenant.
2. How each protocol advertises keys
| Protocol | Endpoint | How the new key is published | How RPs pick the right key |
|---|---|---|---|
| OIDC | GET oidc/{domain}/jwks |
JWKS lists both keys during overlap
(OidcEndpointHelper.TryLoadSigningCertificates →
EamJsonWebKey.CreateJwksJson). |
Each JWK has a kid = base64url(SHA-1 cert thumbprint).
Tokens carry the matching kid in their header; the RP
selects the JWK whose kid matches. |
| SAML2 | GET meta/{domain} |
IdP metadata lists both
<KeyDescriptor use="signing"> elements during overlap
(Saml2Util.Helper.ExportIdentityProviderMetadata). |
SAML has no kid. The SP tries each
configured signing cert until one verifies the assertion signature. |
| EAM | GET eam/{domain}/jwks |
Same JWKS mechanism as OIDC. | Same kid matching as OIDC. |
Key consequence: OIDC/EAM relying parties refetch
JWKS live and match by kid, so overlap is nearly
transparent. SAML SPs frequently pin certs at config time and must be
told to re-import — the overlap window buys you time to coordinate
that.
3. OIDC key rotation (and EAM)
OIDC is the easy case because compliant relying parties fetch
jwks_uri on demand and cache by kid with short
TTLs.
Procedure
Publish the new key (start overlap). Run
AddSecondaryKey(...)for the tenant and persist. The secondary cert now appears in JWKS:GET https://<host>/oidc/{domain}/jwksThe response
keysarray now has two entries with distinctkidvalues. You are still signing with the primary key, so nothing an RP has cached is invalidated. Newly issued tokens still carry the primarykid.Wait for propagation. Allow at least the RPs' JWKS cache TTL to elapse (commonly 1–24 hours) so every RP has seen the two-key JWKS. Live tokens signed by the primary key continue to validate.
Promote the new key. Run
PromoteSecondaryKey(...)and persist. From now on tokens are signed with the new key and carry the newkid. RPs that refreshed JWKS in step 2 already have that key, so validation succeeds. Any RP with a stale cache re-fetches JWKS (triggered by the unknownkid) and picks up the new key.Cleanup. After promotion, JWKS returns to a single key (the new one). The old key can expire harmlessly; no token in flight is still signed with it once the longest-lived access/id token TTL has passed.
OIDC validation checklist
GET /oidc/{domain}/jwksreturns two keys during overlap, one key after cleanup.- A token minted before promotion validates against
the pre-promotion
kid. - A token minted after promotion carries the new
kidand validates against the new JWK. - Diagnostic trace logs
publishing secondary signing key ... for two-key rotationwhile the secondary slot is populated.
Covered by automated tests in
Saml2ProtocolTests/OidcJwksRotationTests.cs(two-key JWKS output andkid-matched validation) and documented inOIDC_QA_Test_Plan.md§5.10.
4. SAML2 key rotation
SAML is the harder case: many SPs store the IdP signing cert at
configuration time and do not poll
meta/{domain}. There are two paths depending on the SP's
capabilities.
Path A — SPs that consume your metadata URL (best case)
If the SP is configured to refresh from (or can re-import) your
meta/{domain} URL:
- Publish the new key (start overlap). Run
AddSecondaryKey(...)and persist.GET /meta/{domain}now advertises two<KeyDescriptor use="signing">elements. Keep signing with the primary. - Have SPs re-import / let them refresh. SPs that re-import the metadata now trust assertions signed by either key. Wait for your rollout window so every SP has re-imported.
- Promote. Run
PromoteSecondaryKey(...)and persist. Assertions are now signed with the new key, which the SPs already trust. - Cleanup. Metadata returns to a single (new)
<KeyDescriptor>. SPs re-import at their leisure; the old cert can expire.
Path B — SPs that pin a single cert and never re-poll (common reality)
Many older or hand-configured SPs (e.g. older ADFS, custom apps)
accept only one signing cert and ignore extra
<KeyDescriptor> entries. Publishing both certs in
your metadata does not help them — the SP must be updated on
their side. The overlap window still protects you, but
the manual step is unavoidable:
- Generate the secondary key (still signing with the
primary):
AddSecondaryKey(...). - Distribute the new public cert to every SP owner and have them add/import it while the old cert is still active. SPs that support two certs add both; single-cert SPs must schedule a cutover.
- Confirm coverage. Do not promote until every SP trusts the new cert.
- Promote with
PromoteSecondaryKey(...). Any SP that has not been updated by this point will break, so schedule the promote around the slowest SP.
SAML2 validation checklist
GET /meta/{domain}contains two<KeyDescriptor use="signing">blocks during overlap, one after cleanup (verified bySaml2UtilTests.ExportIdentityProviderMetadata_WithSecondaryCert_*).- Both public certs appear in the metadata during overlap.
- After promotion, a fresh SSO login produces an assertion the SP accepts (it was pre-loaded with the new cert).
- The IdP
entityIDand endpoint URLs are unchanged by rotation (only the key changes).
5. Emergency rotation (suspected key compromise)
When a private key may be compromised you cannot wait for a graceful overlap — the old key must stop being trusted immediately. Accept that some relying parties will break until re-configured.
AddSecondaryKey(...)then immediatelyPromoteSecondaryKey(...)(or generate + promote in one operator action) so the compromised key is no longer in either slot.- OIDC/EAM: RPs pick up the new
kidfrom JWKS on their next validation; impact is minimal. - SAML: every SP that pinned the old cert breaks until it imports the new one — notify SP owners before/at cutover.
- Revoke any long-lived tokens/sessions still trusting the old key (for OIDC, see the refresh-token revocation path; for SAML, force re-authentication).
6. Best practices (both protocols)
- Rotate proactively, on a schedule. Plan rotation
well before expiry (e.g. 30–60 days out), not on the expiry date.
Monitor the OIDC "near expiry" diagnostic trace
(
OidcEndpointHelper.TraceCertificateExpiry). - Always overlap; never hard-swap. Populate the secondary slot and let it propagate before promoting. A direct edit of the primary cert with no overlap is the single most common cause of SSO outages.
- One rotation per tenant covers all protocols. OIDC,
SAML2, and EAM share the same primary/secondary slots, so a single
AddSecondaryKey→propagate→PromoteSecondaryKeycycle rotates every protocol for that tenant. - Size the overlap window to the slowest consumer. For OIDC, the JWKS cache TTL (hours). For SAML, the time needed for the slowest SP owner to re-import — often days. Do not promote early.
- Inventory your SAML SPs by capability. Know which SPs auto-refresh metadata (Path A) vs. pin a single cert (Path B). Path B SPs gate your promotion schedule.
- Keep the
entityID/ issuer stable. Rotation changes only the key, never the issuer or endpoint URLs, so SP trust configuration (beyond the cert) stays valid. - Use adequate key strength and lifetime. Prefer RSA 2048+; a 1–2 year cert lifetime with proactive rotation balances security and operational churn.
- Verify before promoting. Confirm the new key is
actually being published (
/oidc/{domain}/jwksshows two keys;/meta/{domain}shows twoKeyDescriptors) before you promote. - Clean up after promotion. Confirm the secondary slot is cleared and endpoints return a single key, so you start the next rotation from a known-clean state.
- Log and audit each step. Record who ran
AddSecondaryKey/PromoteSecondaryKeyand when, and capture the pre/post thumbprints for traceability.
7. Quick reference
| Step | OIDC / EAM | SAML2 |
|---|---|---|
| 1. Start overlap | AddSecondaryKey → JWKS shows 2 keys |
AddSecondaryKey → metadata shows 2
KeyDescriptors |
| 2. Propagate | Wait for JWKS cache TTL | Wait for SPs to re-import (Path A) / distribute cert (Path B) |
| 3. Promote | PromoteSecondaryKey → sign with new
kid |
PromoteSecondaryKey → sign with new cert |
| 4. Cleanup | JWKS back to 1 key; old key expires | Metadata back to 1 KeyDescriptor; old cert expires |
| Key selection | kid = base64url(SHA-1 thumbprint) |
SP tries each configured signing cert |
| Propagation model | RPs refetch JWKS live | Many SPs pin cert at config time |
© 2013–2026 SurePassID. All rights reserved. Protected by patents pending. SurePassID, the SurePassID logo and design, and Secure SSO are registered trademarks or trademarks of SurePassID, Corp. in the United States and/or other jurisdictions. All other marks and names mentioned herein may be trademarks of their respective companies.
SurePassID 360 Central Avenue #800 St. Petersburg, FL 33701 USA +1 (888) 200-8144 surepassid.com