Concepts
OIDC discovery and JWKS
The single biggest operational difference between OIDC and SAML is not the token format, it is that an OIDC provider publishes where everything lives and which keys are currently valid. That one design decision removes the failure that breaks more SAML connections than anything else: a certificate that rotated and nobody was told.
The discovery document
Every OIDC provider serves a JSON document at a well-known path under its issuer URL. It lists the authorization endpoint, the token endpoint, the JWKS location, which scopes and claims are supported, and which signing algorithms it will use. One HTTP request replaces the page of values a SAML setup asks a customer to copy by hand.
This matters more than it sounds. Every value transcribed by a human is a value that can be transcribed wrongly, and a SAML setup transcribes five or six of them. Discovery reduces that to one: the issuer URL. If the issuer is right, everything else is derived; if it is wrong, the failure is immediate and obvious rather than appearing three fields later as an audience mismatch.
The document is fetched during connection setup and refreshed periodically, because providers do move endpoints — rarely, but they do, and a cached configuration from eighteen months ago is how a connection breaks with no change on either side.
1{2 "issuer": "https://idp.foo-corp.example",3 "authorization_endpoint": "https://idp.foo-corp.example/oauth2/v1/authorize",4 "token_endpoint": "https://idp.foo-corp.example/oauth2/v1/token",5 "userinfo_endpoint": "https://idp.foo-corp.example/oauth2/v1/userinfo",6 "jwks_uri": "https://idp.foo-corp.example/oauth2/v1/keys",7 "response_types_supported": ["code", "id_token", "code id_token"],8 "subject_types_supported": ["public"],9 "id_token_signing_alg_values_supported": ["RS256"],10 "scopes_supported": ["openid", "profile", "email", "groups"],11 "claims_supported": ["sub", "email", "email_verified", "given_name", "family_name"]12}
What a JWKS document holds
The JWKS — JSON Web Key Set — is the list of public keys the provider is currently signing tokens with. Each key carries a key id, and every token the provider issues names the key id it was signed with in its header. Verification is therefore mechanical: read the kid from the token, find the matching key in the set, verify.
Because the set can hold several keys at once, rotation is uneventful. The provider publishes the new key alongside the old, starts signing with the new one, and removes the old after existing tokens have expired. Nobody coordinates anything, and nothing breaks.
| Field | Meaning | Why it matters |
|---|---|---|
| kid | Key id | The token header names it. This is how the right key is selected. |
| kty | Key type, usually RSA or EC | Determines how the key material is interpreted. |
| alg | Signing algorithm, usually RS256 | Verify against the algorithm the provider advertises, never against whatever the token claims. |
| use | Intended use, "sig" for signing | A key marked for encryption should not be accepted for signature verification. |
| n and e | RSA modulus and exponent | The public key material itself, base64url encoded. |
Why this beats certificate rotation
In SAML, the signing certificate is exchanged once during setup and stored on the service provider side. When the provider rotates it, every service provider holding the old certificate starts rejecting valid assertions, and the only warning is a support ticket saying that sign-in stopped working overnight. There is no mechanism in the protocol that would have prevented it.
In OIDC, the equivalent event is invisible. The verifier fetches the key set, finds the kid it needs, and continues. The comparison below is not a claim that OIDC is a better protocol in general — SAML does things OIDC does not — but on this specific axis the difference is stark and it is worth knowing which one a customer is handing you.
| Event | SAML | OIDC |
|---|---|---|
| Provider rotates its key | Every integration breaks until the new certificate is installed | Transparent, provided the key set is refetched |
| Two keys valid at once | Not supported by most configurations | Normal — the set holds both |
| Warning before expiry | Whatever the customer's IT team remembers to send | Not needed |
| Recovery | Obtain and install the new certificate, per connection | Refetch the key set |
Paycux fetches discovery and key set documents for you and keeps them current, so neither rotation appears in your application. This guide exists because the mechanism is worth understanding when you are reading a provider's own documentation during a difficult setup.
Caching without creating an outage
If you are verifying tokens yourself anywhere — an internal service, a gateway, a background worker — the caching behaviour is where the bugs live. Fetching the key set on every verification is a hard dependency on the provider being reachable, and caching it indefinitely reintroduces exactly the rotation problem OIDC solves.
- 1Cache the key set, keyed by issuer, with a lifetime measured in hours rather than minutes.
- 2On encountering an unknown kid, refetch once — a rotation just happened. Do not refetch on every unknown kid, or a stream of forged tokens becomes a request amplifier pointed at your customer's provider.
- 3Rate limit that refetch per issuer, and serve the stale set if the refetch fails. Stale keys still verify tokens signed before the rotation.
- 4Validate the issuer and audience claims as well as the signature. A correctly signed token from a different application is still not yours.
- 5Reject tokens whose alg does not match what the discovery document advertises, and never accept none.
1const cache = new Map();23async function keyFor(issuer, kid) {4 let set = cache.get(issuer);56 if (!set || !set.keys[kid]) {7 // Bilinmeyen kid yalniz bir kez yeniden cekilir; hata halinde bayat set kullanilir.8 try {9 set = await fetchJwks(issuer);10 cache.set(issuer, set);11 } catch (err) {12 if (!set) throw err;13 }14 }1516 return set.keys[kid];17}