Identity, Authentication, and Access Management Questions
Designing and operating identity and access control systems. Covers authentication protocols and standards (OAuth, SAML, OIDC, MFA), authorization models (RBAC, ABAC), identity lifecycle and privilege management, IAM architecture and automation, and access control across cloud and on-premises environments. The 'who can do what' control plane, distinct from cryptographic key management.
Compare OAuth 2.0, OpenID Connect (OIDC), and SAML for solving authentication and authorization problems. For each protocol explain primary use cases (e.g., web SSO, mobile apps, enterprise federation), how authentication statements are conveyed, and typical deployment considerations (mobile vs enterprise SSO). Provide criteria you would use to choose one protocol over the others.
Sample Answer
Direct answer
OAuth 2.0 is an authorization framework: it lets a user grant a third-party application scoped access to an API or resource without handing over a password. On its own it has no standard concept of "who logged in," only "what access was granted." OpenID Connect (OIDC) is a thin identity layer built on top of OAuth 2.0 that adds a standardized token proving who authenticated, not just what the app can now touch. SAML (Security Assertion Markup Language) is an older, XML-based protocol built specifically for browser-based single sign-on (SSO), most often used to federate identity into enterprise web applications. All three answer "who is this, and can we trust that claim across a network boundary," but they target different client types and eras of the web.
Structured elaboration
| OAuth 2.0 | OIDC | SAML | |
|---|---|---|---|
| Primary use case | Delegated authorization: "let this app read my calendar" | Web and mobile login (SSO): "let this app know who I am" | Enterprise SSO: federating identity into an organization's web apps |
| How the trust statement is conveyed | An access token authorizes API calls; the token itself does not certify who authenticated | A signed ID token (a JSON Web Token, or JWT) carrying claims such as sub, iss, aud, exp, and the time of authentication | An XML assertion containing an authentication statement, signed by the identity provider and posted to the application via a browser redirect |
| Typical deployment fit | Any client needing scoped API access: mobile apps, single-page apps, machine-to-machine calls | The modern default for new consumer and enterprise login integrations | Legacy and regulated enterprise software, where the identity provider is often an on-prem or cloud directory (for example Active Directory Federation Services, or an identity provider like Okta configured for SAML) that the buyer already standardized on |
Criteria for choosing between them:
- Building a login experience for a modern web or mobile app that also needs to call an API on the user's behalf: use OIDC. It sits on top of OAuth 2.0, so you get delegated authorization and a verified identity from the same flow.
- Only need delegated API access, with no identity concept for the calling app itself (a backend job reading a user's calendar): plain OAuth 2.0 is sufficient and simpler.
- Integrating with an enterprise's existing identity provider, and that provider or the target application only speaks SAML: you use SAML even though it is heavier to implement than a JWT-based approach, because the counterpart has no OIDC endpoint to talk to.
- Mobile deployment pushes the decision toward OIDC: SAML's browser-redirect-and-XML-post pattern is awkward inside a native app, while OIDC's authorization code flow was purpose-built for exactly that client type.
- Enterprise SSO deployment sometimes forces SAML regardless of preference: large enterprise buyers frequently standardize on a SAML identity provider for audit and compliance reasons, and the vendor's application may expose only a SAML integration point.
Worked example
Trace "Log in with Google," which demonstrates both the OAuth delegation layer and the OIDC identity layer built on top of it:
- The user clicks "Log in with Google"; the app redirects to Google's authorization endpoint requesting
scope=openid email profile. - Google authenticates the user through its own login screen and the user consents to the requested scopes.
- Google redirects back to the app with a short-lived authorization code.
- The app exchanges that code at Google's token endpoint for an ID token (the OIDC-specific artifact, a JWT) and an access token (the underlying OAuth artifact).
- The app validates the ID token's signature and claims (
issequals Google's issuer,audequals the app's own client id,exphas not passed) to learn who logged in, from thesubandemailclaims. - If the app also wants to read the user's Google Calendar, it uses the separate access token for that call. That is the original OAuth layer doing its job, distinct from step 5.
This split is exactly why using bare OAuth to implement login was a historical mistake, before OIDC existed: an access token alone does not certify identity (it authorizes calls to a specific API, and it isn't required to be a verifiable, self-contained token at all), so an app inspecting only an access token to decide "who is logged in" could be fooled by a token that was legitimately issued, just for a different purpose or audience. OIDC's ID token exists specifically to close that gap.
Trade-offs and pitfalls
- SAML assertions are XML-based and require careful canonicalization and signature validation. Implementing that by hand is a well-known source of signature-wrapping vulnerabilities; always use a maintained library rather than parsing and verifying the XML yourself.
- Treating an OAuth access token as proof of identity, instead of using OIDC's ID token, is the single most common protocol-selection mistake in this space; it works in testing and fails once a token issued for a different audience gets presented to the wrong service.
- Bridging protocols (a SAML-only enterprise identity provider fronting an OIDC-only application, or the reverse) is a common real integration need, but it adds an extra hop and an extra trust boundary; treat that bridge as its own design problem rather than assuming one protocol trivially substitutes for the other.
You are the lead security engineer responsible for migrating 200 services from local username/password auth to OIDC SSO. Draft a phased migration plan covering discovery of affected systems, gating criteria for rollout phases, developer onboarding, handling legacy clients, session migration, rollback strategy, and metrics/KPIs to determine successful migration.
Sample Answer
Direct answer
A 200-service local-auth-to-OIDC (OpenID Connect) migration succeeds or fails on sequencing and gating, not on the OIDC integration itself, which is well understood. The plan starts with an automated discovery pass to build the actual inventory, migrates in cohorts gated by objective criteria rather than a calendar date, and treats legacy clients that genuinely cannot be touched as a permanent, owned exception category with a compensating control, not a blocker to the other 199 services. Rollback must be a rehearsed, per-cohort procedure, not a whole-program undo, and the metrics that actually indicate success track authentication failure rate and support-ticket volume per cohort, not "percentage of services migrated" alone, since a service can be technically migrated and still be failing silently for real users.
Structured elaboration
Discovery of affected systems
- An accurate inventory of 200 services rarely already exists. Build it by combining an automated scan (searching each service's codebase and configuration for local-auth library usage, or a local users/passwords database table) with a manual attestation pass where each service owner confirms or corrects the automated finding, since automated scans reliably miss non-standard implementations and manual-only surveys reliably miss services nobody remembers still exist.
- Classify each discovered service by migration complexity, not just "does it use local auth": does it have an active maintaining team, does it fit the standard web-request auth pattern the new OIDC integration expects, does it have exotic requirements (a legacy client-credential grant that doesn't map cleanly to OIDC's user-facing flows). This classification is exactly what the rollout-phase gating below sequences on.
Gating criteria for rollout phases
- Sequence cohorts by complexity and blast radius, not alphabetically or by convenience: phase 1 is a small number of low-risk, actively maintained, standard-pattern services, validating the integration itself against real production traffic at low stakes; phase 2 is the bulk of standard-pattern services; a final phase handles the exotic and legacy cases identified during discovery.
- Gate advancement to the next phase on objective criteria measured from the previous phase, not a calendar date: authentication error rate for migrated services staying within an agreed band of the pre-migration baseline, support-ticket volume attributable to auth not exceeding an agreed threshold, and zero unresolved rollback incidents from the current phase, all before the next cohort begins.
Developer onboarding
- Provide each service team a reference implementation, a working, minimal example of the OIDC integration pattern in the organization's dominant language or framework, and a self-service migration checklist, rather than requiring the central team to hand-hold all 200 migrations; the central team's actual job is reviewing and unblocking, not implementing each one.
- Run office hours or a dedicated support channel scoped to each phase specifically, so a team hitting an integration problem gets fast help exactly when they need it, during their own migration window, not a generic, perpetually available channel that's easy to deprioritize.
Handling legacy clients
- A client that genuinely cannot be modified, an unmaintained service, a third-party appliance with hardcoded local-auth expectations, needs a documented, permanent exception category with a compensating control, for example a protocol-translation proxy that accepts OIDC tokens externally and translates to whatever the legacy client's internal auth expects, isolating the limitation behind a boundary the rest of the migration doesn't have to work around.
- Track exceptions explicitly as a named, owned risk with a periodic re-review, not silently left as "still local auth, will get to it eventually", since an unowned exception is how a supposedly temporary gap becomes a permanent, unmonitored one.
Session migration
- For a service transitioning from local sessions to OIDC-issued tokens, support both simultaneously for a transition window: an already-logged-in user's local session keeps working until it naturally expires, while a fresh login goes through OIDC, rather than forcibly terminating every existing session the moment a service cuts over, which would force a mass simultaneous re-authentication event across the affected user population.
- Communicate the transition window's own expiry to affected users in advance, so the eventual forced re-authentication, once local sessions are fully retired, is expected rather than a surprise support spike.
Rollback strategy
- Rollback must be scoped and rehearsed per cohort, not as a whole-program undo: each phase's migration should be revertible independently, for example feature-flagging the auth path per service rather than a single global switch, so a problem discovered in phase 2 doesn't require unwinding phase 1's already-stable services.
- Rehearse the rollback procedure itself before it's needed, a planned drill on a non-production or canary service, since a rollback procedure that's only ever existed on paper is the procedure most likely to fail under the actual pressure of a real incident.
Metrics and KPIs for successful migration
- Track per cohort, not just in aggregate: authentication failure rate against baseline, support-ticket volume attributable to auth, time-to-resolution for auth-related incidents during the transition window, and the percentage of a cohort's user sessions that have naturally transitioned to OIDC, not just "service marked as migrated" in a tracker.
- Treat "percentage of services migrated" as a status metric, not a success metric; a service can be technically cut over and still failing silently for a subset of its users, a legacy client that never got the exception treatment it needed. The real success signal is the auth-failure and support-ticket metrics staying within their agreed bands, not a checklist being checked off.
Worked example
Concrete cohort sizing and a gating-criteria illustration for 200 services. The specific numbers below are illustrative POLICY CHOICES a program would set and tune for its own risk tolerance, not measured outcomes:
- Phase 0 (discovery): touches all 200 services, changes nothing in production.
- Phase 1 (pilot): a deliberately small cohort, for example 10 services (5% of the fleet), chosen for low risk and an actively engaged owning team, validating the integration pattern itself against real traffic before wider rollout.
- Phase 2 (bulk): the majority of the remaining roughly 180 services that the complexity classification did not flag as exotic.
- Phase 3 (remainder): the exotic and legacy clients requiring the proxy-based exception pattern described above.
An example gate from Phase 1 to Phase 2: authentication error rate for the pilot cohort must stay within an agreed multiple of its pre-migration baseline (for instance, no more than 1.5 times baseline) sustained for a full week, and there must be zero unresolved rollback incidents from the pilot. The discipline that matters is having an explicit, objective threshold at all, evaluated against real pilot data before advancing, not any particular multiple or duration; an organization with a lower risk tolerance would simply set a stricter number and hold the same discipline.
Trade-offs and pitfalls
- A purely calendar-driven rollout, finish by end of quarter, creates pressure to advance cohorts before the previous phase's own gating criteria genuinely clear, exactly how a migration ships a phase 2 problem that phase 1's own gate should have caught. The gating criteria only work if the organization is willing to actually hold a phase when its own thresholds aren't met, not treat them as advisory.
- Supporting local sessions and OIDC simultaneously during the transition window is the correct way to avoid a mass forced re-authentication, but it means running two authentication code paths in parallel for a period, real complexity and a real, if temporary, expanded attack surface that must have its own defined end date, not linger indefinitely because retiring the old path never gets prioritized once the new one works.
- Treating unmaintained legacy clients as a permanent exception category is honest, but a compensating-control proxy in front of one is itself new infrastructure someone now owns; if that ownership isn't assigned explicitly, the proxy becomes exactly the kind of unowned, unmonitored gap the original local-auth service was.
- "Percentage of services migrated" is an easy, visible metric to report upward, which is exactly why it's tempting to over-index on it even though it says almost nothing about whether the migration is succeeding for real users; report it alongside the auth-failure and ticket-volume metrics, never in place of them.
Describe proactive and reactive methods to detect and prevent privilege escalation across accounts and services. Include design-time strategies (least privilege, separation of duties, permission boundaries), runtime detection signals, monitoring heuristics, automated guardrails, and testing approaches such as red-teaming and entitlement scanning.
Sample Answer
Direct answer
Preventing privilege escalation takes two complementary layers, not one. Design-time controls (least privilege, separation of duties, permission boundaries) shrink what any identity could ever escalate into, before a single request is evaluated. Runtime detection, backed by monitoring heuristics and automated guardrails, watches for the signs that an identity is actively trying to gain access it was not designed to have, and reacts faster than a human analyst can. Testing through red-teaming and entitlement scanning closes the loop, because permission drift and gaps between intended and actual access are the normal state of any system of real size, not an occasional exception.
Structured elaboration
Design-time strategies. Least privilege grants only what a role currently needs and defaults to deny for everything else; an over-privileged identity is the raw material nearly every escalation technique depends on, so shrinking it is the single highest-leverage control. Separation of duties (SoD) structurally prevents one identity from combining two permissions that together create an escalation primitive, most classically the ability to both define a new role or policy and assign roles to identities: combined, that pair lets an identity create something more powerful and then grant it to itself. Permission boundaries are an outer limit on what a role can ever be granted, independent of what the role's own attached policy says; even if a compromised process manages to attach a broader permission to itself, the boundary caps the effective permission at the intersection of the role's own policy and the boundary, so a successful self-grant still lands well short of what was attempted.
Runtime detection signals. An actual escalation attempt tends to look like one or more of: an identity gaining a permission it did not have before, outside any documented change process; an identity assigning a role or policy to itself or to another identity it does not normally manage; a tight, unusual sequence such as create-role followed quickly by attaching a highly privileged policy followed by assuming that same role, especially by an identity that has never performed any of those three actions before; a low-privilege identity suddenly invoking a highly privileged action it has no history of calling; or authentication from an unfamiliar device or location immediately followed by a permission-elevating action.
Monitoring heuristics. Turn those signals into rules by building a rolling behavioral baseline per identity rather than one global threshold, since a rare admin call might be entirely ordinary for one service account and alarming for another. Track entitlement deltas: diff each identity's effective permission set against what it had some days earlier, and flag growth with no matching approved change. Model identities, roles, and permissions as a graph and specifically watch for new edges that create a path from a low-trust identity to a high-value resource, which catches an emerging escalation path even when no single step along it looks suspicious in isolation.
Automated guardrails. Detection that only alerts a human is a race the attacker can win on latency alone. High-risk, well-defined action combinations (create-role immediately followed by attaching an administrator-equivalent policy) should be denied or require pre-approval automatically, encoded as an organization-wide policy that holds regardless of what any individual identity's own attached policy allows, the same structural idea as a permission bounder applied at the organization level rather than the role level. For the rarest, highest-confidence signals, automatically and temporarily quarantining the identity (suspending its credentials pending human review) trades a brief operational cost if the signal turns out to be a false positive against letting an active compromise continue; noisier or more ambiguous signals should alert a human instead of act automatically, since an overly aggressive automated response becomes its own denial-of-service risk against legitimate work.
Testing approaches: entitlement scanning and red-teaming. Entitlement scanning is a continuous, automated audit that computes each identity's true effective permissions, the union across every role, group, resource policy, and boundary that could apply, not just what is directly attached, and checks that union against policy. This computation is genuinely nontrivial, because permissions routinely combine across several artifacts and reading any single one of them undercounts what is actually possible. Red-teaming is a dedicated team (or a periodic contracted exercise in a smaller organization) actually attempting privilege escalation against the real environment with the same techniques a real attacker would use. The distinction matters: scanning finds static gaps, a permission that should not exist; red-teaming finds process gaps, a permission or path that looks fine in a static review but escalates in practice because of an interaction the scan did not model, or because the runtime detection meant to catch it did not actually fire.
Worked example
A cloud service account, ci-deploy-bot, is scoped to deploy application code to a specific set of resources and should never be able to create roles or attach administrator-equivalent policies.
Design time: ci-deploy-bot's own attached policy grants only deploy-specific actions, and separately, an organization-level permission boundary caps every service account in this environment from ever attaching an administrator-equivalent policy to any role, regardless of what any individual account's own policy allows.
An attacker compromises ci-deploy-bot's credentials (leaked in a build log) and attempts a classic escalation chain: create a role named temp-admin, attach an administrator-equivalent policy to it, then assume that role.
Runtime detection: both the create-role and attach-policy calls are actions ci-deploy-bot has never invoked in its recorded history, a deviation from its established baseline, and the two calls happen four seconds apart, a rapid and unusual sequence. The combination of "never-before-seen sensitive action" and "rapid create-role-then-attach-admin-policy sequence" trips a high-confidence rule.
Automated guardrail: because this specific pattern is both rare and severe, the system automatically suspends ci-deploy-bot's credentials and blocks the pending assume-role call rather than only alerting, and pages a human for review; the credential is already contained by the time anyone looks at the alert.
Even if that detection had somehow failed to fire, the organization-level permission boundary from the design-time step independently caps whatever role ci-deploy-bot could create: temp-admin would not actually receive administrator-equivalent capability no matter what policy was attached to it, a backstop that does not depend on detection working at all.
A weekly entitlement scan run before this incident had already flagged that ci-deploy-bot retained an unused role-creation permission left over from an earlier project, one that policy said should have been removed. That stale grant is exactly what made the attempted escalation possible in the first place, illustrating that a scan finding only has value once it is actually remediated, not merely generated. A red-team exercise the previous quarter had specifically tested this same create-role-then-attach-admin path against a comparable service account and confirmed the detection rule fired within the expected time, which is why the response above worked as designed rather than being untested theory.
Trade-offs and pitfalls
- Runtime detection with no design-time boundary turns every escalation attempt into a pure race against detection latency. A permission boundary is a structural backstop that still holds even when detection is slow, silent, or simply blind to a new technique, which is why design-time controls are not optional even in a heavily-monitored environment.
- Automated guardrails calibrated too aggressively create their own denial-of-service risk. Teams burned by frequent false positives tend to route around the control by requesting broad standing exceptions, which quietly undoes the protection; the automated-action threshold should be reserved for signals that are both rare and severe, with everything noisier routed to a human.
- Entitlement scanning that reads only directly-attached permissions, not the full effective set across roles, groups, and boundaries, systematically undercounts real risk, because escalation paths are routinely combinatorial and no single artifact looks dangerous on its own.
- Red-teaming a generic, textbook list of escalation techniques instead of the organization's own actual role and policy graph produces false confidence. The value is in testing the specific structure that exists, not a checklist that happens to exist elsewhere.
- Design-time and runtime controls are complements, not substitutes for each other, and scanning and red-teaming exist to verify that stays true over time. Least privilege reduces the surface, monitoring and guardrails catch what still gets through, and testing confirms both are still working as the environment inevitably drifts; skipping any one layer leaves a specific, predictable gap none of the others were built to cover.
Design an end-to-end authentication and authorization architecture for a SaaS platform with 10M users that supports web SPAs, server-rendered pages, mobile apps, third-party APIs, SSO via SAML and OIDC, social login providers, and a microservices backend. Specify token flows for user and service identities, session management choices, refresh strategies, token revocation, rate limiting, certificate/key rotation, identity provider integration patterns, and recovery plans for a major credential compromise.
Sample Answer
Direct answer
At 10 million users across web, mobile, and server-rendered surfaces, plus enterprise federation and social login, the architecture needs one identity provider (IdP) that every client type talks to via standard protocols, OpenID Connect (OIDC) for the consumer-facing clients, OIDC or Security Assertion Markup Language (SAML) for enterprise single sign-on (SSO), and delegated social login as just another federated identity source into the same IdP, fronted by an API gateway that terminates authentication once and issues short-lived access tokens the microservices backend trusts without each service re-implementing login. User sessions and service-to-service calls use deliberately different token strategies, short-lived, frequently refreshed tokens for users, workload-scoped credentials for services, because they have different renewal patterns and different blast radii if compromised, and the whole design assumes a credential will eventually be compromised at this scale, so revocation and a rehearsed recovery plan are first-class design requirements, not an afterthought.
Structured elaboration
Client-to-IdP token flows (web single-page apps, mobile apps, server-rendered pages). Web single-page apps (SPAs) and mobile apps both use the OIDC authorization-code flow with Proof Key for Code Exchange (PKCE, a mechanism that prevents a stolen authorization code from being redeemed by anything other than the app that requested it), since both are public clients that cannot safely hold a client secret. Server-rendered pages, which run on a server that can hold a secret, use the standard OIDC authorization-code flow as a confidential client, with the session for the browser handled server-side rather than exposing a token to client-side code at all.
Social login providers. Treated as additional federated identity sources into the same IdP: the IdP acts as a broker, federating to each social provider via that provider's own OIDC or OAuth support, then issuing the platform's own token to the client, so downstream services only ever see one token format regardless of whether the user originally signed in with a password, a social account, or enterprise SSO.
Enterprise SSO via SAML and OIDC. For business customers whose employees need to sign in via their own corporate identity provider, the platform's IdP supports both SAML and OIDC as inbound federation protocols, since many enterprise identity providers still only offer SAML, converting either into the platform's own OIDC-based token before anything reaches the microservices backend, so the backend has exactly one token format to validate regardless of how the user actually authenticated.
Token flows for service identities, distinct from user identities. Service-to-service calls inside the microservices backend, and calls the backend itself makes to third-party APIs, do not reuse a user's token. Each service has its own workload identity, and inter-service calls use short-lived, machine-issued credentials, an OAuth client-credentials grant scoped narrowly to the specific downstream service and action, or an internally-issued service token, obtained through the platform's own workload-identity mechanism, not a shared static API key.
Session management choices. For the web SPA and mobile app, the "session" is really the token's lifetime: a short-lived access token, minutes, plus a longer-lived refresh token, with the refresh token held in a secure, non-JavaScript-accessible location, an httpOnly, Secure, SameSite cookie for web, the platform keystore for mobile, rather than in memory accessible to any script. For the server-rendered app, the session is a traditional server-side session, a session ID in an httpOnly cookie, with the underlying tokens kept server-side and never sent to the browser at all, trading a small amount of server-side state for eliminating an entire class of token-theft-via-browser risk.
Refresh strategies. Refresh tokens rotate on every use: each time a refresh token is redeemed for a new access token, the old refresh token is invalidated and a new one issued. If a refresh token is ever redeemed twice, the old one used again after rotation, that's a strong signal of theft, since a legitimate client would never reuse an already-rotated token, and the response is to revoke the entire token family for that session immediately, not just deny the one request.
Token revocation. Because access tokens are short-lived JSON Web Tokens (JWTs) validated locally by each service, no database call needed to check them, which is what makes the system scale to 10 million users' worth of request volume without every request hitting a central session store, revoking a still-valid access token requires either waiting out its short lifetime, the usual path, since lifetimes are kept intentionally short for exactly this reason, or, for the urgent case, maintaining a small, fast-to-check denylist a gateway consults for the rare immediate-revoke case, such as a confirmed account compromise, rather than trying to make ordinary revocation instant for every token, which would defeat the purpose of using stateless tokens in the first place. Refresh tokens, by contrast, are tracked server-side, they have to be, to support rotation and reuse detection, so revoking a refresh token, and therefore a user's ability to get any new access token, is immediate.
Rate limiting. Enforced at the API gateway, keyed on the authenticated identity, not just source IP address, since mobile and corporate networks legitimately share IPs across many users, for normal traffic, with a separate, much stricter limit on the authentication endpoints themselves, login, token refresh, password reset, keyed on both identity and IP, since those endpoints are the ones an attacker would hammer during a credential-stuffing attempt.
Certificate and key rotation. The IdP's token-signing key rotates on a schedule, with the OIDC discovery document publishing both the current and the immediately-previous signing key during the overlap window, so already-issued, still-valid tokens signed with the old key continue to validate while every new token uses the new key, meaning rotation never requires an instant, all-at-once cutover. Any certificate used for mutual TLS between the gateway and internal services follows the same overlap-window pattern, issued by an internal certificate authority with short-lived certificates rather than long-lived ones that would make compromise-driven rotation slow and disruptive.
Identity provider integration patterns. The IdP is the single place that understands how a user actually authenticated, password, social, enterprise SAML or OIDC; everything downstream of the gateway only ever sees the platform's own normalized token, which is what lets new login methods, a new social provider, a new enterprise customer's SAML setup, be added without touching a single microservice.
Recovery plan for a major credential compromise. First, revoke the affected refresh tokens, and for a widespread compromise, rotate the IdP's signing key immediately rather than waiting for the scheduled rotation, accepting that this invalidates every access token platform-wide and forces re-authentication, a deliberate trade of short-term disruption for closing the exposure. Second, force-expire sessions for the affected population by adding them to the fast-path denylist so already-issued access tokens stop working immediately rather than waiting out their normal lifetime. Third, require a password reset, and re-enrollment of MFA if applicable, for affected accounts before they can obtain a new token. Fourth, communicate to affected users and, if the scale or nature of the compromise requires it, to regulators, on a timeline set by legal and compliance rather than engineering.
Worked example
flowchart TD
SPA[Web single-page app] -->|OIDC auth code plus PKCE| IdP[Identity provider]
Mobile[Mobile app] -->|OIDC auth code plus PKCE| IdP
SSR[Server-rendered app] -->|OIDC auth code| IdP
Social[Social login providers] -->|federated identity| IdP
Enterprise[Enterprise SSO via SAML or OIDC] -->|federation| IdP
IdP -->|issues tokens| Gateway[API gateway]
Gateway -->|validates access token| Services[Microservices backend]
Services -->|client-credentials tokens| ThirdParty[Third-party API integrations]
Gateway -->|enforces rate limiting| Services
- A user opens the mobile app, which starts an OIDC authorization-code-with-PKCE flow against the platform's IdP.
- The IdP authenticates the user, password, or federates to a social provider, or, for a business account, federates to the customer's own SAML or OIDC identity provider, and issues a short-lived access token plus a refresh token; the mobile app stores the refresh token in the device's secure keystore.
- The mobile app calls the API gateway with the access token; the gateway validates the token's signature locally, no database lookup, checks the fast-path denylist for the rare revoked case, applies rate limiting keyed on the user's identity, and forwards the request to the appropriate microservice.
- That microservice, fulfilling the request, needs data from a second internal service; it presents its own workload credential, not the user's token, to make that call, scoped only to what that specific service-to-service interaction requires.
- When the access token nears expiry, the app silently redeems the refresh token for a new access token; the old refresh token is invalidated and a new one issued, rotation, and if that same old refresh token is ever presented again afterward, the gateway treats it as theft and revokes the whole session family.
Trade-offs and pitfalls
Stateless, locally-validated access tokens are what make the system scale to 10 million users without a central session-lookup bottleneck, but that scalability is exactly what makes instant revocation of an individual access token hard; the fast-path denylist is a deliberate, narrow exception, not a general revocation mechanism, and treating it as one, checking it on every request for every user, would reintroduce the central-lookup bottleneck the design was trying to avoid. Storing refresh tokens client-side in anything JavaScript can read, localStorage or a JS-accessible cookie, is a common shortcut that defeats the httpOnly-cookie or secure-keystore protection the design relies on; it's an easy mistake to make because it "works" in testing and only becomes a problem when a cross-site-scripting vulnerability elsewhere in the app gets exploited. Rotating the IdP's signing key without an overlap window, publishing only the new key, invalidates every still-valid token instantly, forcing every one of 10 million users to re-authenticate at once; that's the right call during an actual compromise but the wrong default behavior for routine, scheduled rotation, which is why routine rotation uses the overlap window and compromise response deliberately skips it. A recovery plan that only covers the technical revocation steps and skips the communication and legal-timeline steps is incomplete for an incident of this scale; at 10 million users, a credential-compromise event is very likely a regulatory and public-communication event as much as a technical one.
Design single sign-on (SSO) and single logout (SLO) across multiple web applications and multiple identity providers (SAML and OIDC). Explain front-channel vs back-channel logout mechanisms, how you'd correlate sessions across apps, and how to handle IdP unavailability or failure modes without leaving orphaned sessions.
Sample Answer
Direct answer
Correlate every application's local session to the identity assertion that created it via a single shared identifier, a Security Assertion Markup Language (SAML) NameID plus SessionIndex, or an OpenID Connect (OIDC) sid (session ID) claim, so a single logout event issued by either protocol's identity provider (IdP) can be mapped back to every application session it created, regardless of which protocol established it. Logout must propagate through BOTH front-channel (browser-mediated redirects or iframes, reaching sessions the user's current browser can still touch) and back-channel (server-to-server calls, reaching sessions a redirect chain won't, background jobs, other devices, closed tabs) mechanisms, and every session must carry an absolute maximum time-to-live (TTL) as a backstop, so an IdP outage during logout can orphan a session for, at most, a bounded window, never indefinitely.
Structured elaboration
Session correlation across apps
- The correlating identifier is protocol-specific: SAML federates identity via a NameID plus a SessionIndex, a per-IdP-session identifier included in the original assertion; OIDC federates it via the ID token's
sidclaim. Each application, on establishing its local session from either assertion type, must persist that correlating identifier alongside its own local session record. - A central session registry, keyed by the correlating identifier rather than any one app's own session id, maps "this IdP session" to "every local app session it created", exactly what a logout event needs to fan out to: the IdP issues one logout event naming one correlating identifier, and the registry resolves it to N app sessions to terminate.
Front-channel vs back-channel logout
- Front-channel logout: the IdP, or a gateway, redirects, or loads via a hidden iframe, the user's current browser through each app's own logout endpoint in sequence, SAML's
LogoutRequest/LogoutResponsevia the HTTP-Redirect or HTTP-POST binding, or OIDC's front-channel logout via each relying party's registered logout URI loaded in an iframe. This reaches any app whose session lives in the SAME browser session actively completing the logout. - Back-channel logout: the IdP calls each app's logout endpoint directly, server-to-server, independent of the user's browser, SAML's back-channel
LogoutRequestvia the SOAP binding, or OIDC's back-channel logout via a signed logout token posted to each relying party's back-channel logout endpoint. This is the ONLY mechanism that reaches sessions the browser-redirect chain cannot: a session on a different device, a session in a background tab the user never revisits, or a mobile app's session with no browser redirect surface at all. - A front-channel-only implementation is a common, incomplete-by-construction shipped bug: it looks correct in the common case, one browser, one active tab, and silently leaves every OTHER session, another device, a background tab, an app the browser navigated away from mid-chain, live and orphaned.
sequenceDiagram
participant U as User
participant App1 as App A
participant App2 as App B
participant IdP as Identity Provider
U->>App1: Click logout
App1->>IdP: Front-channel logout request
IdP->>App2: Back-channel logout token (server to server)
App2-->>IdP: Session invalidated ack
IdP-->>App1: Front-channel logout response
App1-->>U: Redirect to logged-out page
Handling IdP unavailability without orphaned sessions
- A back-channel logout call to an app that's temporarily unreachable must be retried, not fired-and-forgotten; queue it with backoff and track delivery per correlating-identifier-and-app pair, so a transient failure doesn't silently leave that one app's session alive indefinitely.
- Every session, independent of whether logout propagation ever successfully reaches it, must carry its own absolute maximum lifetime, a hard session TTL requiring re-authentication once elapsed regardless of activity. This is the backstop bounding the worst case: even a completely failed logout fan-out, every back-channel call permanently failing, leaves an orphaned session alive for at most that TTL, never forever.
- Run a periodic reconciliation job comparing the session registry's "logout events issued" log against "sessions actually confirmed terminated", and re-attempt delivery for any gap, so a retry queue that silently exhausted its attempts doesn't go unnoticed indefinitely.
- If the IdP ITSELF is unavailable, not just one app's back-channel endpoint, new logins for the affected identity fail clearly, but this does not, by itself, terminate EXISTING sessions; existing sessions rely on the TTL backstop above, not an emergency broad logout the down IdP couldn't issue anyway.
Worked example
A concrete failure trace: App B's back-channel logout endpoint is unreachable at the moment logout fires.
- The user logs out via App A. The IdP's front-channel redirect updates App A's own session immediately.
- The IdP's back-channel call to App B times out.
- A retry queue schedules redelivery with exponential backoff, attempts at 1, 2, and 4 seconds after the first failure.
- Cumulative retry window:
1 + 2 + 4 = 7 seconds. If all three attempts fail, the periodic reconciliation job, running on its own separate schedule, detects the "issued but not confirmed" gap and re-queues delivery. - Worst case, if reconciliation also cannot reach App B (a prolonged outage), App B's session survives only until its own absolute session TTL, for example 8 hours, elapses. That TTL, not the retry logic, is the actual upper bound on how long the orphaned session can live, and it holds regardless of how badly the redelivery path fails.
Trade-offs and pitfalls
- Correlating on a SAML SessionIndex or OIDC
sidonly works if every app actually persists it at session-creation time; a legacy app that stores only its OWN session id and discards the IdP's correlating identifier cannot be reached by any logout fan-out at all, no protocol-level fix helps if the correlating id was thrown away at login. - Back-channel logout requires each app to expose a network endpoint reachable by the IdP, not hidden behind the user's own browser session, a real infrastructure requirement, firewall rules, service discovery, that front-channel-only deployments never had to solve. Teams sometimes skip back-channel specifically because standing this up is real work, which is exactly how the front-channel-only gap above ships.
- A hard absolute session TTL as the ultimate backstop is a genuine trade-off against convenience, even a perfectly legitimate, continuously active session eventually forces re-authentication. The alternative, no absolute TTL at all, means a failed logout fan-out has no bound whatsoever, an orphaned session that lives forever, strictly worse for a control whose entire purpose is bounding exposure.
- Mixing SAML and OIDC IdPs on the same platform means the correlation layer has to normalize two structurally different logout wire formats, XML-based SAML requests and responses versus JSON/JWT-based OIDC logout tokens, into one internal event shape. Under-testing this normalization layer itself, rather than each protocol's logout independently, is where cross-protocol SSO/SLO bugs actually tend to live.
Unlock Full Question Bank
Get access to all Identity, Authentication, and Access Management interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.