At 10:00, a user clicks Log out. At 10:01, a copied access token still reads documents. In this hypothetical system, the interesting interview question is whether that behavior violates the security contract—and which component could have stopped it.

A staff engineer authentication interview becomes more useful when you follow identity through a complete lifecycle: login, API access, renewal, account disablement, and logout. The architecture below is an illustrative practice exercise, with proposed requirements and a rehearsal rubric; it does not represent any employer’s hiring process.

1. Establish the authentication contract

Start with a browser-based document application, one trusted identity provider, a server backend, and a document API. Ask who owns account recovery and multifactor authentication, whether mobile clients are in scope, and what “log out everywhere” must mean. For this exercise, the provider owns primary authentication, and account disablement must stop new protected requests within 60 seconds.

Keep three artifacts separate. OAuth defines access tokens as credentials for protected resources; a refresh token obtains replacement access tokens and is presented to the authorization server. OpenID Connect adds an ID token carrying claims about authentication for the client. Your application’s session represents its own continuing relationship with the browser.

Propose a backend that acts as the OpenID Connect client, retains OAuth credentials server-side, and gives the browser an opaque session cookie. Draw the browser, backend, identity provider, session store, and API separately. Mark where each credential travels. This makes the next design decision concrete: which requests require shared state, and which can succeed from a token alone?

2. Walk one login through the callback

Use the authorization code flow with PKCE. Explain the transaction in order, including what the backend remembers before the browser leaves:

  1. Start a bound transaction. Generate a fresh unpredictable state, a nonce, and a PKCE verifier. Store them in a short-lived server-side login transaction bound to the initiating browser. Keep the intended return location on an application allowlist.
  2. Send the authorization request. Redirect to the configured provider with response_type=code, scope=openid, the client identifier, registered redirect URI, state, nonce, and an S256 challenge derived from the verifier. RFC 7636 specifies the challenge and verifier exchange.
  3. Check the callback. Require a live transaction, compare state, and reject replay or a browser-binding mismatch. Redeem the code at the configured token endpoint over TLS with the verifier and the backend’s registered client authentication. The authorization code exchange keeps the token response on this server-to-server connection.
  4. Validate before creating a session. Verify the ID token’s signature using trusted issuer keys, exact issuer, audience containing this client’s identifier, expiry, returned nonce, and applicable authorized-party rules. These are distinct checks in OpenID Connect’s ID token validation rules. Consume the transaction and issue a fresh application session identifier.

Explain what PKCE actually binds

The challenge commits the transaction to a verifier that must later accompany code redemption; an intercepted code alone is insufficient. It is not a general defense for every browser request. RFC 9700 requires redirect-flow CSRF protection and permits relying on PKCE under specified conditions, including confirmed provider support. The state check above makes transaction correlation explicit. Cookie-authenticated application mutations need their own CSRF controls.

Also explain failure recovery. If the callback has no matching transaction, stop and offer a fresh login. If the token exchange times out after the provider consumed the code, do not assume another redemption will succeed. Treat uncertain outcomes as a reason to restart authentication through the established flow, rather than constructing a session from an unvalidated response.

3. Validate tokens for their actual recipients

“Verify the JWT” leaves too much unspecified. For this exercise, define separate validation policies for ID tokens at the backend and access tokens at the API. RFC 8725 calls for mutually exclusive validation rules where different JWT types might otherwise be confused.

Configure accepted algorithms and issuer-key relationships; do not let an untrusted header choose unrestricted cryptography or an arbitrary key-fetch URL. A kid selects among keys you already trust for that issuer. The JWT security guidance requires algorithm verification and warns about attacker-controlled lookup inputs. A valid signature from someone else’s key is insufficient.

If you adopt the RFC 9068 JWT access-token profile, the API checks its access-token type, exact expected issuer, its own resource identifier in aud, signature, and expiration. Reject alg=none. The audience for an ID token is the client; the access-token audience identifies the intended resource server. Do not substitute an ID token at the document API because both artifacts happen to be JWTs. Other access-token formats require their own documented validation contract.

Make key discovery an operational dependency

Begin discovery from an issuer your application has accepted through configuration. OpenID Connect Discovery publishes a signing-key location and requires the returned issuer to match the issuer used to retrieve the configuration. Reading an issuer URL from an unverified token does not establish trust in that issuer.

Propose a rotation runbook: publish replacement keys before using them, retain old verification keys while legitimate tokens remain valid, and bound refresh attempts for unknown key identifiers. Test rotation against cached metadata. If an unknown key remains unresolved, reject the token; make the user-facing error distinguish temporary verification trouble from a confirmed account lockout.

4. Bound browser sessions and renewal

For the proposed browser session, use a host-scoped cookie with Secure, HttpOnly, and an explicitly selected SameSite policy. Rotate the session identifier after login and enforce idle and absolute expiry server-side. OWASP’s session guidance explains these controls and their limits: HttpOnly prevents script from reading the cookie, but injected JavaScript can still make authenticated requests in the victim’s browser.

Choose SameSite behavior with the actual provider callback in mind, including whether it uses a top-level navigation or a cross-site POST. For state-changing application requests, propose a framework-supported CSRF token plus origin checks where appropriate. OWASP treats SameSite as defense in depth for most deployments; do not allow a convenient GET endpoint to become a way around your mutation protections.

Then trace renewal. If the provider issues refresh tokens, decide how the backend stores them, how long the application session may continue, and what happens when refresh fails. A still-open browser tab should not silently extend an absolute session deadline.

RFC 9700 describes refresh-token rotation: replacement invalidates the previous token while preserving relationship information for replay detection. For public clients, replay detection requires rotation or sender-constrained refresh tokens. Our backend is confidential; state that distinction instead of applying a public-client requirement indiscriminately.

For this design, serialize refresh per session across backend replicas and replace stored credentials atomically. Otherwise, two legitimate requests might redeem the same rotating token. Rehearse a lost refresh response: the provider may have advanced its state while your backend retained the old value. Follow the provider’s documented recovery semantics, and require a new login when the outcome cannot be resolved safely. An unlimited retry loop is not a renewal policy.

5. Turn revocation into a measurable budget

Separate local logout, provider logout, and loss of API access. Invalidate the application session server-side and expire its cookie. If the user requests provider logout, OpenID Connect RP-Initiated Logout defines a request to the provider’s logout endpoint, with mechanisms including an ID token hint and registered return URI. That request does not itself prove that every previously issued API credential has become unusable.

RFC 7009 explains the revocation tradeoff: a self-contained access token validated without contacting the issuer needs another interaction if immediate revocation is required. Short lifetimes provide a different bound. Revoking a refresh token can prevent future renewal, but an API doing only offline signature and expiry checks cannot infer that revocation from an unchanged token.

Alternatively, the API can ask an authenticated introspection endpoint for token status. RFC 7662 explicitly discusses introspection caching: caching reduces traffic while introducing a window in which revoked credentials can still be accepted. A response containing an expiration time must not be cached beyond it.

An illustrative disablement calculation

Suppose the API accepts locally validated access tokens with a five-minute lifetime and permits 30 seconds of expiration leeway. Assume accurate clocks apart from that allowance, immediate prevention of new issuance and refresh, and no additional status check. A token minted just before disablement could remain acceptable for approximately 300 + 30 = 330 seconds. That exceeds our proposed 60-second contract.

Now propose an authoritative account-status check whose disabled state reaches every serving region within 10 seconds. Each API caches an active result for at most 20 seconds, measures cache age reliably, and denies protected requests when it cannot refresh an expired result. Under those explicit assumptions, a region could cache one last active result just before the update arrives: 10 + 20 = 30 seconds is the modeled bound for new requests.

This calculation is a design target, not measured performance. It excludes work already admitted and long-lived streams; define cancellation or periodic revalidation separately. If a partition breaks the propagation bound, the guarantee breaks unless the design also limits how long a disconnected region may serve. Do not call a best-effort notification a hard revocation guarantee.

Choose the failure behavior with the requirement owner. For this exercise, deny new protected operations once status evidence expires, retain a usable sign-in error page, and alert on stale regional state. If availability requires continued access, explicitly renegotiate the disablement bound. A token format cannot settle that product decision.

6. Rehearse a 30-minute design conversation

Use this self-review schedule to practice an answer you can explain without prompts. The timings and criteria are illustrative preparation tools.

  • Minutes 0–4: State the clients, trusted provider, credential boundaries, and disablement requirement. Identify who owns recovery and stronger authentication.
  • Minutes 4–11: Walk the browser login through code redemption, token validation, and fresh session creation. Explain what state, nonce, and PKCE each contribute.
  • Minutes 11–17: Name each token’s recipient, the expected issuer and audience, key-discovery policy, cookie controls, and refresh ownership.
  • Minutes 17–25: Handle an unknown signing key, concurrent refresh, provider outage, and account disablement. Calculate the access window with your chosen assumptions.
  • Minutes 25–30: State unresolved tradeoffs and verification work. Describe what evidence would prove the disablement bound across regions.

Give yourself one point each for a complete login trace, distinct token-validation policies, an explicit session lifecycle, a quantified revocation model, and coherent failure handling. Use a missing point to select the next rehearsal. This five-point rubric is an author-created practice aid, with no claimed relationship to hiring outcomes.

Propose negative tests alongside the design: wrong issuer, wrong audience, replayed callback, unexpected token type, stale account-status evidence, and a lost refresh response. Ask a practice partner to change one assumption midway through. Your explanation should make clear which guarantee changes and which mechanism must change with it.

Finish by tracing the logged-out user’s next request. Identify the credential it presents, the component that rejects it, and the deadline by which every region must agree. That answer makes authentication an enforceable lifecycle rather than an unexplained login box on the diagram.

Practice the authentication follow-up

Use Interview Copilot to prepare a system design explanation, then rehearse how your answer changes when token theft, provider failure, or account disablement enters the scenario.

Create a free account