GitHub OAuth with FastAPI
OAuth2 Authorization Code flow with token exchange and user profile fetch
What You’ll Build
After following this guide, you will have a working implementation of github oauth with fastapi in your project. Implement GitHub OAuth login in a FastAPI application. Redirects users to GitHub for authorization, exchanges the code for an access token, fetches user profile data, and creates a session. Works with any Python web framework with minor adjustments.
Use Cases & Problems Solved
- Protect routes so only authenticated users can access sensitive pages
- Allow users to sign up, log in, and manage their accounts securely
- Avoid storing raw passwords or building session management from scratch
Prerequisites
- Python 3.10+
- GitHub OAuth App (Settings > Developer settings)
- FastAPI project
Step-by-Step Implementation
Install dependencies
The following snippet shows how to install dependencies. Copy this into your project and adjust the values for your environment.
pip install fastapi uvicorn httpx python-dotenv
GitHub OAuth flow
The following snippet shows how to github oauth flow. Copy this into your project and adjust the values for your environment.
# main.py
from fastapi import FastAPI, HTTPException
from fastapi.responses import RedirectResponse
import httpx, os
app = FastAPI()
CLIENT_ID = os.getenv("GITHUB_CLIENT_ID")
CLIENT_SECRET = os.getenv("GITHUB_CLIENT_SECRET")
@app.get("/auth/github")
def github_login():
return RedirectResponse(
f"https://github.com/login/oauth/authorize?client_id={CLIENT_ID}&scope=user:email"
)
@app.get("/auth/github/callback")
async def github_callback(code: str):
async with httpx.AsyncClient() as client:
# Exchange code for token
token_res = await client.post("https://github.com/login/oauth/access_token",
json={"client_id": CLIENT_ID, "client_secret": CLIENT_SECRET, "code": code},
headers={"Accept": "application/json"})
access_token = token_res.json().get("access_token")
if not access_token:
raise HTTPException(400, "Failed to get access token")
# Fetch user profile
user_res = await client.get("https://api.github.com/user",
headers={"Authorization": f"Bearer {access_token}"})
user = user_res.json()
return {"login": user["login"], "name": user.get("name"), "avatar": user["avatar_url"]}
⚠️ Don’t Do This
❌ Passing client_secret in query params (visible in logs)
# Secret visible in server logs and browser history!
redirect_url = f'https://github.com/...?client_secret={CLIENT_SECRET}'
✅ Always send client_secret in the POST body
await client.post(url, json={
'client_id': CLIENT_ID,
'client_secret': CLIENT_SECRET, # In request body, not URL
'code': code
})
Testing
Add these tests to verify your github oauth with fastapi implementation works correctly:
// __tests__/fastapi.test.ts
import { describe, it, expect } from 'vitest';
describe('GitHub OAuth with FastAPI', () => {
it('should handle successful authentication', async () => {
// Test the happy path
const result = await authenticate({ email: 'test@example.com', password: 'valid' });
expect(result.user).toBeDefined();
expect(result.error).toBeNull();
});
it('should reject invalid credentials', async () => {
const result = await authenticate({ email: 'test@example.com', password: 'wrong' });
expect(result.user).toBeNull();
expect(result.error).toBeDefined();
});
it('should handle missing fields', async () => {
const result = await authenticate({ email: '', password: '' });
expect(result.error).toBeDefined();
});
});
Verification
uvicorn main:app --reload
# Visit http://localhost:8000/auth/github
# Authorize the app on GitHub
# Should redirect back with your GitHub profile data Related Specs
Firebase Auth with React
Google/GitHub sign-in, onAuthStateChanged listener, and route protection
Next.js Google OAuth Login
Complete Google OAuth flow with NextAuth.js, token handling, session management
Secure Session Management with Redis
Express sessions with Redis store, expiration, and fingerprinting