OAuth 2.0 is an authorization framework that allows third-party applications to access a user’s resources on another service without exposing the user’s credentials. When you click “Sign in with Google” or authorize an app to access your GitHub repos, that’s OAuth.
The core problem OAuth solves
Before OAuth, if App A wanted to access your data on Service B, you’d give App A your Service B password. This is terrible — App A has full access, you can’t revoke it granularly, and if App A is compromised, your Service B account is too.
OAuth introduces delegated authorization: you grant App A a limited-scope token that provides specific access (read your email, but not send on your behalf) for a limited time, without ever sharing your password.
Key roles
- Resource Owner: You, the user.
- Client: The application requesting access (e.g., a third-party app).
- Authorization Server: Issues tokens after you grant permission (e.g., Google’s auth server).
- Resource Server: Hosts the protected data (e.g., Google’s API).
Authorization Code flow
The most common and secure flow for web applications:
- The client redirects you to the authorization server with a
client_id,redirect_uri, and requestedscope. - You log in and consent to the requested permissions.
- The authorization server redirects back to the client with a short-lived authorization code.
- The client exchanges this code (plus its
client_secret) for an access token on a back-channel request. - The client uses the access token to call the resource server’s API.
The authorization code is exchanged server-to-server, so the access token never passes through the browser.
Tokens
- Access token: Short-lived (minutes to hours). Sent with API requests in the
Authorization: Bearer <token>header. - Refresh token: Long-lived. Used to obtain new access tokens without making the user log in again.
- ID token (OpenID Connect): A JWT containing user identity claims. OIDC is an identity layer built on top of OAuth 2.0.
PKCE (Proof Key for Code Exchange)
PKCE (pronounced “pixy”) adds protection for public clients (mobile apps, SPAs) that can’t safely store a client_secret. The client generates a random code_verifier, hashes it to create a code_challenge, and the authorization server verifies the original verifier during the token exchange. This prevents authorization code interception attacks.
Common mistakes
- Storing access tokens in localStorage (vulnerable to XSS — use HTTP-only cookies instead)
- Not validating the
stateparameter (enables CSRF attacks) - Requesting overly broad scopes
- Using the Implicit flow (deprecated in OAuth 2.1 — use Authorization Code with PKCE instead)
Inspect JWTs from OAuth flows with the JWT Decoder or build test tokens with the JWT Builder.