In OpenID Connect, when is a UserInfo round trip worth making if the ID token already carries the attributes?
answer
- one is a photograph, one a phone call
- frozen at sign-in versus answered now
- freshness is what the trip buys
- updated_at says when it last moved
- the cost lands on the login path
basics
~20 sWhen the attribute can change between sign-ins. An ID token is a snapshot frozen when it was minted; a UserInfo call is answered now. The round trip buys freshness, and buys nothing for values that cannot move.
solid answer
~50 sThe two sources differ in **when they were true**. An ID token states what was so at the moment of authentication and is never revised afterwards — a display name changed an hour later is not in it. A UserInfo call is answered when it arrives, so it returns the current values. The round trip therefore pays for mutable attributes a relying party renders: a display name, a picture, a verified email address. It pays nothing for the account key or for facts about the sign-in itself, which cannot change after issuance, and it is a poor place to read values needed on every request, since that puts a network call in the request path. The costs are a hop on the login path, a dependency on the provider being reachable, and a cache policy you now own.
code
pseudocode · 19 lineson sign-in:
claims <- validated ID token claims # a snapshot of this moment
store account key and sign-in facts from claims
before rendering a public profile card:
if cached profile is newer than the staleness window:
render cached profile
return
body <- call userinfo endpoint with the access token
if call failed:
render cached profile # no new information, not empty
return
if body.sub is not exactly equal to claims.sub:
render cached profile # mismatch: keep what was trusted
return
cache body with the current time
render bodygo deeper
Hold on to one contrast: the statement from sign-in says what was true then, and a call to the endpoint says what is true now.
Explain the consequence — which attributes go stale in a token, which cannot change at all, and why that decides where each is read from.
Show the operating decisions: a deliberate staleness window, a degrade path when the provider is slow, and the subject comparison still running before anything is overwritten.
The broader call is whether every service reads attributes from the provider directly or from one profile your platform owns, and what each choice costs in coupling, latency and blast radius when the provider has a bad day.
A relying party finishing a sign-in already holds a set of claims. Calling the UserInfo endpoint asks for more, or asks for the same ones again. Whether that call is worth making is not a question about which source is richer — it is a question about **when each one was true**. ## Two moments, not two sources An ID token is a statement about an event: this person authenticated at this provider at this time. Every member in it was fixed when it was minted, and the provider does not reach back into a token that has already been issued. If the end user edits their display name an hour later, the relying party's copy still says the old one, and will keep saying it until a new sign-in produces a new statement. A UserInfo call is answered when it arrives. It reflects the provider's record at that instant. That is the entire difference, and every trade-off below comes out of it. ## What the round trip buys - **Current values** for attributes that move between sign-ins — a name, a picture, whether an email address has since been verified. - **`updated_at`**, a number of seconds since the epoch saying when the provider last changed the end user's information, which lets a relying party decide cheaply whether anything actually moved before rewriting its own copy. - **A place for attributes needed rarely**, so that the statement validated at every login stays about authentication rather than growing into a profile document. ## What it costs - **A hop on a path the user is waiting on.** Making the call during sign-in adds the provider's latency to a page nobody wants slow. - **A dependency.** A sign-in that could have completed from the validated token alone now fails, or hangs, when the provider's resource endpoint is degraded. Design the call so a failure degrades to the values already in hand rather than failing the login. - **A live credential.** The call can only be made while the relying party holds a usable access token, which ties attribute refresh to the lifetime of something that was issued for a different purpose. - **A cache policy you now own.** Having two sources of the same attribute means deciding which one wins, and for how long, in code that somebody will read in two years. ## Deciding what to read from where | what is needed | where to read it | |---|---| | the key your account rows hang off | the authentication statement you validated at sign-in | | a fact about the sign-in itself | the same statement — it cannot change after issuance | | a mutable attribute you render to other people | a UserInfo call, at a cadence you choose | | something needed on every request you serve | the statement you already hold, so no call sits in the request path | ## The failure this is really about The common production bug is not a missing call — it is a call made exactly once. A relying party copies profile members into its own tables on first login and never reads again. Months later a person has changed their name at the provider, changed it in three other applications that federate to the same provider, and cannot work out why this one still shows the old one. There is no error anywhere; the data is simply as old as the account. The opposite failure is rarer but more expensive: calling UserInfo on every request to render a name that changes twice a year, turning an internal page render into a dependency on somebody else's uptime. ## Operating it 1. Read and store the stable key and the sign-in facts from the validated statement, once per sign-in. 2. Refresh rendered attributes on a staleness window you choose deliberately — per sign-in, daily, or on demand when the person asks — rather than by accident. 3. Compare the returned `sub` against the sign-in statement's `sub` before replacing anything, and on mismatch keep what you had. 4. Treat a failed call as *no new information*, not as *the attribute is now empty*. The question an interviewer is really asking is whether a candidate knows that an authentication statement is a photograph. Candidates who have only wired up a login treat both sources as "where the user's details come from"; candidates who have operated one know which of the two goes stale, and have decided on purpose how long they will let it.
- A relying party renders a person's display name on every page. Where should it read that name from?From its own store, refreshed from UserInfo on a cadence, never from a call in the render path. The statement validated at sign-in seeds the value; a periodic refresh keeps it honest. Putting a provider round trip behind every page makes your availability a function of theirs for a value that changes twice a year.
- What does `updated_at` let a relying party avoid?Rewriting its own records and invalidating downstream caches when nothing has actually changed. The member is a number of seconds since the epoch marking when the provider last changed the end user's information, so comparing it with the value stored last time turns a full field-by-field diff into one integer comparison.
- The provider's UserInfo endpoint is timing out during sign-in. What should the relying party do?Complete the sign-in. The authentication statement was already validated, so the session is legitimate; the missing attributes are cosmetic. Degrade to whatever values are already stored, mark them stale, and retry out of band. Failing the login for a profile picture converts a partial outage on someone else's system into a total one on yours.
A printed staff directory is accurate on the day it went to press; ringing the desk is accurate now. The ID token is the printing and the UserInfo call is the phone call — worth making only for the entries that actually change between printings.
saying these in an interview costs you the question
- Treats the ID token's claims as updating themselves over time.
- Copies attributes once at first login and never reads them again.
- Calls the endpoint on every page render to display a name.
- Fails the whole sign-in when the attribute call times out.
- Assumes a UserInfo answer is always richer than the sign-in statement.