BE5: Sign-in with OAuth 2 and OpenID Connect
The authorization code flow with PKCE
🎯You'll be able to
Follow each step of the authorization code flow with PKCE, the recommended way for web and mobile apps to get tokens.
🏠The everyday picture
At a hospital, you show your ID at reception and get a numbered slip. You take the slip to the pharmacy counter, which hands over the medicine only if the slip matches the one reception issued, and only once.
📘What it really is
- The authorization code flow, step by step:
- the app sends the user's browser to the IdP's
/authorizepage with:client_id,redirect_uri,scope, a randomstate, and a PKCEcode_challenge; - the user signs in at the IdP (the app never sees the password) and agrees to the scopes;
- the IdP redirects back to the exact
redirect_uriwith a short-lived, one-time code and the samestate; - the app checks
statematches (against forged sign-ins), then sends the code to the IdP's/tokenendpoint with the PKCEcode_verifier; -
the IdP checks the verifier matches the challenge and returns tokens.
-
PKCE (Proof Key for Code Exchange): the app makes a random secret (the verifier), sends only its SHA-256 hash (the challenge) at the start, and reveals the verifier at the end. Someone who steals the code can't use it without the verifier.
-
OAuth 2.1 guidance: use the code flow with PKCE for all apps; the old implicit flow (tokens in the URL) and the password grant (app collects the password) are not recommended.
-
Redirect URIs must match exactly, so a code can't be sent to an attacker's site.
📝A worked example
Input: npm run token in the kit. Steps you can watch:
tools/get-token.jsmakesverifier = random 32 bytes,challenge = BASE64URL(SHA256(verifier)), and a randomstate.- It prints a link:
http://localhost:4100/authorize?response_type=code&client_id=tiffin-web&redirect_uri=http://localhost:4500/callback&scope=openid profile orders:read orders:write&state=...&code_challenge=...&code_challenge_method=S256. - You choose "test customer 1"; the server redirects to
http://localhost:4500/callback?code=...&state=.... - The tool checks
state, then postsgrant_type=authorization_code, the code,redirect_uri,client_idandcode_verifierto/token. - Expected output: a JSON response with
access_token,id_token,refresh_token,expires_in: 600andscope.
Try sending the same code twice: the second time the server answers invalid_grant, because codes work once.
🌍Where it's used
- Web apps and single-page apps signing users in.
- Mobile apps, which can't keep a client secret, so PKCE protects them.
- Command-line tools that open a browser to sign in.
📝Case scenario
A team's mobile app uses the old implicit flow: the access token comes back in the redirect URL. Another app on the phone registers the same custom URL scheme and receives some users' tokens. With the code flow and PKCE, a stolen code is useless without the verifier, which never left the real app.
🖱️Try it
Run npm run token, sign in, and copy the access token. Then change one character of the code_verifier in tools/get-token.js and
try again. Success: the first run gives tokens, the second gives invalid_grant with a PKCE message, and you can explain why.
⚠️Common mistakes
- Skipping
statechecks. Sign: the callback accepts any code. Fix: comparestatewith the value you sent. - Loose redirect URIs. Sign: wildcards like
https://*.example.com/*. Fix: register exact URIs. - Using the password grant. Sign: your app's own form collects the IdP password. Fix: send users to the IdP with the code flow.
🛠️For your project
Your Project 1 demo shows a real token from npm run token being used against your API.
✅Quick check
What does PKCE protect against?
Show the answer
A stolen authorization code being exchanged for tokens by someone who doesn't have the code verifier.
Why must the app check state on the callback?
Show the answer
To make sure the response belongs to a sign-in it started, stopping forged or replayed callbacks.
