ID Tokens: The Identity Proof Hiding Inside OAuth 2.0
OAuth 2.0 was never built to answer "who is this person?" — yet almost every modern login screen answers exactly that question using something called an ID Token. This guide takes the ID Token apart piece by piece so you know exactly what it is, how it's built, how it's verified, and why getting it wrong has caused real security incidents.
Picture a sealed envelope handed to you by a notary. Inside, a signed and stamped letter says: “I, the notary, personally confirm this person is exactly who they claim to be, and I confirmed it at this exact time.” You don’t need to call the notary’s office to double-check — the seal itself is proof. An ID Token is that sealed envelope in the digital world. It is the single piece of the OpenID Connect (OIDC) puzzle that turns OAuth 2.0, a protocol built purely for permissions, into something that can also answer the question every login screen ultimately needs to answer: “who, exactly, is this?”
1Core Concepts
The gap OAuth 2.0 left open
OAuth 2.0, released in 2012, was designed to answer one question only: “can this application perform this specific action on the user’s behalf?” It hands out access tokens — scoped, revocable permissions — but deliberately says nothing standardized about who the underlying user actually is. For years, developers still needed to know “who is logged in right now,” so many of them improvised: they’d take the access token, use it to call an API like “get my profile,” and treat a successful response as proof of identity. This workaround was inconsistent across providers and, worse, created real security holes, because an access token’s meaning is defined only by whichever API accepts it — it was never designed to be a trustworthy identity statement.
What is an ID Token?
An ID Token is a compact, digitally signed piece of data, introduced by OpenID Connect in 2014, that makes a formal, verifiable statement: “this specific user authenticated with us, at this specific time, and here are some verified facts about them.” Unlike an access token, an ID Token is meant to be read directly by the application, not sent onward to an API. It is built using a widely adopted format called a JWT (JSON Web Token), which lets any application verify its authenticity using nothing but a public key — no extra network call required.
An access token is like a hotel keycard — it opens specific doors (your room, the gym, the pool) but doesn’t say anything about who’s holding it. An ID Token is like the passport you showed at check-in — a government-issued, tamper-evident document that says, unambiguously, exactly who you are, issued by an authority the hotel already trusts.
Proves identity
Confirms who logged in and when — never used to call an API.
JWT (JSON Web Token)
A compact, URL-safe, three-part signed string.
Consumed by the app
Read once at login time, then typically discarded.
Why “ID Token” and “access token” are so often confused
Both are issued at almost the same moment, by the same server, during the same login flow — which is exactly why beginners mix them up. The cleanest way to separate them: the access token is a permission slip for machines (APIs), while the ID Token is an identity certificate for the application itself.
2Architecture & Components
- End User: The human being who logs in and whose identity the ID Token describes.
- Relying Party (RP): The application requesting login — this is the party that will read and trust the ID Token.
- OpenID Provider (OP): The trusted authority (Google, Microsoft, Okta, Auth0, a company’s own identity system) that authenticates the user and issues the ID Token.
- JSON Web Key Set (JWKS): A publicly available set of cryptographic keys, published by the OpenID Provider, that any Relying Party can use to verify an ID Token’s signature.
- Discovery Document: A well-known, standardized JSON file the OpenID Provider publishes, listing its endpoints and public keys so Relying Parties can configure themselves automatically.
The three parts of a JWT-based ID Token
Every ID Token, structurally, is three sections joined by dots, each separately encoded:
Header
Describes the token type and the signing algorithm used (for example, RS256), so the receiver knows how to verify it.
Payload (Claims)
The actual identity facts — who the user is, who issued the token, who it’s meant for, and when it expires.
Signature
A cryptographic seal, computed over the header and payload, proving the token hasn’t been altered since the OpenID Provider issued it.
graph TD
OP["OpenID Provider"] -->|Publishes| JWKS["JSON Web Key Set (public keys)"]
OP -->|Issues| IDT["ID Token: Header + Payload + Signature"]
IDT -->|Delivered to| RP["Relying Party / Application"]
RP -->|Fetches once, caches| JWKS
RP -->|Verifies signature using| JWKS
RP -->|Reads claims: sub, iss, aud, exp| USER["Establishes who the user is"]
3Internal Working
The most important claims inside the payload
- iss (Issuer): The exact identity of the OpenID Provider that created the token — the application must confirm this matches the provider it actually trusts.
- sub (Subject): A stable, unique identifier for the user, guaranteed by the provider never to be reassigned to a different person.
- aud (Audience): The specific application this token was issued for — critical, because it prevents a token meant for App A from being reused to log into App B.
- exp (Expiration): The exact timestamp after which the token must be rejected, no matter how valid it looked a moment earlier.
- iat (Issued At): The timestamp when the token was created, useful for detecting unusually old tokens.
- nonce: A random value the application generated before starting login, echoed back inside the token, specifically to block replay attacks.
- Optional profile claims: Depending on requested scopes, the token (or a companion UserInfo endpoint) may include email, name, or picture.
How signature verification actually works
When an application receives an ID Token, it does not simply trust the claims written inside — anyone could type arbitrary text into a JWT-shaped string. Instead, the application recalculates the expected signature using the OpenID Provider’s public key (fetched from the JWKS endpoint) and compares it against the signature attached to the token. If even a single character in the header or payload changed after signing, the recalculated signature will not match, and the token is rejected outright. This is what makes the ID Token “self-contained and verifiable” — no phone call to the OpenID Provider is needed at verification time.
Think of a banknote’s security thread and watermark. You don’t call the central bank every time someone hands you cash — you hold the note up to the light and check the watermark yourself. The ID Token’s digital signature is that watermark: verifiable instantly, by anyone who knows what genuine ones look like, without contacting the issuer.
The full mandatory validation checklist
A correct OpenID Connect implementation must check all of the following before trusting an ID Token — skipping even one has caused real vulnerabilities in production systems:
- Signature is valid, using the correct public key and algorithm
issmatches the expected OpenID Provider exactlyaudmatches this application’s own registered client IDexphas not passednonce, if one was sent, matches what the application originally generated
4Data Flow & Lifecycle
sequenceDiagram
participant Browser
participant RP as "Relying Party (App)"
participant OP as "OpenID Provider"
Browser->>RP: 1 User clicks "Sign in"
RP->>Browser: 2 Redirect to OP with scope=openid and a nonce
Browser->>OP: 3 User authenticates
OP->>Browser: 4 Return authorization code
Browser->>RP: 5 Deliver code
RP->>OP: 6 Exchange code for tokens (back-channel)
OP->>RP: 7 Return ID Token and access token
RP->>RP: 8 Verify ID Token signature, claims, nonce
RP->>Browser: 9 Establish local session (cookie)
Step-by-step lifecycle
- Request: The application includes the special
openidscope, plus a freshly generatednonce, when redirecting the user to the OpenID Provider. - Issuance: After successful login, the OpenID Provider creates and signs the ID Token, embedding the requested claims.
- Delivery: The ID Token arrives at the application, typically via the secure back-channel token exchange step.
- Verification: The application runs the full validation checklist described in Chapter 3.
- Consumption: The application reads the identity claims once, creates its own session (usually a cookie), and then generally discards the ID Token entirely.
- Expiration: Even if it were kept around, the ID Token becomes worthless the moment its
exptimestamp passes.
An ID Token is meant to be used once, at login time. If your application finds itself repeatedly re-checking an old ID Token to decide “is this user still logged in,” that’s a sign the session design needs rethinking — use your own session mechanism for that instead.
5Advantages, Disadvantages & Trade-offs
Advantages
- Self-contained: no extra network call needed to verify identity
- Standardized claim names (
sub,iss,aud) work the same across every compliant provider - Cryptographically tamper-evident
- Compact enough for mobile networks and browser redirects
Disadvantages / Trade-offs
- Cannot be revoked mid-flight the way a session can — once issued, it’s valid until it expires
- Payload claims are only base64-encoded, not encrypted, by default — never put secrets inside one
- Clock synchronization matters — a server with the wrong system time can wrongly reject valid tokens
- Developers unfamiliar with JWTs sometimes misuse the ID Token as if it were an access token
6Security
Key risks and their defenses
- “None” algorithm attack: Some early JWT libraries allowed a token’s header to declare “no signature algorithm,” which some careless implementations then blindly trusted. Modern libraries explicitly reject this, but it’s a cautionary lesson in never trusting a token’s own claims about how it should be verified.
- Algorithm confusion attacks: An attacker tries to trick a server expecting an asymmetric signature (RS256) into instead validating with a symmetric method (HS256) using a key it can guess or already knows. Defense: explicitly pin the expected algorithm rather than trusting whatever the token header claims.
- Missing audience check: Skipping the
audvalidation lets a token legitimately issued for one application be replayed to authenticate against a different one. - Replay attacks: Without nonce validation, a captured ID Token could theoretically be resubmitted. The nonce ties each token to one specific login attempt.
- Storing sensitive data inside claims: Because the payload is only encoded, not encrypted, anyone who obtains the token can read its contents — never place passwords, secrets, or highly sensitive personal data inside an ID Token.
Never send an ID Token to your own backend API as though it were an access token to authorize a request. The ID Token’s aud claim identifies your application, not your API — using it this way silently breaks the audience-restriction protection the entire design relies on.
Encrypted ID Tokens
For situations demanding confidentiality of the claims themselves (not just tamper-evidence), OpenID Connect also supports an optional encrypted variant, nested inside another JWT layer. This is less common in everyday consumer logins but appears in higher-assurance enterprise and government identity systems.
7Monitoring, Logging & Metrics
What to log
- Every ID Token validation failure, tagged by reason (bad signature, expired, wrong audience, wrong issuer, nonce mismatch)
- JWKS key rotation events, including the key identifier used for each validation
- Clock-skew related rejections, since these often point to infrastructure drift rather than genuine attacks
Validation failure rate
A sudden spike right after a provider’s key rotation usually means the app’s cached JWKS is stale.
Nonce mismatch count
Persistent mismatches can indicate replay attempts or a broken session-storage mechanism for nonces.
JWKS fetch latency
Slow key retrieval directly delays every login on a cache miss.
Issuer distribution
Tracking which OpenID Providers are actually being used helps catch unauthorized or misconfigured identity sources.
Caching the JWKS correctly
Fetching the JWKS on every single login would be wasteful and slow, so applications cache it — but caching it forever is dangerous, because it would miss legitimate key rotations. Best practice is to cache the JWKS for a reasonable period (commonly a few hours) and refresh immediately whenever a token references a key identifier that isn’t in the current cache.
8Design Patterns & Anti-patterns
Recommended patterns
- Verify, then translate: Validate the ID Token fully, extract only the claims you actually need, then immediately create your own internal session — don’t keep passing the raw token around your system.
- Pin the expected algorithm: Explicitly configure which signing algorithm your application expects, rather than reading it from the token itself.
- Use library-provided validation, not hand-rolled parsing: Mature OpenID Connect libraries implement the full checklist correctly; writing your own JWT parser from scratch is a common source of subtle bugs.
- Separate identity from authorization: Use the ID Token purely to establish who the user is, and a separate mechanism (roles, permissions, scopes) to decide what they can do.
The Problem
Using the ID Token to authorize calls to a backend API, treating it interchangeably with the access token.
Why It Fails
The ID Token’s audience is the application itself, not the API. APIs relying on it may end up unintentionally accepting tokens that were never meant to authorize anything beyond proving login.
The Fix
Keep a strict separation: ID Token proves identity to the application; access token authorizes API calls.
9Best Practices & Common Mistakes
Best practices
- Always validate every item on the full checklist from Chapter 3 — never skip even one for convenience.
- Generate a fresh, cryptographically random nonce for every single login attempt; never reuse one.
- Use a well-maintained OpenID Connect library rather than writing custom JWT parsing logic.
- Keep the application’s system clock synchronized (via NTP) to avoid false expiration or “not yet valid” rejections.
- Rotate signing keys periodically on the provider side, and make sure Relying Parties handle key rollover gracefully.
Common mistakes teams actually make
Mistake: Trusting an unverified token’s claims
Reading the sub or email claim out of a token before checking its signature, effectively trusting user-supplied data as if it were verified fact.
Mistake: Hardcoding a single JWKS key instead of the full key set
Pinning to one specific public key breaks the very next time the provider rotates its keys, causing a sudden, confusing outage across all logins.
Mistake: Ignoring the “aud” claim for multi-tenant applications
An application serving multiple client IDs (web app, mobile app) that doesn’t check aud per context risks a token from one client being accepted where it shouldn’t be.
10Real-World & Industry Examples
Google Sign-In
When a website shows “Sign in with Google,” Google’s OpenID Provider issues an ID Token containing the user’s Google account identifier, email, and name, which the website verifies before creating a local account session — all without the website ever seeing the user’s Google password.
Microsoft Entra ID (formerly Azure AD)
Enterprise applications integrated with Microsoft’s identity platform receive ID Tokens describing the signed-in employee, often including organizational claims like their department, which internal apps use to personalize dashboards.
Auth0 and Okta as Identity Brokers
Many companies don’t talk to Google, Microsoft, and their own employee directory separately. Instead, they integrate once with a broker like Auth0 or Okta, which normalizes all of these different sources into a single, consistent ID Token format for every connected application.
Mobile Banking Apps
Many banking apps use ID Tokens issued by their own internal identity system after biometric login (fingerprint or face recognition), letting the app confirm identity locally before making any authorized API calls with a separate access token.
11Frequently Asked Questions
You should not. ID Tokens are meant for the application itself to establish identity, not for authorizing API calls — that is the access token’s job.
Not by default — it’s signed, not encrypted, meaning its contents are readable by anyone who obtains it, but cannot be altered without detection. Encrypted variants exist but are far less common.
Skipping nonce validation removes a key defense against replay attacks, where a previously issued, still-valid ID Token could be resubmitted by an attacker to impersonate a login event.
Email addresses can change or be reused over time, but the sub claim is guaranteed by the OpenID Provider to be a stable, unique identifier for that user — always prefer it as your primary key for identifying accounts.
Generally no — most applications read it once, extract the needed claims, establish their own session, and discard the token. Long-term storage of raw ID Tokens is rarely necessary and adds unnecessary risk.
12Summary and Key Takeaways
Key Takeaways
- An ID Token is OpenID Connect’s contribution to OAuth 2.0 — a signed, verifiable statement of user identity, distinct from the access token.
- It is structured as a JWT with three parts: header, payload (claims), and signature.
- Critical claims include
iss,sub,aud,exp, andnonce— every single one must be validated, not just the signature. - ID Tokens are self-contained and verifiable offline, using public keys published in a JWKS, avoiding extra network calls at verification time.
- They are short-lived and meant to be used once, at login time, then discarded in favor of the application’s own session mechanism.
- The most damaging real-world mistake is using an ID Token to authorize API access — that responsibility belongs strictly to the access token.
- Correct implementation depends far more on disciplined validation and key management than on the protocol’s design alone.