OAuth 2.0 and OpenID Connect power the “Sign in with Google” buttons, API integrations, and permission systems behind nearly every modern web application. This guide walks through the concepts visually — starting from first principles and building up to a complete interactive flow diagram.
Authentication vs Authorization
These two terms sound similar, but they solve different problems:
- Authentication answers “Who are you?” — Think of showing your passport at border control. You prove your identity.
- Authorization answers “What are you allowed to do?” — Think of a visa stamp in that passport. It grants specific permissions.
A system can authenticate you (confirm your identity) without authorizing you to do everything, and vice versa. OAuth 2.0 is primarily an authorization framework. OpenID Connect (OIDC) adds an authentication layer on top.
The Problem Before OAuth
Imagine it’s 2008. You want Gmail to import your Yahoo contacts. The only way to do this? Give Gmail your Yahoo password.
This was a real pattern. Applications like Gmail would literally ask:
“Enter your Yahoo email and password so we can fetch your contacts.”
This approach was terrible for several reasons:
- Gmail gets full access to your entire Yahoo account — not just contacts
- You can’t revoke Gmail’s access without changing your Yahoo password
- If Gmail gets hacked, your Yahoo credentials are compromised too
- There’s no audit trail of what Gmail did with your account
OAuth was created to solve exactly this problem.
Delegated Authorization
The core idea of OAuth is delegated authorization: letting a third-party application access your resources on another service without sharing your password.
Instead of credentials, the flow produces an access token — a short-lived, scoped key that grants only the permissions you approved. Gmail never sees your Yahoo password. It just gets a token that says “this user allowed read-only access to their Yahoo contacts.”
OAuth 2.0 Key Terminology
Before diving into the flow, let’s define the four main roles:
| Term | What it is | In our example |
|---|---|---|
| Resource Owner | The user who owns the data | You (the person with a Yahoo account) |
| Client | The app requesting access | Gmail (wants to read your contacts) |
| Authorization Server | Issues tokens after user consents | login.yahoo.com |
| Resource Server | Hosts the protected resources | contacts.yahoo.com (the API) |
Two other terms you’ll see constantly:
- Access Token — a credential the Client sends to the Resource Server to access protected resources. Typically short-lived (minutes to hours).
- Authorization Code — a temporary, one-time-use code that the Client exchanges for an access token. It’s never exposed to the browser for long.
Authorization Code Flow
The Authorization Code Flow is the recommended flow for web applications with a server-side back-end. It keeps secrets out of the browser and exchanges a short-lived code for tokens securely on the server.
Click through the interactive diagram below to see each step:
Step-by-step HTTP details
Step 1 — Authorization Request
The Client redirects the user’s browser to the Authorization Server:
GET /authorize? response_type=code &client_id=gmail-app-id &redirect_uri=https://gmail.com/callback &scope=contacts.read &state=abc123Host: login.yahoo.comThe state parameter is a CSRF protection token — the Client generates it and verifies it when the response comes back.
Step 2 — User Login + Consent
The Authorization Server shows a login page. The user enters their Yahoo credentials (directly on Yahoo’s site — never shared with Gmail). After authenticating, the user sees a consent screen:
“Gmail wants to: Read your contacts. Allow / Deny?”
Step 3 — Authorization Code Returned
If the user clicks Allow, the Authorization Server redirects back to the Client:
HTTP/1.1 302 FoundLocation: https://gmail.com/callback?code=AUTH_CODE_HERE&state=abc123The authorization code is short-lived (typically 30-60 seconds) and can only be used once.
Step 4 — Exchange Code for Token (Back Channel)
The Client’s server sends a POST request directly to the Authorization Server — this never touches the browser:
POST /token HTTP/1.1Host: login.yahoo.comContent-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=AUTH_CODE_HERE&redirect_uri=https://gmail.com/callback&client_id=gmail-app-id&client_secret=GMAIL_SECRETThe response includes the access token:
{ "access_token": "ya29.a0AfH6SM...", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "1//0gdBq..."}Step 5 — Access Protected Resource
The Client uses the access token to call the Resource Server:
GET /api/contacts HTTP/1.1Host: contacts.yahoo.comAuthorization: Bearer ya29.a0AfH6SM...The Resource Server validates the token and returns the requested data.
Scopes, Consent & Channels
Scopes
Scopes define what the Client can do. They’re the granular permissions attached to an access token:
contacts.read— read contacts onlycontacts.write— create and modify contactsemail— access the user’s email addressprofile— access basic profile info
The Client requests scopes, the user approves them, and the issued token is limited to those scopes.
Front Channel vs Back Channel
You may have noticed that some steps happen in the browser and others happen server-to-server:
- Front channel (browser-based) — steps 1, 2, and 3. The browser is involved as the intermediary. Data flows through redirects and query parameters, which are visible in the URL bar and browser history.
- Back channel (server-to-server) — steps 4 and 5. Direct HTTP calls between servers. The
client_secretandaccess_tokennever appear in the browser.
This separation is what makes the Authorization Code Flow secure. Sensitive credentials stay in the back channel where they can’t be intercepted by browser extensions, JavaScript, or network observers on the client side.
Other OAuth 2.0 Flows
The Authorization Code Flow isn’t the only option. OAuth 2.0 defines several flows (also called “grant types”) for different situations:
Implicit Flow (Legacy)
Returns the access token directly in the URL fragment (#access_token=...) after user consent. No back-channel exchange.
- No client secret required — designed for browser-only apps (SPAs)
- Less secure — token is exposed in the browser
- Deprecated — replaced by Authorization Code Flow with PKCE
Resource Owner Password Credentials
The user gives their username and password directly to the Client, which exchanges them for a token:
POST /token HTTP/1.1Host: login.yahoo.comContent-Type: application/x-www-form-urlencoded
grant_type=password&username=user@yahoo.com&password=hunter2&client_id=gmail-app-id- Only for trusted first-party apps (e.g., Yahoo’s own mobile app)
- Defeats the purpose of OAuth for third-party apps
- Deprecated in OAuth 2.1
Client Credentials
No user involved — the Client authenticates as itself:
POST /token HTTP/1.1Host: login.yahoo.comContent-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_id=my-service-id&client_secret=MY_SECRET- Machine-to-machine communication (microservices, cron jobs, CI/CD)
- No user context — the token represents the application, not a user
OpenID Connect (OIDC)
OAuth 2.0 handles authorization but doesn’t define a standard way to get user identity. OpenID Connect (OIDC) is a thin identity layer built on top of OAuth 2.0 that adds authentication.
What OIDC Adds
| OAuth 2.0 only | With OpenID Connect |
|---|---|
| Access Token (opaque) | Access Token + ID Token (JWT) |
| No standard user info | /userinfo endpoint |
| Custom scopes per provider | Standard scopes: openid, profile, email |
| Authorization only | Authentication + Authorization |
The ID Token
The ID Token is a JSON Web Token (JWT) that contains claims about the authenticated user:
{ "iss": "https://login.yahoo.com", "sub": "user-12345", "aud": "gmail-app-id", "exp": 1711036800, "iat": 1711033200, "email": "user@yahoo.com", "name": "Jane Doe"}Key fields:
iss(issuer) — who issued this tokensub(subject) — unique user identifieraud(audience) — which Client this token is forexp/iat— expiration and issued-at timestamps
OIDC Scopes
To trigger OIDC, the Client includes openid in the scopes:
GET /authorize? response_type=code &client_id=gmail-app-id &scope=openid profile email &redirect_uri=https://gmail.com/callbackHost: login.yahoo.com| Scope | Returns |
|---|---|
openid | Required — enables OIDC, returns sub claim |
profile | Name, picture, locale, etc. |
email | Email address and verification status |
address | Postal address |
phone | Phone number |
OAuth 2.0 vs OpenID Connect
Think of it this way:
- OAuth 2.0 = “Gmail is allowed to read your Yahoo contacts” (authorization)
- OpenID Connect = “This person is jane@yahoo.com” (authentication)
OIDC uses the same Authorization Code Flow — it just adds openid to the scopes and returns an ID Token alongside the access token.
Which Flow Should You Use?
| Application Type | Recommended Flow | Why |
|---|---|---|
| Server-side web app | Authorization Code | Has a secure back-end for secrets |
| Single-page app (SPA) | Authorization Code + PKCE | No client secret; PKCE prevents code interception |
| Native/mobile app | Authorization Code + PKCE | Same as SPA — no safe place for secrets |
| Machine-to-machine | Client Credentials | No user interaction needed |
| First-party trusted app | Resource Owner Password (legacy) | Avoid if possible; use Auth Code + PKCE instead |
PKCE (Proof Key for Code Exchange, pronounced “pixy”) extends the Authorization Code Flow for public clients that can’t keep a client_secret. The Client generates a random code_verifier, hashes it into a code_challenge, and sends the challenge with the authorization request. When exchanging the code for a token, it sends the original verifier — only the legitimate Client can match the challenge.
Access Control Models
Once a user is authenticated and authorized, you still need to decide what they can do within your application. Here are three common models:
- RBAC (Role-Based Access Control) — users are assigned roles (
admin,editor,viewer), and each role has a fixed set of permissions. Simple and widely used. - ABAC (Attribute-Based Access Control) — decisions are based on attributes of the user, resource, and environment (e.g., “allow if user.department == resource.department AND time < 17:00”). More flexible but more complex.
- PBAC (Policy-Based Access Control) — centralized policies (often written in a policy language like Rego or Cedar) evaluate access requests. Combines the flexibility of ABAC with maintainability.
Most applications start with RBAC and evolve toward ABAC or PBAC as requirements grow more complex.