It is the flow almost every interactive app should use.
The authorization code grant is the default flow for any app with a user:
- The client redirects the browser to the AS's authorization endpoint with
response_type=code, itsclient_id, a pre-registeredredirect_uri, the requestedscopeand a randomstate. - The user logs in at the AS and approves the requested access.
- The AS redirects back to
redirect_uri?code=...&state=.... The code is short-lived (often under a minute) and single-use. - The client checks that
statematches what it stored, then POSTs the code to the token endpoint (a back-channel call, not through the browser), authenticating itself if it is a confidential client. - The AS returns JSON with an
access_token,token_type(Bearer),expires_in, often arefresh_token, and with OIDC anid_token.
Why the two steps? The front channel (browser redirects) is visible to the user's browser, history and extensions; the token travels only on the direct, authenticated back channel.
state protects against CSRF on the callback: an attacker cannot make your app accept their code. Redirect URIs must be registered and matched exactly by the AS, so codes cannot be sent to an attacker's site.
Every client, including SPAs and mobile apps, should also use PKCE with this flow, next lesson.
Example
# 1. Browser is redirected to the authorization endpoint
https://auth.example.com/authorize?response_type=code
&client_id=web-app
&redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback
&scope=openid%20profile%20orders.read
&state=af0ifjsldkj
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256
# 2. After login/consent the AS redirects back:
# https://app.example.com/callback?code=SplxlOBeZQQYbYS6WxSbIA&state=af0ifjsldkj
# 3. Back channel: exchange the code for tokens
curl -s https://auth.example.com/token \
-u "web-app:$CLIENT_SECRET" \
-d grant_type=authorization_code \
-d code=SplxlOBeZQQYbYS6WxSbIA \
-d redirect_uri=https://app.example.com/callback \
-d code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
# {"access_token":"...","token_type":"Bearer","expires_in":3600,"refresh_token":"..."}Remember
- Front channel returns a short-lived code; back channel swaps it for tokens
statemust round-trip and match to block CSRF- Redirect URIs are pre-registered and matched exactly
- Use PKCE with every authorization code flow
Watch out for
- Skipping the
statecheck on the callback, allowing login CSRF.