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

Related Articles