Authorization code¶
The authorization code grant ends every flow that goes through a browser. The authorization request brings the person to the provider, the provider signs them in and asks for their consent where one is needed, and the browser carries a code back to a redirect URI of the client. The client then spends that code at the token endpoint (RFC 6749 §4.1.3), with the PKCE verifier that only it holds.
The code¶
| Lifetime | 60 seconds, tokens.authorization_code_ttl, at most 600 (RFC 6749 §4.1.2) |
| Value | 256 random bits in base64url, 43 characters |
| What the provider keeps | Its SHA-256, never the value |
| Bound to | The client, the redirect URI of the request, the PKCE challenge, the DPoP key the request reserved it for when it reserved it for one (RFC 9449 §10), and the session it was issued in |
| What it carries | The scopes, the resources, the authorization details, the nonce and the claims of the request, and what the session had proven at that instant: auth_time, acr and amr |
What the session had proven is frozen on the code: a step-up or a new login afterwards changes the session, and never a code already issued nor a token that descends from one.
The request¶
| Parameter | |
|---|---|
grant_type |
authorization_code |
code |
Required |
redirect_uri |
Required: the one the authorization request carried, as it carried it |
code_verifier |
Required when the authorization request carried a code_challenge (RFC 7636 §4.5), refused when it carried none |
resource |
Optional and repeatable: narrows what the access token is addressed to, among the resources of the authorization (RFC 8707 §2.2) |
authorization_details |
Optional: narrows the details the access token carries, among those of the authorization (RFC 9396 §7) |
The client authenticates beside them with the method it registered, which for a public client is its
client_id and nothing else (client authentication).
What is checked¶
A code is spent by its first presentation, whatever comes of it. It is looked up by its digest and
consumed atomically before anything else is compared: of any number of requests presenting the same code at
once, exactly one is the first, and a presentation that fails a check below has spent the code all the same.
Every refusal of this list is invalid_grant with the one description The authorization code is invalid.,
so that a client guessing learns nothing of which check it failed.
- The code is one this provider issued, and it was not presented before.
- It was issued to the client that authenticated.
- It has not expired.
redirect_uriis the redirect URI of the authorization request, compared as a string (RFC 6749 §4.1.3).- PKCE holds (RFC 7636 §4.6): the
code_verifier, transformed withS256, is thecode_challengeof the request. A code issued without a challenge, which only a confidential client registered withrequires_pkce: falseobtains, is refused when acode_verifiercomes with it (RFC 9700 §4.8.2). - A code the request reserved for a key, with
dpop_jktor by the DPoP proof of its pushed request, is spent by a request whose DPoP proof is signed by that key (RFC 9449 §10), and by no other. - The session the code was issued in is still active: it has not ended, and it has not expired.
A missing code or redirect_uri is invalid_request, answered before the code is read, which leaves it
unspent. A code_verifier that is not 43 to 128 unreserved characters (RFC 7636 §4.1) is refused. Other
refusals come after every check has passed, and the code is spent: a resource the authorization does not
carry is invalid_target, and an authorization_details it does not carry is
invalid_authorization_details.
What the answer carries¶
- An access token, in the shape the client registered (access tokens). It is addressed to the resources the token request named, or to all those of the authorization when it named none, or to the client itself when the authorization carries none. It is bound to what this request binds it to, the key of its DPoP proof or the certificate of its connection, as grants says, and is a bearer token otherwise.
- An ID token when
openidis among the scopes, carrying thenonceof the authorization request (ID tokens). - A refresh token when one is due: the client is registered for
refresh_token, and eitheroffline_accesswas granted or the client is confidential. It starts a family whose absolute lifetime istokens.refresh_token_absolute_ttl, ninety days by default, and it carries the whole authorization, whatever subset of its resources and details the access token beside it was narrowed to (refresh tokens).
The act tokens.issued is written in the journal of the person, with what the client was given.
A code presented twice¶
A code presented a second time is refused with invalid_grant, and the authorization it stood for is taken
back (RFC 6749 §4.1.2): one of the two presentations may have been an attacker's, and the provider cannot
tell which.
| What descends from the code | What happens to it |
|---|---|
| The grant itself | Revoked: the provider's own endpoints and its introspection refuse every access token of that grant from then on |
| Every refresh token descending from the code | Revoked, the rotated ones included |
| The opaque access tokens of the grant | Removed, so that wherever they are presented they are unknown |
| A self-contained access token of the grant | Accepted until it expires by a resource server that verifies it offline against the JWK Set, which nothing the provider does can reach. It lives tokens.access_token_ttl, ten minutes by default |
The act authorization_code.replayed is written in the journal of the person, naming the client the code
was issued to, the client that presented it again, and the refresh tokens it revoked.