Audience
This guide is for anyone who needs to understand OAuth 2.0 and OpenID Connect conceptually:
- Backend developers implementing authentication and authorization in APIs
- Frontend developers integrating “Login with Google/GitHub/etc.” buttons
- Security engineers reviewing OAuth implementations for vulnerabilities
- Technical architects choosing between authentication strategies
- Product managers understanding SSO and third-party integrations
Basic understanding of HTTP and APIs is helpful but not required. We’ll build from first principles.
Goal
After reading this guide, you’ll understand:
- What problem OAuth 2.0 solves (and what it doesn’t)
- The four roles in every OAuth interaction
- How the main OAuth flows work (Authorization Code, Client Credentials)
- Why PKCE exists and what it protects against
- The difference between OAuth 2.0 and OpenID Connect
- When to use ID tokens vs. access tokens
- How to choose the right flow for your application
You won’t be ready to implement OAuth securely (that requires deeper study), but you’ll have a clear mental model of how it all fits together.
1. The Problem OAuth Solves
Imagine you’re building a photo printing service. Users want to print photos from their Google Drive. The naive solution: ask users for their Google password.
This is terrible for multiple reasons:
- Trust: Users must trust you with their Google credentials
- Access scope: You get full access to their Google account, not just photos
- Revocation: If users want to stop using your service, they must change their Google password
- Security: You now store passwords for accounts you don’t own
OAuth solves this by enabling delegated authorization:
“Let users grant your application limited access to their resources on another service, without sharing their credentials.”
graph LR
A[Photo Printing App] -->|"Can I access
Alice's photos?"| B[Google]
B -->|"Alice, do you
approve this?"| C[Alice]
C -->|"Yes, only
read photos"| B
B -->|"Here's a token
for photos only"| A
style A fill:#e3f2fd
style B fill:#fff3e0
style C fill:#e8f5e9Key insight: OAuth is about authorization (what you can do), not authentication (who you are).
What OAuth is NOT
OAuth does not tell you:
- Who the user is
- The user’s email or name
- Whether the user is logged in to their account
OAuth only tells you: “This token grants access to these specific resources.”
This distinction matters. We’ll see how OpenID Connect adds identity later.
2. The Four Roles
Every OAuth interaction involves four distinct roles. Understanding them is essential.
graph TB
subgraph "The Four OAuth Roles"
RO[Resource Owner
The user who owns the data]
C[Client
Your application]
AS[Authorization Server
Issues tokens]
RS[Resource Server
Hosts protected data]
end
RO -->|"Grants permission"| C
C -->|"Requests token"| AS
AS -->|"Issues token"| C
C -->|"Presents token"| RS
RS -->|"Returns data"| C
style RO fill:#e8f5e9
style C fill:#e3f2fd
style AS fill:#fff3e0
style RS fill:#fce4ecResource Owner
The Resource Owner is the user who owns the protected data. In our photo printing example, it’s the person whose Google Drive photos you want to access.
The resource owner can grant or deny access to their resources. They’re the ultimate authority.
Client
The Client is your application—the software that wants to access the resource owner’s data. It could be:
- A web application
- A mobile app
- A desktop application
- A backend service
Important: In OAuth terminology, “client” means your application, not the end user.
Authorization Server
The Authorization Server authenticates the resource owner and issues access tokens to the client. It’s the gatekeeper.
Examples:
- Google’s OAuth server (
accounts.google.com) - GitHub’s OAuth server (
github.com/login/oauth) - Your company’s identity provider
The authorization server handles:
- User authentication (login)
- Consent screens (“Allow this app to…?”)
- Token issuance
- Token validation (sometimes)
Resource Server
The Resource Server hosts the protected resources and accepts access tokens. It’s where the data lives.
Examples:
- Google Drive API
- GitHub API
- Your company’s internal API
In many systems, the authorization server and resource server are the same (or run by the same organization). But conceptually, they’re separate roles.
Role Summary
| Role | Question It Answers | Example |
|---|---|---|
| Resource Owner | Whose data is it? | Alice (the user) |
| Client | What app wants access? | Photo Printing Service |
| Authorization Server | Who issues tokens? | Google OAuth |
| Resource Server | Where is the data? | Google Drive API |
3. Authorization Code Flow
The Authorization Code Flow is the most common and secure OAuth flow. It’s used when your application has a backend server that can securely store secrets.
When to Use It
- Web applications with a server-side component
- Mobile apps (with PKCE—more on this later)
- Any scenario where the client can keep a secret
The Flow Step by Step
sequenceDiagram
participant U as User (Browser)
participant C as Client (Your App)
participant AS as Authorization Server
participant RS as Resource Server
U->>C: 1. Click "Login with Google"
C->>U: 2. Redirect to Google
U->>AS: 3. User authenticates & consents
AS->>U: 4. Redirect back with auth code
U->>C: 5. Send auth code to backend
C->>AS: 6. Exchange code for tokens
AS->>C: 7. Return access token (& refresh token)
C->>RS: 8. Request data with access token
RS->>C: 9. Return protected dataLet’s walk through each step:
Step 1-2: Initiate the flow
User clicks “Login with Google” in your app. Your app redirects them to Google’s authorization endpoint:
https://accounts.google.com/o/oauth2/v2/auth?
response_type=code
&client_id=YOUR_CLIENT_ID
&redirect_uri=https://yourapp.com/callback
&scope=https://www.googleapis.com/auth/drive.readonly
&state=xyz123
Key parameters:
response_type=code: We want an authorization codeclient_id: Your app’s identifier (registered with Google)redirect_uri: Where to send the user after authorizationscope: What permissions you’re requestingstate: Random value to prevent CSRF attacks
Step 3: User authenticates and consents
Google shows a login screen (if needed) and a consent screen:
“Photo Printing Service wants to:
- View your Google Drive files
Allow / Deny”
Step 4-5: Authorization code returned
If the user approves, Google redirects back to your redirect_uri:
https://yourapp.com/callback?
code=4/0AX4XfWh...
&state=xyz123
The code is the authorization code—a short-lived, one-time-use token.
Step 6-7: Exchange code for tokens
Your backend server (not the browser!) exchanges the code for tokens:
POST /token HTTP/1.1
Host: oauth2.googleapis.com
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=4/0AX4XfWh...
&redirect_uri=https://yourapp.com/callback
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET
Google responds with:
{
"access_token": "ya29.a0AfH6SM...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "1//0g..."
}
Step 8-9: Access protected resources
Your app uses the access token to call Google’s API:
GET /drive/v3/files HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer ya29.a0AfH6SM...
Why the Extra Step?
Why not just return the access token directly? Why the code exchange?
Security. The authorization code passes through the user’s browser (in the URL). If we returned the access token there:
- It would be visible in browser history
- It could be logged by proxies
- Malicious browser extensions could steal it
By requiring a backend exchange with the client_secret, we ensure:
- Only your server can get the access token
- The token never touches the browser
- Even if someone steals the code, they can’t use it without your secret
4. Client Credentials Flow
The Client Credentials Flow is simpler—it’s for machine-to-machine communication where no user is involved.
When to Use It
- Backend services calling other backend services
- Scheduled jobs accessing APIs
- Any scenario where there’s no human user
The Flow
sequenceDiagram
participant C as Client (Backend Service)
participant AS as Authorization Server
participant RS as Resource Server
C->>AS: 1. Request token with client credentials
AS->>C: 2. Return access token
C->>RS: 3. Request data with access token
RS->>C: 4. Return dataStep 1-2: Request token
Your service authenticates directly with its credentials:
POST /token HTTP/1.1
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic BASE64(client_id:client_secret)
grant_type=client_credentials
&scope=api.read api.write
Response:
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600
}
Step 3-4: Use token
Same as before—include the token in API requests.
No Resource Owner
Notice there’s no user in this flow. The client acts on its own behalf, not on behalf of a user. The access token represents the client’s permissions, not a user’s.
This is appropriate for:
- Microservices communication
- Batch processing jobs
- Admin tools
5. Implicit Flow (Deprecated)
You might encounter the Implicit Flow in older documentation. It was designed for browser-based apps that couldn’t securely store secrets.
graph LR
A[Browser App] -->|"Request"| B[Auth Server]
B -->|"Token in URL fragment"| A
style A fill:#ffcdd2
style B fill:#ffcdd2Why it’s deprecated:
- Access token exposed in browser URL
- Vulnerable to token leakage
- No refresh tokens
- PKCE provides a better solution
Don’t use Implicit Flow for new applications. Use Authorization Code Flow with PKCE instead.
6. PKCE Explained
PKCE (Proof Key for Code Exchange, pronounced “pixy”) is an extension that makes the Authorization Code Flow secure for public clients—applications that can’t keep a secret.
The Problem PKCE Solves
Mobile apps and single-page applications (SPAs) can’t securely store a client_secret. Anyone can:
- Decompile a mobile app
- View JavaScript source code
Without a secret, what stops an attacker from intercepting the authorization code and exchanging it for tokens?
The Solution: Code Verifier and Challenge
PKCE adds a dynamic secret for each authorization request:
sequenceDiagram
participant C as Client
participant AS as Auth Server
Note over C: Generate random
code_verifier
Note over C: Create code_challenge
= SHA256(code_verifier)
C->>AS: 1. Auth request + code_challenge
Note over AS: Store code_challenge
AS->>C: 2. Return authorization code
C->>AS: 3. Token request + code_verifier
Note over AS: Verify:
SHA256(code_verifier)
== stored challenge?
AS->>C: 4. Return tokensHow it works:
Client generates a secret (code_verifier): A random string, e.g.,
dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXkClient creates a challenge:
code_challenge = BASE64URL(SHA256(code_verifier))Authorization request includes the challenge:
https://auth.example.com/authorize?
response_type=code
&client_id=YOUR_CLIENT_ID
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256
...
- Token exchange includes the original verifier:
POST /token
grant_type=authorization_code
&code=AUTH_CODE
&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
- Server verifies: SHA256(code_verifier) must match the stored challenge
Why This Works
Even if an attacker intercepts the authorization code:
- They don’t have the
code_verifier - The challenge was hashed, so they can’t reverse it
- They can’t complete the token exchange
PKCE should be used for all authorization code flows, not just public clients. It’s a defense-in-depth measure.
7. OpenID Connect: Identity Layer
OAuth 2.0 answers: “Can this app access these resources?”
But it doesn’t answer: “Who is the user?”
OpenID Connect (OIDC) is a layer on top of OAuth 2.0 that adds identity. It answers: “Who just logged in?”
graph TB
subgraph "The Relationship"
OAuth[OAuth 2.0
Authorization]
OIDC[OpenID Connect
Identity + Authorization]
end
OAuth -->|"Built on top of"| OIDC
style OAuth fill:#fff3e0
style OIDC fill:#e3f2fdWhat OIDC Adds
- ID Token: A JWT containing user identity claims
- UserInfo Endpoint: An API to fetch user profile data
- Standard Scopes:
openid,profile,email - Standard Claims:
sub,name,email,picture
When to Use OIDC
- “Login with Google/GitHub/Facebook”
- Single Sign-On (SSO)
- Any time you need to know WHO the user is
When Plain OAuth is Enough
- Accessing user resources (photos, files, repos)
- Machine-to-machine communication
- Any time you only need access, not identity
8. ID Token vs. Access Token
OIDC introduces a new token type: the ID Token. Understanding the difference between ID tokens and access tokens is crucial.
Access Token
Purpose: Grants access to resources
Audience: Resource servers (APIs)
Contents: Can be opaque (random string) or a JWT with authorization claims
Example use:
GET /api/photos
Authorization: Bearer ya29.a0AfH6SM...
Who validates it: The resource server
ID Token
Purpose: Proves user identity
Audience: Your application (the client)
Contents: Always a JWT with identity claims
Example decoded:
{
"iss": "https://accounts.google.com",
"sub": "110169484474386276334",
"aud": "YOUR_CLIENT_ID",
"exp": 1704067200,
"iat": 1704063600,
"email": "[email protected]",
"name": "Alice Smith",
"picture": "https://lh3.googleusercontent.com/..."
}
Who validates it: Your application
Key Differences
| Aspect | Access Token | ID Token |
|---|---|---|
| Purpose | Access resources | Identify user |
| Audience | Resource server | Client application |
| Format | Opaque or JWT | Always JWT |
| Sent to | APIs | Never sent to APIs |
| Validity | Minutes to hours | Short-lived |
Common Mistake
Never send ID tokens to APIs as authorization.
ID tokens are for your application to know who logged in. Access tokens are for APIs to authorize requests.
# WRONG - ID token to API
GET /api/data
Authorization: Bearer eyJhbGciOiJSUzI1NiIs... (ID token)
# CORRECT - Access token to API
GET /api/data
Authorization: Bearer ya29.a0AfH6SM... (Access token)
9. Choosing the Right Flow
Here’s a decision tree for choosing the right OAuth flow:
flowchart TD
A[Start] --> B{Is there a user?}
B -->|No| C[Client Credentials Flow]
B -->|Yes| D{Can your app
keep a secret?}
D -->|Yes| E[Authorization Code Flow]
D -->|No| F[Authorization Code Flow
+ PKCE]
E --> G{Need identity?}
F --> G
G -->|Yes| H[Add OpenID Connect]
G -->|No| I[Plain OAuth 2.0]
style C fill:#fff3e0
style E fill:#e3f2fd
style F fill:#e8f5e9
style H fill:#fce4ecSummary Table
| Scenario | Flow | OIDC? |
|---|---|---|
| Web app with backend | Authorization Code | Optional |
| Single-page app (SPA) | Authorization Code + PKCE | Usually yes |
| Mobile app | Authorization Code + PKCE | Usually yes |
| Backend service | Client Credentials | No |
| “Login with X” button | Authorization Code (+ PKCE) | Yes |
Modern Recommendations
- Always use PKCE for authorization code flows, even if you have a backend
- Never use Implicit Flow for new applications
- Use OIDC when you need to know who the user is
- Use plain OAuth when you only need to access resources
10. What’s Next
You now understand the conceptual foundation of OAuth 2.0 and OpenID Connect. You know:
- Why OAuth exists (delegated authorization)
- The four roles (resource owner, client, authorization server, resource server)
- How the main flows work (authorization code, client credentials)
- Why PKCE exists (security for public clients)
- The difference between OAuth and OIDC (authorization vs. identity)
- When to use ID tokens vs. access tokens
What This Guide Didn’t Cover
This guide intentionally avoided:
- Implementation details: How to securely implement OAuth in your language/framework
- Common vulnerabilities: CSRF, token leakage, redirect URI manipulation
- Token storage: Where and how to securely store tokens
- Refresh token rotation: Handling token expiration securely
- Advanced flows: Device Authorization, Token Exchange
These topics require deeper study and hands-on practice.
Recommended Next Steps
Hands-on learning:
- Try implementing OAuth with a provider (Google, GitHub, Auth0)
- Use a well-tested library (never roll your own OAuth)
- Study the RFC specifications (listed below)
Course Preview:
OAuth 2.0 Done Right (and Wrong)
A practical course covering:
- Secure implementation patterns
- Real-world attack scenarios
- Token storage strategies
- Common misconfigurations
- Debugging OAuth flows
Coming soon to API/OS
Related Vocabulary Terms
Deepen your understanding by exploring these terms:
- OAuth 2.0 - The authorization framework in detail
- OpenID Connect - Identity layer specification
- JWT - Token format used by OIDC and many OAuth implementations
- Bearer Token - How access tokens are transmitted
- Authorization - What you can do with resources
- Authentication - Proving who you are
Standards & RFCs
- RFC 6749 - OAuth 2.0 Authorization Framework
- RFC 6750 - Bearer Token Usage
- RFC 7636 - PKCE (Proof Key for Code Exchange)
- OpenID Connect Core 1.0 - Identity layer specification
You’ve built a solid mental model of OAuth 2.0 and OpenID Connect. The next step is hands-on implementation—but now you’ll understand what you’re implementing and why.