Dead simple Google + GitHub OAuth for FastAPI with built-in security (state + PKCE) and Redis support
Project description
dead-simple-oauth-fastapi
This package helps you add Google and GitHub OAuth2 web application flow to your FastAPI app.
No more copying the oauth code/logic in every new project.
What it does for you:
- Redirects the user to Google or GitHub
- Receives the callback
- Validates
stateandPKCEautomatically - Returns a typed user object
Installation
# Basic (single-instance deployment)
uv add dead-simple-oauth-fastapi
# With Redis (multi-instance deployment)
uv add "dead-simple-oauth-fastapi[redis]"
Google example
from typing import Annotated
from fastapi import FastAPI, Depends, Request
from fastapi.responses import RedirectResponse
from dead_simple_oauth_fastapi import GoogleOAuthClient, MemoryStore, GoogleUser
google = GoogleOAuthClient(
client_id="YOUR_GOOGLE_CLIENT_ID",
client_secret="YOUR_GOOGLE_CLIENT_SECRET",
redirect_uri="http://localhost:8000/auth/google/callback",
store=MemoryStore(), # Use RedisStore(redis_instance) in multi-instance deployment
)
app = FastAPI()
@app.get("/auth/google")
async def google_oauth_handler(request: Request) -> RedirectResponse:
return await google.redirect(request)
@app.get("/auth/google/callback")
async def google_oauth_callback_handler(
oauth_user: Annotated[GoogleUser, Depends(google.callback_dependency())],
) -> dict:
return {
"sub": oauth_user.sub,
"email": oauth_user.email,
"name": oauth_user.name,
}
GitHub example
Use GitHubOAuthClient and GithubUser instead.
The code is almost the same - just change the class names.
Storage Options
MemoryStore
Stores state in process memory. Good for development and single-instance apps.
from dead_simple_oauth_fastapi import MemoryStore
store = MemoryStore()
![WARNING] Do not use MemoryStore if your app runs on multiple servers — each server has its own memory and the state stored on one will not be visible to another.
RedisStore
Stores state in Redis. Use this for multi-instance or load-balanced deployments.
from redis.asyncio import Redis
from dead_simple_oauth_fastapi import RedisStore
redis = Redis.from_url("redis://localhost:6379", decode_responses=True)
store = RedisStore(redis)
You can also customize the key prefix:
store = RedisStore(redis, prefix="users:oauth:")
Custom Error Messages (optional)
All user-facing error messages can be overridden. You do not have to replace all of them - only what you need.
from dead_simple_oauth_fastapi import OAuthMessages
messages = OAuthMessages(
missing_code="Authorization code is missing.",
invalid_or_expired_state="Login session expired. Please try again.",
)
google = GoogleOAuthClient(..., messages=messages)
You only need to change the messages you want.
What is state and PKCE?
These are two security mechanisms this package uses automatically. You do not need to configure them - but understanding what they do helps.
state - protecting against fake callbacks. (client side)
When your app starts the oauth flow, it generates a random string called state and saves it in storage options you choise a short-lived cookie on the user's browser and send it in the url params.
When Google or GitHub redirects the user back to your callback URL, they include that same state value in the URL. Your app compares the two.
If they match → the callback is valid, request and response legitimately originated in the same browser. If they do not match → someone sent a forged callback, reject it.
Why does this matter? Without it, an attacker could craft a fake callback URL and trick your app into processing it. The state check ensures your app only accepts callbacks it actually initiated or originated.
PKCE - protecting the authorization code(authorization server side)
After the user logs in, Google or GitHub sends an authorization code back to your callback URL. Your app then exchanges this code for an access token.
The problem: this code travels through the browser (in a URL), and could theoretically be stolen.
PKCE (Proof Key for Code Exchange, pronounced "pixy") protects against this. Before the redirect, your app generates two related values:
code_verifier— a random secret, kept privatecode_challenge— a hash of the verifier, sent to the provider
When your app later exchanges the code for a token, it sends the original code_verifier. The provider hashes it and checks it against the code_challenge it stored earlier.
Even if an attacker steals the authorization code, they do not have the code_verifier. The provider will reject their token request.
This package handles both state and PKCE for every request automatically!
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 dead_simple_oauth_fastapi-0.1.0.tar.gz.
File metadata
- Download URL: dead_simple_oauth_fastapi-0.1.0.tar.gz
- Upload date:
- Size: 8.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Arch Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0b5c3c11edd798911a2914ee9f2922a2558cdc73a10002b4eb487a79db828342
|
|
| MD5 |
ecc02dcdb2d071af8b0b31f33d472e39
|
|
| BLAKE2b-256 |
3f2cf8138528d256da55630c28d8414cc8603f492a4348dd29d0eaa512abd441
|
File details
Details for the file dead_simple_oauth_fastapi-0.1.0-py3-none-any.whl.
File metadata
- Download URL: dead_simple_oauth_fastapi-0.1.0-py3-none-any.whl
- Upload date:
- Size: 13.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Arch Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0be2af7c9312a3410c408fc19cc33707ced62efdc034c5b9651a45e5d8d67763
|
|
| MD5 |
8196eb9edb6a9f03686d2be31c39dd7e
|
|
| BLAKE2b-256 |
1ba340bb87b28d2a33c700fd5764f8ae3e26c1bc7747f667fbb04de30348eb3f
|