What is OAuth 2.0?
OAuth 2.0 is an authorization framework that allows applications to obtain limited access to user accounts on other services. It's what powers "Login with Google," "Login with GitHub," and similar features.
Think of OAuth like a valet key for your car. Instead of giving someone your master key (password), you give them a special key that only opens the door and starts the engine - but can't open the trunk or glove box. OAuth lets you grant limited access to your data without sharing your password.
Authentication vs Authorization
These terms are often confused:
- Authentication (AuthN): Who are you? Proving your identity.
- Authorization (AuthZ): What can you do? Granting permissions.
OAuth 2.0 is primarily about authorization - granting access to resources. OpenID Connect (OIDC), built on top of OAuth 2.0, adds authentication.
Traditional Login:
Username: john@example.com
Password: ********
→ Authentication (proves identity)
OAuth 2.0:
"App X wants to access your Google Calendar"
[Allow] [Deny]
→ Authorization (grants permission)
OAuth 2.0 Roles
- Resource Owner: The user who owns the data (you!)
- Client: The application requesting access (your app)
- Authorization Server: Verifies identity and issues tokens (Google, GitHub)
- Resource Server: Hosts the protected data (Google Calendar API)
Example: App wants to read your Google Calendar
Resource Owner: You (the user)
Client: "Calendar Widget" app
Authorization Server: Google's OAuth server
Resource Server: Google Calendar API
Flow:
1. App asks you for permission
2. You approve on Google's site
3. Google gives app a token
4. App uses token to access your calendar
OAuth 2.0 Flows (Grant Types)
Different scenarios require different flows:
1. Authorization Code Flow (Most Common)
Used by web applications with a backend server. Most secure for web apps.
┌──────────┐ ┌─────────────────┐
│ User │ │ Authorization │
│ (Browser)│ │ Server │
└────┬─────┘ └────────┬────────┘
│ │
│ 1. Click "Login with Google" │
│ ────────────────────────────────────────────>
│ │
│ 2. Show Google login page │
│ <────────────────────────────────────────────
│ │
│ 3. User logs in and approves │
│ ────────────────────────────────────────────>
│ │
│ 4. Redirect with authorization code │
│ <────────────────────────────────────────────
│ (code=abc123) │
│ │
┌────┴─────┐ │
│ Client │ 5. Exchange code for tokens │
│ (Server) │ ──────────────────────────────────────>
│ │ │
│ │ 6. Return access_token + refresh_token
│ │ <──────────────────────────────────────
└──────────┘
2. Authorization Code + PKCE
Enhanced security for mobile apps and SPAs. Prevents code interception attacks.
3. Client Credentials Flow
For server-to-server communication. No user involved.
4. Implicit Flow (Deprecated)
Was used for SPAs. Now replaced by Authorization Code + PKCE.
OAuth 2.0 Tokens
Access Token
Used to access protected resources. Short-lived (minutes to hours).
Refresh Token
Used to get new access tokens. Long-lived (days to months).
ID Token (OpenID Connect)
Contains user identity information. Used for authentication.
# Token Response Example
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "dGhpcyBpcyBhIHJlZnJlc2g...",
"scope": "read:profile read:email",
"id_token": "eyJhbGciOiJSUzI1NiIs..." // OpenID Connect
}
Implementing OAuth 2.0 in Python
Example: Google OAuth with FastAPI
# Install: pip install fastapi authlib httpx python-dotenv
from fastapi import FastAPI, Request
from fastapi.responses import RedirectResponse
from authlib.integrations.starlette_client import OAuth
from starlette.middleware.sessions import SessionMiddleware
import os
app = FastAPI()
app.add_middleware(SessionMiddleware, secret_key="your-secret-key")
# Configure OAuth
oauth = OAuth()
oauth.register(
name='google',
client_id=os.getenv('GOOGLE_CLIENT_ID'),
client_secret=os.getenv('GOOGLE_CLIENT_SECRET'),
server_metadata_url='https://accounts.google.com/.well-known/openid-configuration',
client_kwargs={'scope': 'openid email profile'}
)
@app.get('/')
def home():
return {'message': 'Go to /login to authenticate'}
@app.get('/login')
async def login(request: Request):
redirect_uri = request.url_for('auth_callback')
return await oauth.google.authorize_redirect(request, redirect_uri)
@app.get('/auth/callback')
async def auth_callback(request: Request):
token = await oauth.google.authorize_access_token(request)
user_info = token.get('userinfo')
# Here you would:
# 1. Find or create user in your database
# 2. Create your own session/JWT
# 3. Redirect to your app
return {
'email': user_info['email'],
'name': user_info['name'],
'picture': user_info['picture']
}
@app.get('/logout')
def logout(request: Request):
request.session.clear()
return RedirectResponse(url='/')
OAuth 2.0 with GitHub
# GitHub OAuth Example
import httpx
from fastapi import FastAPI, Request
from fastapi.responses import RedirectResponse
import os
app = FastAPI()
GITHUB_CLIENT_ID = os.getenv('GITHUB_CLIENT_ID')
GITHUB_CLIENT_SECRET = os.getenv('GITHUB_CLIENT_SECRET')
REDIRECT_URI = 'http://localhost:8000/auth/github/callback'
@app.get('/login/github')
def login_github():
# Step 1: Redirect to GitHub authorization
github_auth_url = (
f"https://github.com/login/oauth/authorize"
f"?client_id={GITHUB_CLIENT_ID}"
f"&redirect_uri={REDIRECT_URI}"
f"&scope=read:user user:email"
)
return RedirectResponse(url=github_auth_url)
@app.get('/auth/github/callback')
async def github_callback(code: str):
# Step 2: Exchange code for access token
async with httpx.AsyncClient() as client:
token_response = await client.post(
'https://github.com/login/oauth/access_token',
data={
'client_id': GITHUB_CLIENT_ID,
'client_secret': GITHUB_CLIENT_SECRET,
'code': code,
'redirect_uri': REDIRECT_URI
},
headers={'Accept': 'application/json'}
)
token_data = token_response.json()
access_token = token_data['access_token']
# Step 3: Use token to get user info
user_response = await client.get(
'https://api.github.com/user',
headers={'Authorization': f'Bearer {access_token}'}
)
user = user_response.json()
return {
'github_id': user['id'],
'username': user['login'],
'name': user['name'],
'avatar': user['avatar_url']
}
Scopes: Limiting Access
Scopes define what permissions the app is requesting:
# Google Scopes
openid # Basic authentication
email # Read email address
profile # Read name and picture
https://www.googleapis.com/auth/calendar.readonly # Read calendar
https://www.googleapis.com/auth/drive.file # Access Drive files
# GitHub Scopes
read:user # Read user profile
user:email # Read email
repo # Full access to repositories
public_repo # Access public repos only
# Request Example
scope=openid+email+profile # Request multiple scopes
Always request the minimum scopes needed. Users are more likely to approve limited access.
Common OAuth 2.0 Providers
- Google: Most widely used, supports OpenID Connect
- GitHub: Popular for developer tools
- Facebook: Social login
- Microsoft: Enterprise and consumer
- Twitter/X: Social media integration
- Apple: iOS apps (Sign in with Apple)
- Auth0: Identity as a service
OAuth 2.0 Security Best Practices
- Use HTTPS: Always. Never transmit tokens over HTTP.
- Use PKCE: For all public clients (mobile, SPA).
- Validate redirect URIs: Only allow registered URIs.
- Use state parameter: Prevents CSRF attacks.
- Store tokens securely: Never in localStorage, use httpOnly cookies.
- Short token lifetimes: Use refresh tokens for long sessions.
- Validate tokens: Check signature, expiration, audience.
# State parameter prevents CSRF
import secrets
@app.get('/login')
def login(request: Request):
state = secrets.token_urlsafe(32)
request.session['oauth_state'] = state
auth_url = f"{AUTH_URL}?client_id={CLIENT_ID}&state={state}&..."
return RedirectResponse(auth_url)
@app.get('/callback')
def callback(request: Request, state: str, code: str):
# Verify state matches
if state != request.session.get('oauth_state'):
raise HTTPException(400, "Invalid state parameter")
# Continue with token exchange...
OpenID Connect (OIDC)
OIDC is an identity layer built on OAuth 2.0. While OAuth 2.0 handles authorization, OIDC adds authentication.
OAuth 2.0: "App can access your calendar" (authorization)
OIDC: "App knows you are john@example.com" (authentication)
OIDC adds:
- ID Token (JWT with user identity)
- UserInfo endpoint
- Standard scopes (openid, profile, email)
- Discovery document (.well-known/openid-configuration)
Master OAuth 2.0 with Expert Mentorship
Our Full Stack Python program covers OAuth 2.0 and authentication in depth. Learn to implement secure social logins and API authorization with personalized guidance.
Explore Full Stack Python Program