Skip to main content

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 state and PKCE automatically
  • 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 private
  • code_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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dead_simple_oauth_fastapi-0.1.0.tar.gz (8.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dead_simple_oauth_fastapi-0.1.0-py3-none-any.whl (13.0 kB view details)

Uploaded Python 3

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

Hashes for dead_simple_oauth_fastapi-0.1.0.tar.gz
Algorithm Hash digest
SHA256 0b5c3c11edd798911a2914ee9f2922a2558cdc73a10002b4eb487a79db828342
MD5 ecc02dcdb2d071af8b0b31f33d472e39
BLAKE2b-256 3f2cf8138528d256da55630c28d8414cc8603f492a4348dd29d0eaa512abd441

See more details on using hashes here.

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

Hashes for dead_simple_oauth_fastapi-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0be2af7c9312a3410c408fc19cc33707ced62efdc034c5b9651a45e5d8d67763
MD5 8196eb9edb6a9f03686d2be31c39dd7e
BLAKE2b-256 1ba340bb87b28d2a33c700fd5764f8ae3e26c1bc7747f667fbb04de30348eb3f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page