invalid_grant
Docs version v1 (main)Nauthera is pre-1.0, in a design-partner program
The authorization code, refresh token or device code is invalid, expired, already used, revoked, or not this client's (RFC 6749 §5.2).
Meaning
Sent by /token only. For an authorization code: unknown or expired (codes are single
use and live for issuer.auth_code_ttl, 60 seconds by default), already redeemed,
issued to another client, redeemed with another redirect_uri, a code_verifier that
does not match, PKCE required but the code has no challenge, or a verifier for a code
without one. For a refresh token: unknown or expired, already rotated, another client's,
its session ended, every grant of the account retired, the account suspended or blocked,
or its grant family revoked. A device code issued to another client. A code, refresh
token or device code bound to a DPoP key, presented without a proof of that key.
Refusals of one kind share one description, so a stolen credential cannot be told from
an expired one.
Impact
For a refresh, the user is signed out of the application, which must send them through
/authorize again. For a code, the sign-in the user just completed fails at the last
step. Redeeming a code twice or presenting a used refresh token also revokes the whole
grant family: its refresh token stops working and its access tokens are refused at the
server's own endpoints. Two concurrent refreshes with one token, or a retried refresh
whose response was lost, cause exactly this.
Diagnosis
- Look the request up by
X-Request-IDon the dashboard's Troubleshooting page. The refusal is recorded underoauth.token.issued(oauth.token.refreshedfor a refresh) with reasoninvalid_grantand a cause:code_unknown,code_reused,grant_client_mismatch,redirect_uri_mismatch,pkce_mismatch,pkce_required,pkce_verifier_unexpected,refresh_unknown,refresh_reused,session_ended,subject_revoked,account_unavailable,grant_revokedordpop_binding_mismatch. - A reused code or refresh token is recorded instead as
oauth.token.revoked, raised to warning, with atriggerofauthorization_orcode_ replay refresh_token_reuseand the grant family it revoked. - For
session_endedorsubject_revoked, the user's Sessions tab on the dashboard shows the logout, administrator action or password reset that ended it.
Mitigation
- As a client developer: treat any
invalid_granton refresh as "sign in again". Store the new refresh token from every response, and serialize refreshes per user. Redeem a code once, promptly, with the sameredirect_uriand the verifier of its challenge. See Refresh. - As an operator: a
refresh_token_reuserevocation from an application that refreshes concurrently is a client bug; one from an unfamiliar address may be a stolen token. - As a user: sign in to the application again.
Escalation
If codes are refused as code_unknown within their lifetime, check that Redis is
reachable from every replica, since codes are kept there. If refusals do not match the
recorded cause, open an issue with the request id, the server version and the cause.
Never attach codes, refresh tokens or verifiers.
OAuth error runbooks · Generated from internal/ in nauthera-server.