The token endpoint — POST /oauth/token. Two grants:
authorization_code and refresh_token.
Called by a machine with no cookie, so the route must skip CSRF protection;
every response carries cache-control: no-store.
What is checked on a code redemption
All of it, every time — a code is only as good as its bindings:
- redemption is atomic and single-use; a second attempt is a replay, and a replay revokes the whole refresh family rather than merely failing
- the code was issued to this authenticated client
redirect_uribyte-matches the one the code was issued for- the PKCE verifier hashes to the stored challenge (S256, constant-time)
- a
resourcein the request must equal the one the code was issued for — it may narrow nothing and widen nothing, because the audience was fixed at authorization time
Refresh rotation
Every refresh returns a new refresh token and retires the old one. Reusing a retired token is the signal that someone has a copy — and since there is no way to tell whether it is the client or an attacker, the entire family is revoked and both must re-authorize (RFC 6819 §5.2.2.3). A refresh may narrow scope; it can never broaden it.
Every failure answers invalid_grant with a fixed description. A caller cannot
tell an unknown code from an expired one from a stolen one.