Module 04 · API fundamentals · Lesson 14 of 15
OAuth 2.0 and OpenID Connect
Delegated access without sharing passwords: the roles, the authorization code flow with PKCE step by step, which grant fits which client, scopes and consent, and how OpenID Connect adds identity on top.
Why it matters
"Sign in with Google", "connect your calendar", "allow this app to read your repositories": every one of these is OAuth. It solves a problem that used to be solved by handing your password to a third party. In a design, OAuth is how you let partners act on behalf of users, how your own mobile app logs in without handling passwords, and how services authenticate to each other. The flows look fiddly until you see what each step protects against.
The idea
OAuth 2.0 is a framework for delegated authorization: a user (the resource owner) lets an application (the client) access their data at an API (the resource server) by way of an authorization server that authenticates the user and issues tokens. The client never sees the password; it gets a scoped, expiring access token.
The authorization code flow with PKCE
This is the flow for anything with a user and a browser, including single-page and mobile apps.
- 1
Client prepares
Generates a random code verifier and its SHA-256 hash, the code challenge (PKCE). Redirects the browser to the authorization server with client id, requested scopes, redirect URI, a random state value and the challenge. - 2
User authenticates and consents
The authorization server logs the user in (password, passkey, MFA, whatever it enforces) and shows what the client is asking for. - 3
Authorization code returned
The browser is redirected back to the client's registered redirect URI with a short-lived, single-use code and the same state. The client checks state matches to stop login CSRF. - 4
Code exchanged for tokens
The client calls the token endpoint directly (not through the browser) with the code and the original verifier. The server hashes the verifier and compares it to the challenge, proving the same client that started the flow is finishing it. It returns an access token, often a refresh token, and with OpenID Connect an id token. - 5
Client calls the API
Access token in the Authorization header. The resource server validates it (JWT verification or introspection) and checks scopes.
Which grant for which client
| Grant | Who | Notes |
|---|---|---|
| Authorization code + PKCE | Web apps, SPAs, mobile and desktop apps acting for a user | The default; PKCE replaces the client secret for public clients and is recommended for all |
| Client credentials | A service acting as itself (no user) | Client id and secret (or mutual TLS) exchanged directly for a token; service-to-service |
| Device code | TVs, CLIs, devices without a browser | User enters a code on another device; the client polls for tokens |
| Refresh token | Any client that holds one | Exchange for a new access token without re-authenticating; rotate on use |
| Implicit, password | Nobody, any more | Deprecated: tokens in URL fragments or passwords given to the client; do not design with them |
Scopes and consent
Scopes name what the token may do: orders:read, calendar.events. The client requests them, the user sees them on the consent screen, the token carries the granted set, and the resource server enforces them. Request the minimum; a client that asks for everything gets refused by users and blamed in breaches. Scopes are coarse permissions across the API; fine-grained ownership checks remain the resource server's job.
OpenID Connect adds identity
OAuth alone issues access tokens for APIs; it does not tell the client who the user is. OpenID Connect (OIDC) layers that on: the same code flow also returns an id token, a JWT about the user (subject, name, email, when they authenticated) intended for the client, and a userinfo endpoint. "Sign in with X" is OIDC. Access tokens are for calling APIs; id tokens are for knowing who logged in; do not swap them.
Operating it
Register exact redirect URIs and reject anything else; open redirects turn into token theft. Keep access tokens short-lived and rotate refresh tokens on every use so a stolen one is detected when both copies try to refresh. Publish discovery metadata and JWKS so resource servers verify tokens without calling back. For internal service-to-service, client credentials with short tokens or mutual TLS through a mesh is the usual choice.
Trade-offs
OAuth gives you delegated, scoped, revocable access without password sharing and a standard every identity provider speaks, at the cost of several redirects, a token lifecycle to manage, and a protocol with enough options that mistakes are easy. Using a well-maintained provider or library and the one recommended flow removes most of the risk; building an authorization server from scratch is rarely worth it.
In an interview
Pick the grant and name its protections: "Users log in through the authorization code flow with PKCE against our identity provider; state prevents login CSRF, PKCE prevents code interception, redirect URIs are exact-match. The SPA receives a short-lived access token and a rotating refresh token in an HttpOnly cookie. Services authenticate to each other with client credentials." For login specifically, add: "OIDC id token tells us who the user is; the access token is only for calling APIs."
Check yourself
A mobile app uses the authorization code flow. An attacker on the device intercepts the redirect and captures the authorization code. Why can they not obtain tokens?