skip to content

A server-side relying party redirects a depot supervisor to the fleet operator's identity provider — what must it keep until the callback arrives?

level: juniorimportance: must knowfreq 72%

answer

  1. two requests, minutes apart
  2. the callback brings back very little
  3. state is the key, not the data
  4. random values cannot be recomputed
  5. read once, then delete

basics

~20 s

A relying party parks one short-lived record per sign-in attempt, holding the value it sent as state, the nonce, the code_verifier, which provider was used, and the page the user was heading to. The callback request itself brings back almost none of that.

solid answer

~40 s

An authorization-code login is two unrelated HTTP requests minutes apart, and the browser only brings back what the provider echoes: the authorization `code`, the `state` value you sent, and from some providers an `iss`. Everything else has to be waiting on your side. Keep it as one record per sign-in attempt, keyed by the value that travels in `state`: the `nonce` you generated, the `code_verifier` the token request will have to present, which provider the request went to, the destination the user was originally heading to, and a creation timestamp. The callback looks the record up, consumes it, exchanges the code, runs the checks the specification requires, and hands the resulting subject to your session layer. Give it a TTL in minutes and delete it on read.

code

http · 3 lines
http
GET /auth/callback?code=SplxlOBeZQQYbYS6WxSbIA&state=Qm9vdC1sb2FkZXItMTc HTTP/1.1
Host: fleet.example.com
Accept: text/html

go deeper

for a junior

Recall the list: the value sent as state, the nonce, the code_verifier, which provider, and where the user was heading. Say plainly that the callback brings back only a code and the state value.

for a middle

Explain why each item cannot be recovered at the callback — random values cannot be recomputed and context is not echoed — and walk the callback's order of work from lookup to handing a subject on.

for a senior

Show the operational side: a TTL measured in minutes, delete-on-read so a replayed callback finds nothing, and an expired record rendering a restart page rather than an error page.

for a principal

Frame the record as the whole state of a half-finished login. Where it is kept decides how the service may be deployed and what a failed sign-in costs, so it is an architectural choice, not a detail.

## An authorization-code login is two requests, not one A server-side relying party runs a federated login as two separate HTTP exchanges. In the first, the dashboard builds an authorization request and redirects the browser away to the fleet operator's identity provider. In the second — seconds or minutes later, after the supervisor has typed a password and possibly answered a second factor — the browser arrives back at the dashboard's callback endpoint. Nothing in HTTP connects the two. The callback is an ordinary request from a browser that has just been somewhere else. So "what must the server keep" is really "what does the callback fail to bring". ## What the browser actually brings back The provider echoes only what it is supposed to echo: - the authorization **`code`**, single-use and short-lived; - the **`state`** value your authorization request carried, returned unchanged; - from providers that implement it, an **`iss`** value naming which authorization server answered. That is the payload. The provider is not holding your application's memory for you, and the browser is not a place to keep it. Everything the second half of the login needs has to be waiting on your side, findable from the one value you know will come back. ## The record, as one object Hold it as a single record per sign-in attempt rather than as four loose values in four places — one write when the authorization request is built, one read-and-delete when the callback lands: - **the value you sent as `state`** — this is the record's key, not a field inside it; - **the `nonce`** you generated for the authorization request, kept so the ID-token check at the callback has something to compare against; what that comparison proves is the specification's subject, not yours; - **the `code_verifier`**, because the token request has to present the original random value whose derived `code_challenge` went out earlier, and nothing in the callback carries it; - **which provider the request went to**, the moment the dashboard offers more than one sign-in button; - **the destination** the supervisor was originally heading to, so the login lands them on the vehicle list they clicked rather than the home page; - **a creation timestamp**, so the record can expire on its own. Everything in that list is either random, and therefore unrecoverable, or contextual, and therefore unknowable from the callback. That is the test for what belongs in the record. ## What the callback endpoint does, in order 1. Read the `state` query parameter and look up the record by it. 2. If there is no record — absent, expired, already consumed — stop and render a page saying the sign-in expired, offering to start again. This is a normal outcome, not a server error. 3. Consume the record: delete it, atomically, before any further work, so a concurrent or replayed callback finds nothing. 4. Exchange the `code` at the token endpoint, presenting the `code_verifier` from the record. 5. Run the checks the specification requires on what comes back, using the `nonce` you kept. 6. Hand the resulting subject to your own session layer, issue your own session, and redirect to the destination from the record. The relying party's work ends at step 6. What the session layer then does with that subject — where the resulting credential rests in the browser, how long the session lives, whether a local account is created on first sight — is a separate concern with a separate owner. ## Lifetime and single use The record's TTL is **minutes, not hours**. It must be long enough for a realistic authentication at the provider, including a second factor and the occasional password reset; ten to fifteen minutes is defensible and an hour is not. A long TTL buys nothing and leaves a large pile of half-finished logins sitting in a store. Single use matters as much as expiry. A callback can arrive twice for entirely ordinary reasons: a double-clicked link, a refresh, a back button, an over-eager link prefetcher. Delete-on-read makes the second arrival find nothing, which is right — the first attempt either issued a session or failed, and either way the second has nothing to do. If a session was already issued, notice that and send the supervisor onward rather than showing an error. The token endpoint is a second line of defence here: an authorization code redeemed twice comes back as `invalid_grant`, not as a second set of tokens. ## Where this goes wrong first - Keeping the record in a process-local map, which is wrong the moment there is a second instance and is emptied by every deploy. - Trying to derive the `code_verifier` from something deterministic instead of storing the random value. - Pushing the post-login destination into the callback URL instead of into the record. - Writing records and never expiring them, so the store grows for the life of the service.

  • Why must the code_verifier be stored rather than derived again at the callback?
    It is fresh random data generated per authorization request. Nothing in the callback carries it and nothing deterministic reproduces it, while the token request has to present that exact original value. Deriving it from something stable — a session id, a user id, a hash of the time — would make it predictable, which defeats the reason it is random. So it is stored, and it expires with the rest of the record.
  • The same callback arrives twice within a second. What happens?
    The first read consumes the record; the second finds nothing. Render the restart page, or, if the first attempt already issued a session, notice that and send the user on to their destination instead of showing an error. The token endpoint backs this up: an authorization code redeemed twice comes back as `invalid_grant` rather than as a second set of tokens.
  • What does the callback endpoint hand on once the response checks out?
    A subject — the provider's `iss` and `sub` identifiers plus whatever claims you trust — handed to your own session layer, which issues your session. Creating a local account on first sight, mapping claims to roles, joining this identity to an existing account, and deciding where the resulting credential rests in the browser are all separate concerns owned elsewhere.

saying these in an interview costs you the question

  • Thinks the provider returns the nonce and code_verifier with the callback
  • Recomputes the code_verifier instead of storing the random value
  • Keeps pending-login records in a process-local map
  • Gives the record an hour-long TTL, or no TTL at all
  • Returns a server error when the record has expired
  • Puts the post-login destination into the callback URL