skip to content

In OpenID Connect, what does the nonce claim in an ID token bind that token to?

level: middleimportance: should knowfreq 54%

answer

  1. one value, out and back
  2. the client invents it and remembers it
  3. echoed into the assertion, compared exactly
  4. binds the token to this request
  5. conditional: sent, so returned and checked

basics

~20 s

The nonce binds an ID token to the one authentication request that carried the same nonce value. The client generates it, sends it, and must reject any ID token whose nonce claim is not exactly the value it sent.

solid answer

~50 s

The client generates a fresh unguessable `nonce`, puts it on the authentication request, and stores it with the pending login. If the request carried a `nonce`, the provider MUST include a `nonce` claim in the ID token whose value is exactly what was sent, and the client MUST verify that equality. What that buys is request binding: an ID token obtained somewhere else, for another login or another moment, will not carry this client's pending nonce and is rejected before its `sub` is ever read. What it does not buy is anything about how recently the user authenticated, and it is not `state` — `state` binds the callback to the browser session that started the flow, while `nonce` binds the assertion to the request. If no `nonce` was sent, no `nonce` claim is required in the token.

code

http · 2 lines
http
GET /authorize?response_type=code&scope=openid%20profile&client_id=s6BhdRkqt3&state=af0ifjsldkj&nonce=n-0S6_WzA2Mj&redirect_uri=https%3A%2F%2Fenrol.college.example%2Fcallback HTTP/1.1
Host: id.college.example

go deeper

for a junior

Recall the round trip: your application invents the nonce, sends it, and the ID token must come back carrying the same value for you to believe it.

for a middle

Explain both obligations — the provider echoes it when it was sent, the client compares exactly — and distinguish it from the parameter that binds the callback to the browser session.

for a senior

Show where the stored value lives, how a second tab or a replayed callback interacts with it, and why a predictable or reused nonce quietly removes the whole protection.

for a principal

Frame it as one of several bindings a login has to carry, and be able to say which binding each failure mode maps to rather than adding parameters by folklore.

## The value and the round trip `nonce` is a case-sensitive string the client invents for one login attempt. It travels out on the authentication request, and it comes back inside the ID token as a claim of the same name. The rule has two halves, and both matter: 1. **The provider's half.** If the authentication request contained a `nonce`, the authorization server MUST include a `nonce` claim in the ID token, with the value that was sent. Unmodified, uninterpreted, echoed. 2. **The client's half.** The client MUST verify that the `nonce` claim equals the value it sent on that request. Not merely that it is present: equal. Because the client has to compare, it has to remember. The value is stored against the pending login — in the same place the client keeps the rest of its in-flight request state — and looked up when the callback arrives. ## What the binding is worth The ID token is delivered at the end of a flow the user agent takes part in, and a browser will submit whatever it is pointed at. Without a per-request value inside the assertion, a relying party's callback cannot tell *this* login's token from any other valid token of the same shape. An attacker who holds an ID token obtained in a different session — their own, an older one, one captured elsewhere — can present it and be believed. The nonce closes that: the token is only acceptable if it carries the value this client generated for the login currently in flight. A token minted for any other request cannot carry it, because the attacker did not know it when the token was minted. ## Three values that are often confused | Value | Sent where | Compared against | What it binds | |---|---|---|---| | `nonce` | the authentication request | the ID token's `nonce` claim | this assertion to this request | | `state` | the authorization request | the value the client stored for this browser session | the callback to the flow the browser started | | `aud` | nothing — it is issued, not sent | the client's own `client_id` | the assertion to this client | They are not interchangeable and none substitutes for another. A relying party that sends `state` but no `nonce` has bound the callback without binding the assertion inside it; one that checks `nonce` but not `aud` has bound the assertion to its request without confirming it was ever addressed to it. ## What the nonce does not do - It says **nothing about freshness of the sign-in**. A provider can answer from a session established hours ago and still echo the nonce faithfully. How recently the user authenticated is asked for and reported by other members of the specification. - It does **not** authenticate the client. Anyone can put a value on a request. - It does **not** make the token confidential. A signed ID token's payload is readable by anyone who holds it. - It does **not** replace the audience or issuer checks, which fail differently and for different reasons. ## Operational notes Generate the nonce from a cryptographically secure source and never reuse one; a predictable or recycled value hands the attacker the one thing the check depends on. Bind the stored value to the browser that started the flow, so that a second tab or a replayed callback cannot consume another tab's pending nonce. The specification also allows a client to use `iat` to reject tokens issued too far from the current time, which usefully bounds how long nonces have to be stored at all; beyond that, checking for nonce replay is left to the implementation. A detail worth internalising for interviews: the obligation is **conditional in both directions**. Send no nonce and the provider owes you no nonce claim; send one and the provider must echo it and you must check it. "Always reject a token with no nonce" is wrong as stated, and will reject conformant tokens in flows where the client never sent one. ## What a good answer sounds like State the round trip in one sentence, name the two MUSTs, say what the binding rules out — an assertion obtained in some other login being replayed into this one — and be explicit that it is neither `state` nor a statement about how recently the user signed in.

  • The client sent no nonce. Must the ID token still carry one?
    No. The provider's obligation is conditional: it MUST include a `nonce` claim only when the authentication request carried one. A client that rejects every token without a nonce will reject conformant tokens. The sound rule is the symmetric one — if you sent a nonce, require it back and compare exactly; if you did not, you have no comparison to make and have given up that binding.
  • Why store the nonce server-side rather than reading it back out of the returned token?
    Because reading it out of the token is not a check at all — it compares the token with itself. The value has to come from somewhere the attacker cannot influence: the client's own record of the request it started. The specification also lets a client use `iat` to reject tokens issued too long ago, which bounds how long those records need to be kept.
  • Is checking the nonce enough on its own to trust an ID token?
    No. It establishes that the assertion answers this request; it says nothing about who issued it, who it was addressed to, or whether it has expired. The issuer match, the audience check against the client's own `client_id`, signature verification and the expiry check all still apply, and each fails for a different reason.

It is the reference number you write on your own letter and require quoted in the reply. A reply without it may be perfectly genuine correspondence — it is simply not an answer to the letter you sent.

saying these in an interview costs you the question

  • The nonce is the same thing as the state parameter.
  • A nonce proves the user just authenticated.
  • Read the nonce out of the token and check it is present.
  • One fixed nonce per client is fine, it never leaves our code.
  • Always reject an ID token that has no nonce claim.