Overview
OAuth 2.0 is a delegation protocol: it lets a user grant an application limited access to their data on another service without sharing their password. 'Let this photo app read my Google Photos' is OAuth. The app receives an access token with specific scopes, issued by the authorization server after the user consents.
The recommended flow for web and mobile apps is the authorization code flow with PKCE. OAuth itself is about authorization; OpenID Connect (OIDC) adds an ID token on top so apps can also log users in. Client credentials is the flow for machine-to-machine access with no user involved.
Instead of handing a parking attendant your full car keys (your password), you give a valet key that only drives the car a short distance and cannot open the trunk (limited scopes). You can revoke it any time without changing your main key.
When to use it
- 'Sign in with Google / Microsoft / GitHub'.
- Third-party apps accessing a user's data on your platform.
- Single sign-on across your own apps (with OIDC).
- Service-to-service access with client credentials.
Where it shows up in interviews
Recognize it when: users sign in with an existing account.
- Design login for a consumer app
- Design SSO across products
Recognize it when: external apps request access to user data.
- Design the Spotify / GitHub developer platform
- Design an app marketplace
Where it is used in real software
Uses OIDC on top of OAuth 2.0; the app gets an ID token with the user's identity and optional access tokens for Google APIs.
Third-party tools request scopes like repo:read; users can revoke access in settings.
Services obtain short-lived tokens from an authorization server (Okta, Keycloak, Entra ID) to call other services.
Key terms
- Resource owner
- The user who owns the data.
- Client
- The application requesting access.
- Authorization server
- Authenticates the user and issues tokens.
- Scope
- What the token allows, such as photos.read.
- PKCE
- Proof Key for Code Exchange: prevents stolen authorization codes from being used.
- Refresh token
- Long-lived token used to get new short-lived access tokens.
Authorization code flow with PKCE
- 1App creates a code verifier
A random secret; it sends its SHA-256 hash (code challenge) in the next step.
- 2Redirect to the authorization server
/authorize?client_id=...&scope=photos.read&redirect_uri=...&code_challenge=...
- 3User logs in and consents
The user sees which scopes the app is requesting.
- 4Redirect back with a code
The authorization server sends a short-lived one-time code to redirect_uri.
- 5App exchanges the code
POST /token with the code and the original code verifier; the server checks it matches the challenge.
- 6App calls the API
Authorization: Bearer <access_token>; refresh it with the refresh token when it expires.
STEP 1The app redirects the browser to /authorize with the requested scopes and a code_challenge.
Which OAuth flow to use
Implicit and password grants are deprecated
| Client type | Flow | Why |
|---|---|---|
| Server-side web app | Authorization code (+ PKCE) | Client secret stays on the server |
| Single-page app / mobile | Authorization code + PKCE | No secret can be kept; PKCE protects the code |
| Service to service | Client credentials | No user involved |
| TV / CLI with no browser | Device authorization | User approves on another device |
NOWClient type: Server-side web app | Flow: Authorization code (+ PKCE) | Why: Client secret stays on the server
In interviews, 'authorization code with PKCE' is the right default answer for any app involving a user.
Implementation
const base64url = (bytes: ArrayBuffer) => btoa(String.fromCharCode(...new Uint8Array(bytes))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); async function startLogin() { const verifier = base64url(crypto.getRandomValues(new Uint8Array(32)).buffer); const challenge = base64url(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier))); const state = crypto.randomUUID(); // CSRF protection sessionStorage.setItem("pkce", JSON.stringify({ verifier, state })); const url = new URL("https://auth.example.com/authorize"); url.search = new URLSearchParams({ response_type: "code", client_id: "photo-app", redirect_uri: "https://photos.example.app/callback", scope: "openid profile photos.read", code_challenge: challenge, code_challenge_method: "S256", state, }).toString(); location.assign(url);} async function handleCallback(code: string, state: string) { const { verifier, state: expected } = JSON.parse(sessionStorage.getItem("pkce")!); if (state !== expected) throw new Error("State mismatch"); const res = await fetch("https://auth.example.com/token", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: new URLSearchParams({ grant_type: "authorization_code", code, redirect_uri: "https://photos.example.app/callback", client_id: "photo-app", code_verifier: verifier }), }); return res.json(); // { access_token, refresh_token, id_token, expires_in }}Complexity and performance
Short to limit damage if leaked.
Rotate on use; revocable.
One-time per session.
Trade-offs
Short tokens limit exposure but require more refreshes; long tokens are convenient but dangerous if stolen.
Opaque tokens require an introspection call but are instantly revocable; JWTs are verified locally but stay valid until expiry.
Variants and related techniques
Adds an ID token (a JWT with user identity) and a userinfo endpoint for login.
A service swaps a user's token for a narrower token to call another service on their behalf.
Common mistakes
- Using OAuth access tokens as proof of login.
Fix: Use OIDC ID tokens for authentication; access tokens are for calling APIs.
- Skipping state and PKCE.
Fix: state prevents CSRF on the callback; PKCE prevents stolen codes from being redeemed.
- Loose redirect URI matching.
Fix: Register exact redirect URIs; wildcard matching enables token theft.
Interview questions
What is the difference between OAuth 2.0 and OpenID Connect?
OAuth 2.0 grants an app access to resources (authorization). OIDC is built on OAuth and adds an ID token so the app knows who the user is (authentication).
Why is PKCE needed for mobile and single-page apps?
They cannot keep a client secret. PKCE binds the authorization code to a secret generated per login, so an intercepted code is useless without the verifier.
Practice problems
| Problem | Difficulty | What it trains |
|---|---|---|
| Draw the authorization code + PKCE flow | Easy | Actors and steps. |
| Design 'Sign in with Google' for a web app | Medium | OIDC and sessions. |
| Design a developer platform with third-party app access | Hard | Scopes, consent, revocation. |