Definition
Think about the difference between your driver’s license and your gym membership card. Your driver’s license proves who you are - your name, photo, date of birth, address. It’s an identity document. Your gym membership card proves what you can do - access the gym, use equipment, attend classes. It’s an authorization document. In the world of authentication, an ID Token is like your driver’s license - it contains verified claims about your identity. It’s completely separate from the Access Token (more like the gym card) that grants permissions to do things.
ID Tokens were introduced by OpenID Connect (OIDC), which layers identity functionality on top of OAuth 2.0. When you click “Sign in with Google” on a website, the site receives both an access token (to call Google APIs if needed) AND an ID token (containing your verified identity). The ID token is a JWT (JSON Web Token) with specific claims: your unique identifier (sub), email, name, profile picture URL, and when/how you authenticated. The website uses this to establish your session - they know exactly who you are because Google has verified it.
The crucial distinction: ID tokens are meant to be consumed by your application to identify the user, NOT sent to APIs for authorization. When your app receives an ID token from Google, it verifies the signature, checks that Google is the issuer and your app is the intended audience, and extracts user information to create a session. But when calling APIs, you use the access token instead. This separation matters for security - ID tokens contain personal information that shouldn’t be broadcast to every API, while access tokens are designed for that purpose.
Example
Real-World Scenario 1: “Sign In with Google” Flow You click “Continue with Google” on Canva.com. Google authenticates you (password, 2FA, whatever). Google redirects you back to Canva with both tokens. Canva ignores the access token (they don’t need to call Google APIs). They validate the ID token signature against Google’s JWKS, verify the audience claim matches their client ID, extract your email and name, and create a Canva session. The ID token told Canva exactly who you are, verified by Google.
Real-World Scenario 2: Enterprise Single Sign-On Your company uses Okta for SSO. When you log into the internal HR portal, the portal redirects you to Okta, you authenticate, and Okta sends back an ID token. The HR portal extracts your employee ID, department, and roles from the ID token claims. It creates your session with the right department-specific permissions. The ID token carried your verified corporate identity across the authentication boundary.
Real-World Scenario 3: Mobile App User Identification A fitness app lets users sign in with Apple. After Apple Sign In, the app receives an ID token containing the user’s Apple ID (anonymous identifier), email (if shared), and name. The app creates or looks up the user account based on the “sub” claim (unique Apple ID), personalizes the experience with the user’s name, and starts the fitness tracking session. The ID token established who the user is.
Real-World Scenario 4: Multi-Platform Gaming Account A gaming platform supports multiple identity providers. When you sign in via Discord, Twitter, or Google, each provides an ID token with the user’s identity. The platform extracts the unique identifier and provider from each ID token, links them to a single gaming account, and knows that [email protected] (Google), @JohnGamer (Discord), and @john_plays (Twitter) are all the same person. ID tokens from different providers get unified into one identity.
Analogy
The Government-Issued ID: Your passport or driver’s license proves who you are - name, photo, date of birth, nationality. It’s issued by a trusted authority (government) and can be verified as authentic. You show it to prove identity, not to gain access to specific services. ID tokens work identically - they’re government IDs for the digital world, issued by trusted identity providers (Google, Okta, Auth0).
The Name Badge at a Conference: When you register at a conference, you get a name badge with your verified name, company, and attendee type. This badge doesn’t let you into specific sessions (that’s your ticket/access token), but it tells everyone who you are. You wear it on your chest (the ID token travels with you) so people know your identity. The ID token is your verified conference name badge.
The Medical ID Bracelet: A hospital ID bracelet shows your name, date of birth, and medical record number - verified identity information. It doesn’t grant you access to medication (the doctor’s prescription does that), but it ensures staff know exactly who you are before any treatment. The ID token is this medical bracelet - verified identity that precedes any authorization decision.
The School ID Card with Photo: Your student ID shows your photo, name, student number, and year. It proves you’re a student at that school (identity). Different access rights (library after hours, lab access, meal plan) are encoded separately or managed by other systems. The student ID is your verified identity; the access permissions are separate. ID tokens work the same way.
Code Example
// Decode and validate ID token
const jwt = require('jsonwebtoken');
const jwksClient = require('jwks-rsa');
const client = jwksClient({
jwksUri: 'https://oauth.provider.com/.well-known/jwks.json',
cache: true,
cacheMaxAge: 600000
});
function getKey(header, callback) {
client.getSigningKey(header.kid, (err, key) => {
if (err) return callback(err);
callback(null, key.getPublicKey());
});
}
// Verify ID token with all required checks
async function verifyIdToken(idToken, expectedClientId, expectedIssuer) {
return new Promise((resolve, reject) => {
jwt.verify(idToken, getKey, {
audience: expectedClientId, // Must match your client ID
issuer: expectedIssuer, // Must match expected provider
algorithms: ['RS256'], // Must match expected algorithm
clockTolerance: 30 // Allow 30 seconds clock skew
}, (err, decoded) => {
if (err) {
return reject(new Error(`Invalid ID token: ${err.message}`));
}
// Additional validations
if (!decoded.sub) {
return reject(new Error('ID token missing subject claim'));
}
// Check nonce if you sent one (prevents replay attacks)
// if (decoded.nonce !== expectedNonce) { ... }
resolve(decoded);
});
});
}
// Usage: After OAuth callback
app.get('/callback', async (req, res) => {
const { code } = req.query;
// Exchange code for tokens
const tokens = await exchangeCodeForTokens(code);
// Verify and extract identity from ID token
const identity = await verifyIdToken(
tokens.id_token,
process.env.CLIENT_ID,
'https://accounts.google.com'
);
// Use identity to create session
const user = await findOrCreateUser({
providerId: identity.sub,
email: identity.email,
name: identity.name,
picture: identity.picture
});
req.session.userId = user.id;
res.redirect('/dashboard');
});
// Example ID token claims (decoded)
{
"iss": "https://accounts.google.com", // Issuer
"sub": "110169484474386276334", // Unique user ID
"aud": "your-client-id.apps.google", // Your application
"exp": 1700000000, // Expiration
"iat": 1699996400, // Issued at
"email": "[email protected]",
"email_verified": true,
"name": "John Doe",
"picture": "https://lh3.google.../photo.jpg",
"locale": "en"
}
Diagram
flowchart TB
subgraph IDToken["ID Token (JWT)"]
direction TB
Header["Header
alg: RS256
typ: JWT
kid: key-123"]
Payload["Payload (Claims)
iss: https://accounts.google.com
sub: 110169484474386276334
aud: your-client-id
exp: 1700000000
iat: 1699996400
email: [email protected]
name: John Doe"]
Signature["Signature
RSASHA256(header + payload)"]
end
Header --> Payload --> Signature
subgraph Purpose["Purpose: IDENTITY"]
Identity["Who is this user?"]
NotAuth["NOT for API authorization"]
end
IDToken --> Purpose
Note1["ID Token answers: WHO are you?"]
Note2["Access Token answers: WHAT can you do?"]
Security Notes
CRITICAL: ID tokens identify users in OpenID Connect. Never use for authorization (use access tokens).
Token Purpose:
- User identification: Proves user identity
- Authentication: Used for user authentication
- Claims: Contains user information (sub, name, email)
- JWT format: Signed JWT with user claims
Token Structure:
- Header: Algorithm and token type
- Payload: User claims (iss, sub, aud, exp, etc.)
- Signature: Signed with server’s private key
Usage Rules:
- DO NOT authorize: Never use for API authorization
- DO use access tokens: Use separate access tokens for API authorization
- DO verify signature: Always verify ID token signature
- DO validate claims: Validate iss, aud, exp claims
Security Best Practices:
- HTTPS mandatory: Always transmit over HTTPS
- Short lifetime: Keep ID tokens short-lived (minutes)
- Validation: Validate all claims before trusting
- Scope validation: Verify requested scopes are granted
- Audience validation: Verify token intended for your application