← Back to Home

OAuth 2.0 and OIDC — The Visual Guide You Actually Need

Visual walkthrough of OAuth 2.0 and OpenID Connect. Understand authorization flows, JWT structure, grant types, and common security mistakes through animated diagrams.

Every developer uses OAuth. Very few actually understand it. You copy the library config from a blog post, it works, and you move on. Until it breaks — and then you’re staring at redirect loops, invalid_grant errors, and token validation failures with zero mental model of what’s happening.

Let’s fix that. From the ground up.

1. The Authorization Code Flow

This is the flow that 90% of web applications should use. It’s the most secure because the access token never touches the browser — it’s exchanged server-to-server using the authorization code.

Authorization Code Flow — Step by Step

The most common and most secure flow for web applications

1
User → Your AppClicks "Login with Google"
2
Your App → Auth Server302 redirect to /authorize with client_id, redirect_uri, scope, state
3
Auth Server → UserShows login + consent screen
4
Auth Server → Your App302 redirect back with authorization code + state
5
Your App (backend) → Auth ServerPOST /token with code + client_secret (server-to-server)
6
Auth Server → Your AppReturns access_token + id_token + refresh_token

Key insight: the authorization code is short-lived (usually 30 seconds) and single-use. Even if someone intercepts it, they can’t exchange it without the client_secret (which only your backend knows). That’s the security model — separate the user-facing redirect from the secret exchange.

2. OAuth vs OIDC — Finally Clear

People use “OAuth” and “OIDC” interchangeably. They’re not the same thing. OAuth handles authorization (permissions). OIDC handles authentication (identity). You usually want both — and OIDC gives you both in one flow.

OAuth 2.0 vs OpenID Connect

OAuth is Authorization (what can you access). OIDC adds Authentication (who are you).

OAuth 2.0
PurposeDelegated authorization
Question"Can this app access my data?"
Tokenaccess_token (opaque or JWT)
User infoNot included — need separate /userinfo call
Standard claimsNone — provider-specific
Use caseAPI access, third-party integrations
OpenID Connect
PurposeAuthentication + authorization
Question"Who is this user?"
Tokenid_token (always JWT) + access_token
User infoEmbedded in id_token claims
Standard claimssub, email, name, picture, etc.
Use caseLogin/SSO, identity verification
OIDC is a layer ON TOP of OAuth 2.0. Every OIDC flow is also a valid OAuth flow — it just adds the id_token and standardized identity claims.

The practical difference: if you just need “can this app read my Google Calendar?” — that’s pure OAuth 2.0 with an access_token. If you need “who is this person logging into my app?” — that’s OIDC with an id_token. Most login flows use OIDC because you need the user’s identity.

3. JWT Anatomy

The id_token (and often the access_token) is a JSON Web Token. It looks like random characters, but it’s just Base64-encoded JSON with a cryptographic signature. Understanding the structure makes debugging “invalid token” errors much easier.

Inside a JWT — Three Parts, Dot-Separated

Header .
Payload .
Signature
Header (Base64)
"alg": "RS256",
"typ": "JWT"
Tells the server which algorithm to use for signature verification.
Payload (Claims)
"sub": "user_123",
"email": "jas@example.com",
"iat": 1716239022,
"exp": 1716242622,
"iss": "https://auth.example.com"
Claims are NOT encrypted. Anyone can decode Base64. Never put secrets here.
Signature
HMACSHA256(
  base64(header) + "." + base64(payload),
  secret_key
)
Proves the token wasn't tampered with. Server verifies using the same key (HMAC) or public key (RSA/ECDSA).

Critical security point: JWTs are signed, not encrypted. Anyone can decode the payload with Base64. Don’t put sensitive data in JWT claims — put identifiers (user_id) not secrets (SSN, credit card). If you need encrypted tokens, use JWE (JSON Web Encryption), but that’s rare.

4. Grant Types — Picking the Right One

OAuth 2.0 defines multiple “grant types” — different flows for different situations. The wrong choice creates security vulnerabilities. The right choice depends on whether you have a backend, what device you’re on, and whether a human is involved.

Which Grant Type Should You Use?

Authorization Code + PKCERecommended

The gold standard. Works for web apps, mobile apps, SPAs. PKCE adds security for public clients (no client_secret stored on device).

When: Any interactive user login. Always start here.
Client CredentialsMachine-to-Machine

No user involved. Service A authenticates to Service B using its own credentials. Perfect for backend microservice communication.

When: Cron jobs, background workers, internal APIs.
Device AuthorizationLimited Input

For devices without a browser (smart TVs, CLI tools, IoT). Shows a code on device, user enters it on their phone/laptop.

When: TV apps, CLI auth (gh auth login), smart devices.
Implicit (DEPRECATED)Don't Use

Returns token directly in URL fragment. No code exchange. Vulnerable to token leakage via browser history, referrer headers. Replaced by Auth Code + PKCE.

When: Never. Legacy only. Migrate away.
Resource Owner PasswordDon't Use

User gives their password directly to the app. Defeats the entire purpose of OAuth (delegated auth without sharing credentials). Only for legacy migration.

When: Never in greenfield. Only legacy systems being migrated.

The decision tree is simple: human user login? Authorization Code + PKCE. Machine-to-machine? Client Credentials. No browser (TV/CLI)? Device Authorization. That’s it. The other grants are deprecated — don’t use them in new projects.

5. Security Mistakes

I’ve audited dozens of OAuth implementations. The same five mistakes appear in almost every one. None of them are caught by automated testing — they’re logic bugs in the authentication flow that create exploitable vulnerabilities.

5 Mistakes That Get You Hacked

Not validating the `state` parameterEnables CSRF attacks. Attacker crafts a link that logs victim into attacker's account.Generate random state before redirect, verify it matches on callback.
Storing tokens in localStorageXSS = game over. Any script on the page can read localStorage and exfiltrate tokens.Use httpOnly secure cookies. Or token stored in memory + refresh via httpOnly cookie.
Not checking `iss` and `aud` claimsAttacker can use a valid token from a different app/issuer against your API.Always verify issuer matches your auth server and audience matches your client_id.
Long-lived access tokensIf leaked, attacker has access for hours/days. No way to revoke without a blocklist.Short-lived access tokens (5-15 min) + refresh tokens. Rotation on every refresh.
Open redirect_uriIf redirect_uri isn't strictly validated, attacker redirects auth code to their server.Exact match only. No wildcards. Register every valid redirect URI explicitly.

The meta-lesson: OAuth security isn’t about the protocol being insecure. The protocol is well-designed. The vulnerabilities come from implementation shortcuts — skipping state validation, storing tokens insecurely, accepting any redirect_uri. Follow the spec exactly. Every “optional” security feature in the spec exists because someone got hacked without it.