Skip to main content

FastAPI Simple OAuth2 PKCE

A lightweight, stateless OAuth2 middleware for FastAPI applications with PKCE (Proof Key for Code Exchange) flow support. This plugin provides a simple way to implement OAuth2 authorization in your FastAPI applications without the complexity of full OAuth2 providers.

Features

  • PKCE Flow Support: Implements OAuth2 PKCE flow for secure authorization
  • JWT Tokens: Stateless authentication using JWT tokens
  • Custom Claims: Support for custom claims in JWT tokens
  • Simple Integration: Easy to integrate with existing FastAPI applications
  • Single Tenant: Target to inline intergrated with your application easily, no multi-tenantcy support
  • No Database Required: In-memory storage for development (can be extended for production)

Installation

pip install fastapi-simple-oauth2

Quick Start

from fastapi import FastAPI
from fastapi_simple_oauth2 import register_oauth_route, require_claim

app = FastAPI()

# Define your user validation function
def validate_user(username: str, password: str):
    # Your authentication logic here
    if username == "admin" and password == "password":
        return {"user_id": "admin", "role": "admin"}
    return None

# Register OAuth routes
oauth = register_oauth_route(app, validate_callback=validate_user)

# Protected endpoint
@app.get("/protected")
@require_claim({"role": "admin"})
async def protected_route():
    return {"message": "Access granted!"}

API Reference

register_oauth_route(app, validate_callback, key=None)

Registers OAuth2 routes with your FastAPI application.

Parameters:

  • app (FastAPI): Your FastAPI application instance
  • validate_callback (Callable): Function that validates username/password and returns claims
  • key (str, optional): Secret key for JWT signing. If not provided, a random key will be generated.

Returns:

  • OAuth2PKCE: OAuth2 instance for further configuration

require_claims_dependency(required_claims)

Decorator to protect endpoints with specific claim requirements.

Parameters:

  • require_claims_dependency (dict): Dictionary of required claims and their expected values

OAuth2 Flow

1. Authorization Request

The client initiates the OAuth2 flow by redirecting the user to the /authorize endpoint:

GET /authorize?response_type=code&redirect_uri=http://localhost:3000/callback&code_challenge=YOUR_CODE_CHALLENGE&code_challenge_method=S256&state=random_state

Required Parameters:

  • response_type: Must be "code"
  • redirect_uri: Where to redirect after authorization
  • code_challenge: PKCE code challenge
  • code_challenge_method: Must be "S256"

2. User Authentication

The user is redirected to your application where they can authenticate. After successful authentication, the user is redirected back with an authorization code.

3. Token Exchange

The client exchanges the authorization code for an access token:

POST /token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=AUTHORIZATION_CODE&code_verifier=CODE_VERIFIER&redirect_uri=http://localhost:3000/callback

Response:

{
    "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
    "token_type": "Bearer",
    "expires_in": 3600
}

4. Using the Access Token

Include the access token in the Authorization header for protected endpoints:

Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...

Example Usage

Basic Setup

from fastapi import FastAPI
from fastapi_simple_oauth2 import register_oauth_route, require_claim

app = FastAPI()

# Mock user database
USERS = {
    "admin": {"password": "admin123", "claims": {"role": "admin"}},
    "user": {"password": "user123", "claims": {"role": "user"}}
}

def validate_user(username: str, password: str):
    user = USERS.get(username)
    if user and user["password"] == password:
        return user["claims"]
    return None

# Register OAuth routes
oauth = register_oauth_route(app, validate_callback=validate_user)

# Protected endpoints
@app.get("/admin")
@require_claim({"role": "admin"})async def admin_only(
    user_cliaims: ClaimsSet = Depends(
        oauth.require_claims_dependency({"role": "admin"})
    ),
) -> Any:
    return {"message": "Admin access granted!", "data": "sensitive admin data"}

@app.get("/data")
async def read_data(
    user_cliaims: ClaimsSet = Depends(
        oauth.require_claims_dependency({"permissions": ["read"]})
    ),
) -> Any:
    return {"message": "Data access granted!", "data": "some data"}

Frontend Integration

Here's an example of how to implement the OAuth2 flow in a frontend application:

// Generate PKCE code verifier and challenge
function generateCodeVerifier() {
    const array = new Uint8Array(32);
    crypto.getRandomValues(array);
    return base64URLEncode(array);
}

function generateCodeChallenge(verifier) {
    const hash = crypto.subtle.digestSync('SHA-256', new TextEncoder().encode(verifier));
    return base64URLEncode(new Uint8Array(hash));
}

// Start OAuth2 flow
async function startOAuthFlow() {
    const codeVerifier = generateCodeVerifier();
    const codeChallenge = generateCodeChallenge(codeVerifier);
    
    // Store code verifier for later use
    localStorage.setItem('code_verifier', codeVerifier);
    
    const params = new URLSearchParams({
        response_type: 'code',
        redirect_uri: 'http://localhost:3000/callback',
        code_challenge: codeChallenge,
        code_challenge_method: 'S256',
        state: generateRandomState()
    });
    
    window.location.href = `http://localhost:8000/authorize?${params}`;
}

// Exchange code for token
async function exchangeCodeForToken(code) {
    const codeVerifier = localStorage.getItem('code_verifier');
    
    const response = await fetch('http://localhost:8000/token', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
        },
        body: new URLSearchParams({
            grant_type: 'authorization_code',
            code: code,
            code_verifier: codeVerifier,
            redirect_uri: 'http://localhost:3000/callback'
        })
    });
    
    const tokenData = await response.json();
    localStorage.setItem('access_token', tokenData.access_token);
}

License

MIT License - see LICENSE file for details.

Metadata

Release files for fastapi-simple-oauth2 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fastapi-simple-oauth2 0.3.0
File Size Uploaded Provenance
fastapi_simple_oauth2-0.3.0.tar.gz 125.6 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for fastapi-simple-oauth2 0.3.0
File Interpreter ABI Platform
fastapi_simple_oauth2-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 133.4 kB

Release files / fastapi_simple_oauth2-0.3.0.tar.gz

Download URL fastapi_simple_oauth2-0.3.0.tar.gz
Size 125.6 kB
Tags Source
SHA-256 checksum
How to use checksums
dd46391a5cfc3c7809d08fced7f81d8273e4925207f62ea2d9934f00947c22f2
BLAKE2b-256 checksum
How to use checksums
d9fab8f43840db11b820f3a03212286187c49b0cc3e696c793ec426d6e37e15e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.12.9

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 19, 2025.

Transparency log

Release files / fastapi_simple_oauth2-0.3.0-py3-none-any.whl

Download URL fastapi_simple_oauth2-0.3.0-py3-none-any.whl
Size 7.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
40f651931c23c2a11dcb8e4d9cd6007327a10f3c67296cd631f0e93fdf9dc8d0
BLAKE2b-256 checksum
How to use checksums
19f37df9fe335a8e01308c405efe0d37e650d3fb9ae7e38e00d36fd7cfc305c1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.12.9

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 19, 2025.

Transparency log

Security for fastapi-simple-oauth2 0.3.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for fastapi_simple_oauth2-0.3.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 19, 2025.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page