Add OAuth login to your FastAPI app in one command. 6 providers, PKCE support, and a built-in OAuth debugger.
Project description
OAuth for Dummies
The fastest way to add OAuth login to any FastAPI app. 6 providers, PKCE support, and a built-in OAuth debugger.
Quickstart | Providers | OAuth Debugger | PKCE / OAuth 2.1 | How OAuth Works | CLI Reference | Tutorial
Why this exists
Adding OAuth to a FastAPI app should not take an afternoon. But it does, because:
- The official OAuth 2.0 spec is 76 pages long
- Every tutorial shows a different approach
- Redirect URI mismatches waste hours of debugging
- Production auth libraries are overkill when you just need "Login with GitHub"
oauth-for-dummies solves this. One CLI command drops working OAuth routes into your project. Two lines of code to integrate. Done.
Supported Providers
| Provider | Status | Scopes |
|---|---|---|
| GitHub | Ready | read:user, user:email |
| Ready | openid, email, profile |
|
| Discord | Ready | identify, email |
| Spotify | Ready | user-read-email, user-read-private |
| Microsoft | Ready | openid, email, profile, User.Read |
| Ready | openid, profile, email |
All 6 providers work with both the CLI scaffold (oauth-init) and the tutorial app. Only configure the ones you need -- unconfigured providers are hidden from the UI.
Quickstart
pip install oauth-for-dummies
cd your-fastapi-project
oauth-init
That scaffolds three files into your project:
your-fastapi-project/
oauth_config.py # provider credentials from .env (6 providers)
oauth_routes.py # login, callback, logout endpoints + PKCE support
oauth_example_app.py # working demo app (optional)
.env # template for your OAuth keys
Integrate into your existing FastAPI app with two lines:
from oauth_routes import router as oauth_router
app.include_router(oauth_router)
Run the example to see it work:
pip install fastapi uvicorn httpx python-dotenv
# edit .env with your OAuth credentials
uvicorn oauth_example_app:app --reload
# open http://localhost:8000
OAuth Debugger
The tutorial app includes a built-in OAuth Debugger (Learn Mode) that captures and displays every HTTP request and response in the OAuth flow -- in real time, with real data.
Click Learn Mode next to any provider to see:
- Authorization Request -- the exact URL your app constructs, with every query parameter explained
- Callback -- the authorization code and state token received from the provider, with CSRF verification
- Token Exchange -- the server-to-server POST request body and the token response
- User Info -- the raw API response from the provider's userinfo endpoint
- Normalized Profile -- how your app maps provider-specific fields into a standard shape
Each step includes expandable explanations of what happened and why. No black box -- you see exactly what your code is doing.
git clone https://github.com/pranavkumaarofficial/oauth-for-dummies.git
cd oauth-for-dummies
pip install -e .
cp .env.example .env
# add at least one provider's credentials to .env
uvicorn app.main:app --reload
# open http://localhost:8000 and click "Learn Mode"
PKCE / OAuth 2.1
The project supports PKCE (Proof Key for Code Exchange), the key security improvement in OAuth 2.1. PKCE replaces the client secret with a cryptographic challenge, making OAuth safe for public clients (mobile apps, SPAs).
How it works
Instead of sending client_secret during token exchange, PKCE:
- Generates a random
code_verifier(86 chars) - Hashes it into a
code_challenge(SHA-256, base64url) - Sends the challenge with the authorization request
- Sends the verifier with the token exchange
- The provider verifies
SHA256(verifier) == challenge
Enable PKCE on any provider
In the tutorial app, add one line to any provider class:
class MyProvider(OAuthProvider):
use_pkce = True # that's it
In the scaffold, add the provider key to the PKCE_PROVIDERS set in oauth_routes.py:
PKCE_PROVIDERS = {"my_provider"}
The Learn Mode debugger shows PKCE parameters (code_challenge, code_verifier) when enabled.
What is OAuth 2.0?
OAuth 2.0 is how "Login with Google" works. Instead of giving an app your password, you tell Google: "let this app see my name and email." The app never touches your password. It gets a temporary token instead.
+----------+ +--------------+
| You | "Login with GitHub" ----> | Your App |
| (User) | | (FastAPI) |
+----------+ +------+-------+
|
+---------------------+
v
+---------------+
| GitHub | "Allow this app?"
| OAuth Server | <-- You click "Yes"
+-------+-------+
|
v sends authorization code
+---------------+
| Your App | exchanges code for token
| (server) | uses token to get your profile
+-------+-------+
|
v
You're logged in. No password shared. Ever.
That's the entire OAuth 2.0 Authorization Code flow. This project implements it for you.
How OAuth 2.0 Works
Here's the step-by-step flow that happens when a user clicks "Login with GitHub":
sequenceDiagram
participant User
participant App as Your FastAPI App
participant GitHub as GitHub OAuth
User->>App: Clicks "Login with GitHub"
App->>GitHub: Redirects to /authorize (client_id, scope, state)
GitHub->>User: Shows consent screen
User->>GitHub: Clicks "Authorize"
GitHub->>App: Redirects to /callback?code=abc&state=xyz
App->>App: Verifies state parameter (CSRF protection)
App->>GitHub: POST /access_token (code + client_secret)
GitHub->>App: Returns access_token
App->>GitHub: GET /user (Bearer token)
GitHub->>App: Returns user profile (name, email, avatar)
App->>User: Creates session, shows profile page
Key concepts:
| Concept | What it means |
|---|---|
| Authorization Code | A short-lived, one-time code the provider sends to your app. Not the token itself. |
| Access Token | The actual key your app uses to call the provider's API. Obtained by exchanging the code. |
| State Parameter | A random string your app generates to prevent CSRF attacks. Verified on callback. |
| Scopes | Permissions you request. read:user = profile info, user:email = email address. |
| Redirect URI | The URL the provider sends the user back to. Must match exactly what you registered. |
| PKCE | Proof Key for Code Exchange. Replaces client_secret with a cryptographic challenge. Required in OAuth 2.1. |
Getting OAuth Credentials
GitHub
- Go to github.com/settings/developers
- Click "New OAuth App"
- Set callback URL to
http://localhost:8000/auth/github/callback - Copy Client ID and Client Secret into
.env
- Go to console.cloud.google.com/apis/credentials
- Click "Create Credentials" > "OAuth Client ID" > Web application
- Add redirect URI:
http://localhost:8000/auth/google/callback - Copy Client ID and Client Secret into
.env
Discord
- Go to discord.com/developers/applications
- Create a new application > OAuth2
- Add redirect:
http://localhost:8000/auth/discord/callback - Copy Client ID and Client Secret into
.env
Spotify
- Go to developer.spotify.com/dashboard
- Create an app > Edit Settings
- Add redirect URI:
http://localhost:8000/auth/spotify/callback - Copy Client ID and Client Secret into
.env
Microsoft
- Go to portal.azure.com
- Register a new application > Web platform
- Add redirect URI:
http://localhost:8000/auth/microsoft/callback - Copy Application (client) ID and create a Client Secret in
.env
- Go to linkedin.com/developers/apps
- Create a new app > Auth tab
- Add redirect URL:
http://localhost:8000/auth/linkedin/callback - Copy Client ID and Client Secret into
.env
API Reference
Routes
After running oauth-init, your app gets these endpoints for each configured provider:
| Endpoint | Method | Description |
|---|---|---|
/auth/{provider}/login |
GET | Redirects user to the provider's OAuth consent screen |
/auth/{provider}/callback |
GET | Handles the redirect, exchanges code for token |
/auth/logout |
GET | Clears session cookie, redirects to home |
Where {provider} is one of: github, google, discord, spotify, microsoft, linkedin.
Session Helper
from oauth_routes import get_session
@app.get("/dashboard")
async def dashboard(request: Request):
user = get_session(request)
if not user:
return RedirectResponse("/auth/github/login")
# user dict contains:
# - id: str (provider's user ID)
# - name: str (display name)
# - email: str (email address, may be None)
# - avatar: str (profile picture URL)
# - provider: str ("github", "google", "discord", etc.)
return {"welcome": user["name"]}
CLI Reference
oauth-init # scaffold all providers + example app
oauth-init --provider github # only GitHub OAuth
oauth-init --provider discord # only Discord OAuth
oauth-init --no-example # skip the example app, just routes + config
oauth-init --dir ./path/to/project # scaffold into a specific directory
Available --provider values: github, google, discord, spotify, microsoft, linkedin.
Generated files:
| File | Purpose |
|---|---|
oauth_config.py |
Loads provider credentials from .env, configures OAuth endpoints for all 6 providers |
oauth_routes.py |
FastAPI router with login, callback, logout, session management, and PKCE support |
oauth_example_app.py |
Complete working demo with login page and profile page |
.env |
Template with environment variables for all providers |
Security
The generated code includes these security measures out of the box:
- CSRF protection via the
stateparameter (random token verified on callback) - PKCE support for OAuth 2.1 compliance (S256 code challenge)
- HTTP-only cookies for session IDs (not accessible via JavaScript)
- SameSite=Lax cookie policy (prevents cross-site request forgery)
- Server-side token exchange (client secret never exposed to the browser)
- One-hour session expiry (configurable via
max_age)
Note: The generated code uses in-memory session storage. For production, swap
_sessionsdict for Redis, PostgreSQL, or your database of choice.
Project Structure
oauth-for-dummies/
|-- oauth_for_dummies/ # pip-installable CLI package
| |-- cli.py # oauth-init command
| +-- scaffold/ # template files dropped into your project
| |-- oauth_config.py # 6 provider configs
| |-- oauth_routes.py # routes + PKCE support
| +-- oauth_example_app.py # demo with branded buttons
|
|-- app/ # tutorial app (learning resource)
| |-- main.py # FastAPI demo with UI
| |-- config.py # environment variable loader
| |-- auth/
| | |-- routes.py # auth route handlers
| | +-- storage.py # session + debug session storage
| +-- learn/
| +-- routes.py # OAuth debugger (Learn Mode) routes
|
|-- providers/ # OAuth provider implementations
| |-- base.py # abstract OAuthProvider class + PKCE
| |-- github.py # GitHub
| |-- google.py # Google
| |-- discord.py # Discord
| |-- spotify.py # Spotify
| |-- microsoft.py # Microsoft
| |-- linkedin.py # LinkedIn
| +-- registry.py # provider auto-discovery
|
|-- tests/ # unit tests (20 tests, all passing)
|-- docs/ # tutorials and diagrams
+-- pyproject.toml # PyPI packaging configuration
Comparison with Other Libraries
| oauth-for-dummies | fastapi-oauth2 | Authlib | python-social-auth | |
|---|---|---|---|---|
| What it is | CLI scaffold + debugger | Middleware library | Production auth library | Social auth framework |
| Approach | Transparent (you own the code) | Black box (middleware) | Black box (library) | Black box (framework) |
| Providers | 6 | Many (via social-core) | Many | Many |
| OAuth debugger | Yes | No | No | No |
| PKCE / OAuth 2.1 | Yes | No | Yes | No |
| Learn from it | Yes (every HTTP request visible) | No | No | No |
| Setup time | 30 seconds | 5 minutes | 30+ minutes | 30+ minutes |
| CLI scaffolding | Yes | No | No | No |
When to use oauth-for-dummies: You want to understand OAuth, get started fast, and own the code without a runtime dependency.
When to use something else: You need 20+ providers, enterprise SSO (SAML), or a battle-tested library you don't want to maintain.
Tutorial
This repo includes a complete tutorial app that logs every step of the OAuth flow to your terminal:
git clone https://github.com/pranavkumaarofficial/oauth-for-dummies.git
cd oauth-for-dummies
pip install -e .
cp .env.example .env
# add your OAuth credentials to .env
uvicorn app.main:app --reload
You'll see output like this for every login:
============================================================
STEP 1 -- Redirect user to GitHub
============================================================
URL: https://github.com/login/oauth/authorize
client_id: abc12345...
redirect_uri: http://localhost:8000/auth/github/callback
scope: read:user user:email
state: kF9x2mQp...
============================================================
See also:
- How OAuth Works -- visual explanation of every step
- Step-by-step Tutorial -- build OAuth from scratch
Contributing
Contributions welcome. Some ideas:
- Add a provider -- Twitter/X, Apple, Facebook, Twitch
- Improve the CLI -- interactive mode,
--framework flasksupport - Write tests for the scaffold files
- Deploy the debugger -- host a public demo of Learn Mode
See CONTRIBUTING.md for setup instructions.
FAQ
Q: Is this production-ready? A: The generated code is fine for internal tools, prototypes, and small apps. For production at scale, swap the in-memory session store for a database and add HTTPS.
Q: Can I use this with Flask/Django? A: Not yet. Currently FastAPI only. Flask support is planned.
Q: What Python versions are supported? A: Python 3.9 and above.
Q: Do I need to understand OAuth to use this?
A: No. Run oauth-init, add your keys to .env, and it works. But if you want to understand what's happening, use Learn Mode or read the tutorial.
Q: What is PKCE and do I need it? A: PKCE (Proof Key for Code Exchange) is a security improvement that replaces client_secret with a cryptographic challenge. It's required in OAuth 2.1 and recommended for all new apps. This project supports it out of the box.
License
MIT -- use it, learn from it, build on it.
If this saved you time, consider giving it a star on GitHub.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file oauth_for_dummies-1.1.0.tar.gz.
File metadata
- Download URL: oauth_for_dummies-1.1.0.tar.gz
- Upload date:
- Size: 22.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
27597ebdc5a0ae342b8e4815896caf294fcd776751e3c3a96dd814acf5769bec
|
|
| MD5 |
d456186074da8e70b86cec41389ba1fa
|
|
| BLAKE2b-256 |
8465d0a1f88ef79564e41e873b3c90df0495a3fab4415e685f229599759a0fb1
|
Provenance
The following attestation bundles were made for oauth_for_dummies-1.1.0.tar.gz:
Publisher:
publish.yml on pranavkumaarofficial/oauth-for-dummies
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
oauth_for_dummies-1.1.0.tar.gz -
Subject digest:
27597ebdc5a0ae342b8e4815896caf294fcd776751e3c3a96dd814acf5769bec - Sigstore transparency entry: 1370761985
- Sigstore integration time:
-
Permalink:
pranavkumaarofficial/oauth-for-dummies@f0cfeb90effeb0eb76a9fea8be0ba983cda0c57b -
Branch / Tag:
refs/heads/main - Owner: https://github.com/pranavkumaarofficial
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f0cfeb90effeb0eb76a9fea8be0ba983cda0c57b -
Trigger Event:
push
-
Statement type:
File details
Details for the file oauth_for_dummies-1.1.0-py3-none-any.whl.
File metadata
- Download URL: oauth_for_dummies-1.1.0-py3-none-any.whl
- Upload date:
- Size: 17.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
de143dfb4a067c3be833afc84b20ae723d21bd0566c382f8bceebf9b6304ba5b
|
|
| MD5 |
dfbb7789e2c9655361fc18af2b11a6d4
|
|
| BLAKE2b-256 |
ea3919a5227e33aeed594c1fead58d2fde1e23430bf7d6d50932afde83e6bf92
|
Provenance
The following attestation bundles were made for oauth_for_dummies-1.1.0-py3-none-any.whl:
Publisher:
publish.yml on pranavkumaarofficial/oauth-for-dummies
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
oauth_for_dummies-1.1.0-py3-none-any.whl -
Subject digest:
de143dfb4a067c3be833afc84b20ae723d21bd0566c382f8bceebf9b6304ba5b - Sigstore transparency entry: 1370762129
- Sigstore integration time:
-
Permalink:
pranavkumaarofficial/oauth-for-dummies@f0cfeb90effeb0eb76a9fea8be0ba983cda0c57b -
Branch / Tag:
refs/heads/main - Owner: https://github.com/pranavkumaarofficial
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@f0cfeb90effeb0eb76a9fea8be0ba983cda0c57b -
Trigger Event:
push
-
Statement type: