Token
Exchange an authorization code for tokens, or refresh an access token.
POST
Handles two grants, selected by the
grant_type body field: authorization_code (with mandatory PKCE) and refresh_token (with rotation and reuse detection). Accepts both application/json and application/x-www-form-urlencoded. Prefer form encoding, since that’s what most OAuth libraries send by default.
Access tokens are JWTs signed with ES256, valid for 900 seconds. Refresh tokens are opaque strings, valid for 60 days from whenever they were last issued, and rotate on every use.
Client authentication
How the client proves its identity depends on thetoken_endpoint_auth_method it registered with:
- Public clients (
none) don’t send a secret. PKCE (code_verifier) is the only proof. This is the default and covers native, CLI, and other clients that can’t keep a secret. - Confidential clients additionally authenticate, on top of PKCE:
client_secret_basic: sendclient_idandclient_secretin the HTTPAuthorization: Basicheader (each URL-encoded, joined with:, base64-encoded). With this methodclient_idcan be omitted from the body.client_secret_post: sendclient_secretas a body parameter alongsideclient_id.private_key_jwt: send a JWT signed with the client’s private key asclient_assertion, withclient_assertion_typeset tourn:ietf:params:oauth:client-assertion-type:jwt-bearer. With this methodclient_idcan be omitted from the body. Seeprivate_key_jwtfor the claims the JWT must carry.
client_secret, fails with invalid_request.
PKCE
Before starting the authorization request, generate:code_verifier: a high-entropy random string, 43–128 characters, from the unreserved character set (A-Z,a-z,0-9,-,.,_,~). Keep it client-side only.code_challenge: the base64url-encoded SHA-256 hash ofcode_verifier, sent during authorization.
Authorization code grant
Body Parameters
string
required
Must be
"authorization_code".string
Required, except with
client_secret_basic, where the Authorization header
already carries it, and with private_key_jwt, where the assertion does.string
Required for a confidential client using
client_secret_post. Omit for public
clients and for client_secret_basic (send the secret in the Authorization
header instead). See Client authentication.string
Required with
private_key_jwt. Must be
urn:ietf:params:oauth:client-assertion-type:jwt-bearer.string
Required with
private_key_jwt: a JWT signed with the client’s private key.
See Client authentication.string
required
The code from the
/oauth/authorize callback. Single-use: redeeming it twice
fails with invalid_grant. Expires 10 minutes after it was issued.string
required
Must exactly match the
redirect_uri used in the authorization request.string
required
The original value that
code_challenge was
derived from.
Body Parameters
string
required
Must be
"refresh_token".string
Required, except with
client_secret_basic, where the Authorization header
already carries it, and with private_key_jwt, where the assertion does.string
Required for a confidential client using
client_secret_post. Omit for public
clients and for client_secret_basic. See Client
authentication.string
Required with
private_key_jwt. Must be
urn:ietf:params:oauth:client-assertion-type:jwt-bearer.string
Required with
private_key_jwt: a JWT signed with the client’s private key.
See Client authentication.string
required
string
Optionally narrow the scope of the new access token. Must be a subset of what
the grant already has. You can’t use this to escalate scope.