Integration and debugging
Token exchange and refresh
Investigation order for invalid_grant, why refresh tokens stop working, why scopes go missing, and whether to validate with jwks or introspect.
invalid_grant when exchanging the code
Check in order:
| Check | Notes |
|---|---|
| Was the code already used | Codes are single-use; reuse always fails |
| Has it expired | Code lifetimes are short; exchange immediately after receiving one |
Do code_verifier and code_challenge pair up | The PKCE S256 result must match |
Does redirect_uri match verbatim | It must equal the value in the authorization request exactly |
| Is client authentication correct | Confidential clients must use their registered credentials |
The full list is in Error codes.
Why did my refresh token suddenly stop working
Two causes:
- Rotation — every refresh issues a new value and kills the old one immediately. If two code paths refresh with the same old value, one must fail;
- Expiry — refresh tokens themselves expire (30 days for confidential clients, 14 days for public clients).
Serialise refreshes in concurrent scenarios: allow only one in-flight refresh per account per client.
Why is a scope I requested missing from the response
The server filters scopes by client capability, and filtering is a silent drop with no error. Trust the scope in the response, not what you requested. See Scopes and audiences.
Why does UserInfo return 401 with my access token
Most often the audience is wrong: UserInfo only accepts auth audience tokens whose scope includes at least one of openid / profile / email. The other common cause is an expired or revoked token.
Validate with jwks or introspect
| Method | Suitable for | Cost |
|---|---|---|
Local validation with jwks | High call volume, a 5-minute cache is acceptable | Must handle kid rotation and clock skew |
introspect | Needs real-time revocation state | Confidential clients only; a server round trip every time |
Public clients can only validate locally.
Related
- Token endpoint details: Token endpoint
- Token fields: Tokens