Before trusting an ID token, an app must check that the issuer matches, the audience contains its own client ID, the signature verifies with the expected algorithm, the token has not expired, and the nonce matches the one it sent. Skipping any of these can let a token meant for someone else log a user in.
Checklist
These follow section 3.1.3.7 of OpenID Connect Core.
- Decrypt the token if it is encrypted.
- Issuer. The
issmust exactly match the issuer you expect for this provider. - Audience.
audmust contain yourclient_id. Reject if your client is not listed, or if audiences you do not trust are present. - Authorized party. If
azpis present, it should equal yourclient_id. - Signature. Verify the JWS using the issuer's keys. When the token arrives directly from the token endpoint over TLS, the spec allows TLS server validation to stand in for the signature check. Verifying anyway is safer and cheap.
- Algorithm. Expect RS256 or the algorithm your client registered. Do not accept whatever the token header claims.
- Expiry. The current time must be before
exp. - Issued at. Use
iatto reject tokens issued too long ago. The acceptable window is up to you. - Nonce. If you sent a nonce, the token must carry the same value. Check for replay.
- Authentication context. If you requested
acr, confirm the value is appropriate. - Auth time. If you requested
auth_timeor setmax_age, check it, and ask the user to sign in again when too much time has passed.
Why each check exists
The iss check stops a token from a different provider being accepted. The aud check stops a token issued to another application, possibly one a user signed in to legitimately, from working on yours. The algorithm check blocks downgrade tricks, including the well known case where a token claims none or switches between asymmetric and symmetric algorithms.
The nonce binds the token to the browser session that started the login. It is required for the implicit flow and for hybrid flows that return an ID token from the authorization endpoint. Use it in the code flow too.
Use a library, then test it
Do not hand roll this. Use a maintained OIDC client library and configure it with the expected issuer, your client ID and a fixed algorithm list. Then write tests that feed it:
- a token with the wrong issuer
- a token with another client's audience
- an expired token
- an unsigned token
- a token with a changed nonce
All of them must be rejected.
Keys and rotation
Fetch signing keys from the provider's published key set rather than hard coding them, cache with a sensible lifetime, and refresh when you see an unknown key ID. Rate limit refreshes so a flood of bad tokens cannot make you hammer the provider. For general JWT rules, see the RFC 8725 checklist.
Quick check
Is the access token validated the same way?
No. The ID token is for your app. The access token is for the API, and the API validates it, either locally or by asking the authorization server.
Do I need the nonce in the authorization code flow?
The spec requires it for implicit and some hybrid flows. It is still a good defense for the code flow, so most libraries send it.
Sources
Last reviewed: 19 October 2026.