OIDC looks simple from the outside — and is full of footguns when implemented per-app
OpenID Connect on top of OAuth 2.0 is the dominant federated identity protocol for modern stacks. Every major enterprise IdP supports it; every cloud workforce IdP exposes it; every modern application framework offers a library for it. The temptation is to assume that adding a few lines of SDK code per application is enough.
In practice, an OIDC relying party that handles real production traffic must do a long list of things correctly. The ID token's signature must be verified against the IdP's published JWKS, with the correct key selected by kid and the cache refreshed when the IdP rotates keys. The audience claim must match the configured client ID. The issuer must match the configured expected issuer. The expiry must be checked with a bounded clock-skew tolerance. The nonce in the ID token must match the nonce AAM sent in the authorization request — and if it doesn't, that's a replay attempt, not a parsing error.
The OAuth 2.0 layer underneath has its own footguns. State must be CSRF-bound to the user's session and have a short TTL. PKCE must be used (S256 ideally) so an intercepted authorization code cannot be redeemed by an attacker. Mixup attacks — where an attacker tricks the relying party into talking to the wrong IdP — are defeated by binding the callback to a specific IdP and validating issuer accordingly. None of these defenses are automatic in a naïve SDK integration.
The other failure mode is embedding the OIDC SDK directly into every application. Each app then carries its own copy of the trust decisions, the JWKS cache, the audit logging, and the IdP configuration. A single IdP change becomes a coordinated multi-application rollout. MFA, conditional access, posture, and logout behaviour end up re-solved per application, often inconsistently.
OIDC belongs at the access edge — verified once, correctly, in one place that already runs MFA, conditional access, posture, and Backend SSO. The application stays out of the federation protocol and receives a clean, validated identity instead.
How we approach it
One AAM gateway terminates OIDC correctly at the edge; the rest of the access engine composes on top.
Full OpenID Connect relying party plus OAuth 2.0 client
Authorization code flow with PKCE, ID token signature verification via JWKS, nonce-bound replay defense, state-bound CSRF defense, audience/issuer/validity enforcement. The same engine speaks plain OAuth 2.0 for IdPs that don't expose an ID token, so token-only providers and full OIDC providers share one codebase.
Per-application and per-tenant IdP routing
One AAM gateway can route different applications to different IdPs, and different tenants of the same application to different IdPs, at the same time. IdP selection happens per request from the application or tenant context — no separate gateway per IdP, no manual chooser for the user.
Replay, CSRF, and mixup attacks defeated by default
Nonce binds the ID token to the authorization request that asked for it; state binds the callback to the user's session; PKCE binds the code redemption to the original public client. None of these are optional flags that an integrator can forget — they are the default flow.
Composed with MFA, conditional access, posture, and Backend SSO
An OIDC authentication does not stand alone — it composes with edge MFA (if the IdP didn't enforce step-up itself), with conditional access predicates, with continuous trust evaluation, and with Backend SSO injection toward the downstream app. The ID token becomes one input into the AAM session, not the whole session.
Capabilities
Standards-correct OIDC relying party plus the operational features that make federation safe and manageable at scale.
Authorization code flow with PKCE (S256 default)
AAM initiates the OAuth 2.0 authorization code flow with PKCE enabled by default — code_challenge_method=S256, a fresh code_verifier per request, never reused. An intercepted authorization code cannot be exchanged for tokens by an attacker that didn't generate the verifier. Plain PKCE remains available for IdPs that demand it; S256 is the configured default.
ID token signature verification against the IdP's JWKS
On callback, AAM fetches the IdP's JWKS, selects the signing key by the ID token's kid header, and verifies the ID token's signature with the algorithm declared in the header (RS256 and the rest of the standard family). A cache miss on kid triggers an immediate JWKS refresh so an IdP key rotation does not stall valid logins.
Audience, issuer, validity, and nonce enforced — not just parsed
The ID token's audience claim must include the configured client ID, the issuer must match the configured expected issuer, the validity window (exp/nbf) is enforced with a bounded clock-skew tolerance, and the nonce must match the nonce AAM sent in the authorization request. A nonce mismatch is treated as a replay-attack signal with its own audit event, not as a parsing error.
Shared JWKS cache with on-miss refresh for key rotation
JWKS responses are cached in a shared store across worker processes and gateway instances with a 1-hour TTL. A cache hit avoids the network round-trip on every login; a cache miss on the requested kid triggers an immediate refresh from the JWKS URI so a routine IdP key rotation does not produce a login outage.
OIDC parameters: scope, display, max_age, ui_locales, acr_values
Standard OIDC authorization parameters are first-class configuration: scope (openid auto-added), display for the IdP login UX, max_age for forced re-authentication, ui_locales for localised IdP pages, and acr_values for requesting a specific authentication context class — useful for asking the IdP to enforce step-up MFA.
Built-in provider profiles plus fully custom IdPs
Built-in profiles ship with sensible defaults for common providers (well-known endpoints, JWKS URIs, scope conventions). A custom-IdP path remains for any standards-compliant OIDC or OAuth 2.0 provider that needs endpoints, scopes, and mappings supplied explicitly.
ID token claims merged with userinfo, signed claims taking precedence
After signature verification, the ID token's claims are merged with the response from the IdP's userinfo endpoint. Signed ID token claims take precedence over unsigned userinfo fields where they overlap, so an attacker who tampered with a userinfo response cannot quietly override a signed identity claim.
Operational depth
The mechanics that keep an OIDC federation safe, current, and observable.
State binding with 10-minute TTL and session check
The OAuth state parameter is stored against the user's session with a 10-minute TTL. On callback, AAM verifies the state belongs to the same session that initiated the flow — an attacker who replays a state value into another user's browser is rejected with an OAUTH_STATE_MISMATCH audit event.
Audit on every ID token verification skip — by leaf cause
When ID token signature verification is skipped for any operational reason — JWKS unavailable, no matching kid, malformed JWK, missing JWKS URI — a dedicated audit event records the specific leaf cause. Operators can distinguish a transient JWKS outage from a misconfiguration from an unsupported key type without re-reading runtime logs.
Clock-skew tolerance bounded per IdP
ID tokens carry iat, nbf, and exp timestamps. Real clocks drift; AAM applies a bounded clock-skew tolerance (60 seconds by default for the underlying JWT verifier) so small drift does not reject valid logins while large drift — which indicates misconfiguration or replay — still rejects.
Token exchange and userinfo with hardened network timeouts
Token exchange and userinfo requests against the IdP use bounded connect, DNS, and overall timeouts so a slow or unresponsive IdP cannot tie up gateway workers. Token exchange runs on a 30-second overall budget; userinfo runs on a 15-second budget; both have shorter connect and DNS budgets so failures fail fast.
Per-event audit with correlation back to the AAM session
Every step of the OIDC flow — flow start, callback success, token exchange (with which tokens were returned), ID token signature verification outcome, JWKS fetch outcome — is recorded with a correlation ID that ties back to the AAM session and the downstream backend(s). Forensic reconstruction of "who signed in via which IdP and when" is a single-query operation.
Where teams use it
Modern enterprise IdPs (Entra ID, Okta, Auth0, Keycloak)
Existing enterprise IdPs that already authenticate the workforce over OIDC. AAM plugs in as a relying party without asking the IdP team to change anything. New applications join the federation by adding an entry to AAM, not by adding an OIDC SDK to each codebase.
Workforce social IdPs (Google Workspace, GitHub)
Internal tooling that authenticates against the organisation's Google Workspace tenant, or developer tooling that uses GitHub as the IdP. AAM speaks OIDC to Google and OAuth 2.0 to GitHub from the same engine, mapping each into the canonical AAM session shape.
Multi-tenant SaaS with per-tenant OIDC IdPs
SaaS applications where each customer brings their own OIDC IdP. One AAM gateway fronts all tenants; each tenant's traffic routes to that tenant's IdP. Adding a tenant is a configuration change in the IdP registry, not a deploy.
Edge MFA and conditional access on top of an OIDC IdP
Some IdPs authenticate but don't enforce step-up MFA or conditional access for every application equally. AAM accepts the IdP's authentication, then composes its own MFA, conditional access predicates, and continuous trust evaluation on top before granting access to the application.
Common questions
Which IdPs does AAM federate with over OIDC and OAuth 2.0?
How does AAM defend against ID token replay, CSRF, and mixup attacks?
What happens when the IdP rotates signing keys?
What's the difference between OIDC and plain OAuth 2.0 in this page?
What happens to the identity after AAM consumes the tokens?
OIDC done correctly, at the edge
We'll connect AAM to your OIDC IdP — enterprise, cloud, or self-hosted — and let the rest of the access engine compose MFA, conditional access, posture, and Backend SSO on top of the verified ID token.