This guide is written by the team that builds Casdoor and describes the standards as published (listed under Sources), with notes on how Casdoor implements them as of November 1, 2026. Spotted a mistake? Tell us.
OIDC logins produce two tokens with similar-looking names and completely different jobs:
- The ID token tells the client who the user is.
- The access token lets the client call an API.
Sending one where the other belongs is a common source of bugs — and occasionally of security holes. This article explains the difference and how to handle each.
The ID token
A signed JWT issued by the OpenID provider. Its audience is the client: it exists so the client can verify that the user authenticated and read basic claims.
Typical claims:
| Claim | Meaning |
|---|---|
iss |
Issuer |
sub |
Stable user identifier |
aud |
The client ID this token was issued to |
exp, iat |
Expiry and issue time |
nonce |
Binds the token to the login request |
name, email |
Profile claims, per scopes |
Rules:
- Validate it on the client (signature, issuer, audience, expiry, nonce).
- Never send it to APIs. The API has no use for it and should not accept it as an authorization credential.
- Do not use it as a long-term session. Persist the claims you need server-side; keep tokens short.
The access token
An OAuth token that represents a grant, not a person. Its audience is the resource server, and it carries scopes that say what the client may do.
Rules:
- Validate it on the API: signature or introspection, issuer, audience, scopes, expiry.
- Treat it as opaque on the client unless the API design documents otherwise.
- Never use it for identity. An access token may be a JWT with a user ID in it; that does not make it a login credential.
- Keep it short-lived, with refresh tokens for continuity.
Side by side
| ID token | Access token | |
|---|---|---|
| Purpose | Authentication: who is the user | Authorization: what may the client do |
| Audience | The client (your app) | The resource server (your API) |
| Typical format | JWT | Opaque or JWT |
| Validated by | The client | The API |
| Sent to APIs | No | Yes, in the Authorization header |
| Lifetime | Short | Short, renewed by refresh token |
| Claims | User identity | Scopes, grant context |
The mistakes that actually happen
1. The API accepts ID tokens as authorization. An ID token is addressed to the client, not the API. Accepting it means any client that can get an ID token for itself can call the API with it. Check the aud claim on the API side.
2. The client uses the access token as identity. Access tokens can be opaque; parsing them for a user ID is undefined behavior that breaks the day the provider rotates formats.
3. Profile data is read from the access token. If you need richer claims, call the userinfo endpoint with the access token, or request the scopes that put them in the ID token.
4. Tokens are stored where XSS can reach them. Single-page apps storing both tokens in localStorage turn any XSS into full account takeover. Prefer httpOnly cookies with a backend-for-frontend, or memory-only storage.
5. No audience check anywhere. Both tokens are JWTs; both validate as signatures; without an audience check, they are interchangeable. That is exactly what you do not want.
The refresh token
A third token, for continuity: the client exchanges it for new access tokens without bothering the user. It is not used for identity or for API calls, and it is as sensitive as a password.
- Rotate it on use when the provider supports rotation.
- Revoke it on logout, password change and suspicious activity.
- Store it server-side where possible.
A validation checklist
Client, for the ID token:
- Signature against the provider's JWKS.
issmatches the provider.audcontains this client ID.- Not expired, with a small clock skew.
noncematches the request.- Algorithm is expected; reject
none.
API, for the access token:
- Signature or introspection.
issmatches the provider.audcontains this API.- Required scopes are present.
- Not expired.
- Revocation checked where the token is long-lived.
If both sides run the right checks, mixing the tokens is impossible by construction.
Related reading: what OpenID Connect is, what OAuth 2.0 is and OAuth grant types.
Sources
- OpenID Connect Core 1.0 (ID token)
- RFC 6749: The OAuth 2.0 Authorization Framework (access token)
- RFC 6750: Bearer Token Usage
Product names are trademarks of their respective owners.