Skip to content

ID Token Claims — Complete Reference for Standard and Custom OIDC Claims

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about ID Token Claims. We cover key concepts, practical examples, and best practices to help you master this topic.

ID token claims are key-value pairs in the JWT payload that convey verified information about the authenticated user, the authentication event, and the token itself, following the Openid Connect specification.

What You'll Learn

  • All standard OIDC claims and their meanings
  • How to use claims for user profiling and security
  • Custom claims and provider-specific extensions

Why It Matters

Claims are the primary way your application learns about the user. Understanding each claim helps you build correct authorization logic, prevent security vulnerabilities, and handle provider-specific differences in user data.

Real-World Use

Doda Browser extracts the sub claim as the user's permanent ID, email for communication, email_verified to skip email confirmation, and picture for the avatar. The auth_time claim helps enforce re-authentication for sensitive operations.

flowchart LR
    IDToken["ID Token Payload"] --> Required["Required Claims\niss, sub, aud, exp, iat"]
    IDToken --> Recommended["Recommended Claims\nauth_time, nonce, at_hash, acr"]
    IDToken --> Optional["Optional Claims\nname, email, picture, locale"]
    Required --> Verify["Verification Logic"]
    Recommended --> Security["Security Checks"]
    Optional --> Profile["User Profile"]
    style IDToken fill:#dbeafe,stroke:#2563eb

Required Claims

Every ID token must include these claims:

required_claims = {
    "iss": "https://accounts.google.com",  # Issuer identifier
    "sub": "1234567890",                    # Subject (user ID)
    "aud": "your-client-id",               # Audience (your client ID)
    "exp": 1719561600,                      # Expiration time
    "iat": 1719558000,                      # Issued at time
}
recommended_claims = {
    "auth_time": 1719558000,   # When user authenticated (Unix timestamp)
    "nonce": "abc123",         # Anti-replay nonce from auth request
    "at_hash": "Hx4Jz...",    # Access token hash for binding
    "acr": "urn:mace:incommon:iap:silver",  # Authentication Context Reference
}

Optional Standard Claims

profile_claims = {
    "name": "Alice Smith",
    "given_name": "Alice",
    "family_name": "Smith",
    "middle_name": "Marie",
    "nickname": "Ali",
    "preferred_username": "alice_s",
    "profile": "https://example.com/alice",
    "picture": "https://example.com/avatar.jpg",
    "website": "https://alice.com",
    "email": "alice@example.com",
    "email_verified": True,
    "gender": "female",
    "birthdate": "1990-01-01",
    "zoneinfo": "America/New_York",
    "locale": "en-US",
    "phone_number": "+1-555-0100",
    "phone_number_verified": True,
    "address": {
        "street_address": "123 Main St",
        "locality": "Springfield",
        "region": "IL",
        "postal_code": "62701",
        "country": "US"
    },
    "updated_at": 1719500000,
}

Extracting and Using Claims

def process_id_token_claims(claims):
    user = {
        "id": claims["sub"],
        "provider": claims["iss"],
        "email": claims.get("email"),
        "email_verified": claims.get("email_verified", False),
        "name": claims.get("name"),
        "avatar_url": claims.get("picture"),
    }

    # Check if email is verified before allowing login
    if user["email"] and not user["email_verified"]:
        print(f"Warning: {user['email']} is not verified")

    # Use auth_time for re-authentication policy
    auth_time = claims.get("auth_time")
    if auth_time:
        from datetime import datetime, timezone
        auth_datetime = datetime.fromtimestamp(auth_time, tz=timezone.utc)
        hours_since_auth = (datetime.now(timezone.utc) - auth_datetime).total_seconds() / 3600
        if hours_since_auth > 24:
            print("Re-authentication required")
            return None

    return user

Provider-Specific Claims

Different providers add custom claims:

# Google-specific claims
google_claims = {
    "hd": "example.com",  # Hosted domain (Google Workspace)
}

# Microsoft-specific claims
microsoft_claims = {
    "oid": "00000000-0000-0000-0000-000000000000",  # Object ID
    "tid": "tenant-id",  # Tenant ID
    "unique_name": "alice@example.com",
}

# Auth0-specific claims
auth0_claims = {
    "https://dodatech.com/roles": ["admin", "user"],
}

Common Mistakes

1. Using email as the User Identifier

Emails change. The sub claim is the stable, permanent identifier. Always use sub for database keys.

2. Trusting email_verified Without Checking

A provider may return email_verified: false. Require verified emails for sensitive operations or skip email verification entirely.

3. Ignoring auth_time for Sensitive Operations

If a user authenticated 30 days ago, allow password changes without re-authentication? Use auth_time to enforce step-up authentication.

4. Relying on Claims That May Be Missing

Not all providers return name, picture, or locale. Always provide fallbacks or mark fields as unavailable.

5. Not Handling Claim Type Changes

Some providers change claim formats (e.g., email_verified from boolean to string). Validate claim types before use.

Practice Questions

  1. Why should you use sub instead of email as the user identifier?
  2. What is the purpose of auth_time and how can it be used?
  3. What does email_verified indicate and why is it important?
  4. Why might some claims be missing from an ID token?
  5. How do provider-specific claims differ from standard claims?

Answers:

  1. The sub claim never changes. Emails can change when a user updates their email at the provider.
  2. auth_time records when the user last authenticated. Use it to require re-authentication for sensitive operations like password changes.
  3. email_verified indicates the provider confirmed the user owns the email address. Use it to skip verification emails.
  4. Claims depend on the scopes requested (profile, email) and what the provider supports. Request specific scopes for desired claims.
  5. Standard claims are defined by the OIDC spec and consistent across providers. Custom claims are provider-specific and may vary.

Challenge: Build a user profile consolidation function that accepts ID token claims from Google, Microsoft, and Auth0, and returns a normalized user object that works regardless of the provider.

FAQ

What is the difference between `sub` and `oid` in Microsoft's ID tokens?

: sub is the standard OIDC subject claim. oid is Microsoft's unique object ID. Both are stable identifiers.

Can I add custom claims to my ID tokens?

: Yes. If you run your own OIDC provider, you can add custom claims. Some providers (Auth0) allow custom claims via rules/actions.

What is the `azp` claim?

: azp (Authorized Party) is the client ID that the token was issued to. It differs from aud in some scenarios.

How often do claims change?

: sub, iss, and aud never change for a given user-provider pair. Profile claims (name, email, picture) change when the user updates their profile.

What happens if the token contains unexpected claims?

: Ignore unknown claims. The OIDC spec allows providers to include additional claims. Your application should only rely on standard claims.

Mini Project

Create a Python function that validates and normalizes ID token claims from three different providers. Return a standardized user object with fields: id, provider, email, name, avatar, email_verified, and auth_time. Handle missing fields gracefully.

What's Next

Continue with UserInfo Endpoint to learn how to retrieve additional user data, or explore Discovery URL for provider configuration.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro