OAuth 2.0 and OpenID Connect Explained: Modern Web Authentication Architecture
Authentication vs Authorization
| Concept | Question | Protocol |
|---|---|---|
| Authentication | Who are you? | OpenID Connect (OIDC) |
| Authorization | What can you do? | OAuth 2.0 |
OAuth 2.0 only handles authorization—it lets third-party apps access user resources without telling the app "who the user is." OIDC adds an identity layer on top of OAuth 2.0, providing user identity via ID Token.
Four OAuth 2.0 Grant Types
1. Authorization Code (Most Secure, Preferred for Web Apps)
User → clicks "Sign in with GitHub"
→ redirects to GitHub authorization page
→ user grants consent
→ GitHub returns authorization_code
→ backend exchanges code for access_token
→ backend calls GitHub API with token
2. PKCE Extension (Essential for SPAs and Mobile)
Front-end apps cannot safely store client_secret. PKCE uses a dynamic code_verifier to prevent authorization code interception:
Client generates code_verifier + code_challenge
→ authorization request includes code_challenge
→ token exchange submits code_verifier
→ server verifies challenge === SHA256(verifier)
3. Client Credentials (Service-to-Service)
Service A → obtains token with client_id + client_secret
→ calls Service B API
4. Implicit Flow (Deprecated)
token returned directly via URL fragment → use Authorization Code + PKCE instead.
Token Types Compared
| Token | Format | Purpose | Lifetime |
|---|---|---|---|
| Authorization Code | Random string | One-time, exchanged for token | 10 minutes |
| Access Token | JWT or opaque | Access protected resources | 15-60 minutes |
| Refresh Token | Random string | Obtain new Access Token | 7-30 days |
| ID Token | JWT | User identity (OIDC) | Same as Access Token |
Use the JWT Decoder tool to inspect Access Token and ID Token payloads.
OIDC Identity Layer
ID Token is a JWT containing user identity claims:
{
"iss": "https://accounts.google.com",
"sub": "1234567890",
"aud": "your-client-id",
"exp": 1717200000,
"email": "user@example.com",
"name": "张三",
"picture": "https://..."
}
| Claim | Meaning |
|---|---|
iss |
Identity Provider (IdP) |
sub |
Unique user identifier |
aud |
Client ID |
email |
User email |
name |
Display name |
Security Best Practices
1. Always Verify Tokens on the Backend
// ❌ Decode JWT on frontend and trust the user is authenticated
const payload = decodeJwt(token);
if (payload.exp > Date.now()) { /* trust */ }
// ✅ Backend verifies signature + all claims
const verified = await verifyJwt(token, publicKey, {
issuer: 'https://accounts.google.com',
audience: 'your-client-id',
});
2. State Parameter Against CSRF
const state = crypto.randomUUID();
sessionStorage.setItem('oauth_state', state);
// Add &state=${state} to authorization URL
// Verify state matches on callback
3. Token Storage
| Environment | Recommended Approach |
|---|---|
| Web backend | HttpOnly Cookie + Secure + SameSite |
| SPA | In-memory storage + Refresh Token rotation |
| Mobile | Secure storage (Keychain/Keystore) |
Debugging OAuth Flows
- Check Authorization URL parameters (client_id, redirect_uri, scope, state)
- Use JWT Decoder to inspect Access Token scope and exp
- Verify redirect_uri matches registered value exactly
- Check CORS configuration allows token endpoint
Summary
OAuth 2.0 + OIDC is the foundation of modern web authentication. Understanding authorization code flow, PKCE, token type differences, and the security principle that "frontend decode ≠ backend verification" is essential knowledge for every full-stack developer.
Try these browser-local tools — no sign-up required →