How it works
The explanation, with examples you can run.
The cast
- Resource owner: the user.
- Client: the app that wants access (a web app, a mobile app, a backend job).
- Authorization server: issues tokens after the user logs in and consents (Google, Microsoft Entra ID, Okta, Auth0, Keycloak, your own).
- Resource server: the API that accepts access tokens.
The authorization code flow with PKCE
This is the flow to use for web apps, single-page apps and mobile apps:
1. Client creates a random code_verifier and its SHA-256 hash, the code_challenge.2. Client redirects the browser to the authorization server: /authorize?response_type=code&client_id=...&redirect_uri=... &scope=openid profile&state=...&code_challenge=...&code_challenge_method=S2563. User logs in (password, passkey, MFA) and consents.4. Authorization server redirects back: redirect_uri?code=...&state=...5. Client checks that state matches, then POSTs to /token with code, redirect_uri, client_id and the original code_verifier.6. Authorization server checks the verifier against the challenge and returns an access token, often a refresh token, and (with OIDC) an ID token.The browser only ever sees a short-lived, single-use code. PKCE (RFC 7636) means a stolen code is useless: whoever redeems it must present the verifier that matches the challenge from step 1. RFC 9700 requires PKCE for public clients (apps that can't keep a secret, like SPAs and mobile apps) and recommends it for everyone else. state (or the OIDC nonce) ties the response to the request and blocks cross-site request forgery on the redirect.
Tokens and what they're for
- Access token: sent to the resource server. Its audience is the API. The client shouldn't depend on its contents; it may be opaque.
- Refresh token: lets the client get new access tokens without the user. Keep it on the server where possible; for public clients use rotation (each refresh returns a new refresh token and invalidates the old one) or sender-constrained tokens.
- ID token (OIDC): a signed JWT for the client, with
sub(a stable user ID),iss,aud(the client's ID),exp,nonce. The client validates it to log the user in. It is not an API credential.
Identify users by iss plus sub, not by email: emails change and can be reassigned.
Retired and special-purpose flows
- Implicit grant (tokens directly in the redirect URL): RFC 9700 says clients should not use it. Use code + PKCE.
- Resource owner password credentials (the app collects the password): must not be used.
- Client credentials: fine for service-to-service calls with no user.
- Device authorization: for TVs and CLIs that can't show a browser easily.
Practical rules
Use a certified library or your provider's SDK; don't write the flow by hand. Register exact redirect URIs (no wildcards). Request the smallest scopes you need. Validate every ID token's signature, iss, aud, exp and nonce. On APIs, accept only access tokens issued for that API.
Check yourself
3 questions. Get them all right to finish this topic.
0 of 3 right so far. Get all three to finish this topic.