Skip to main content

A stateless OAuth2 middleware for FastAPI with PKCE flow support

Project description

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.

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

fastapi_simple_oauth2-0.2.0.tar.gz (55.7 kB view details)

Uploaded Source

Built Distribution

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

fastapi_simple_oauth2-0.2.0-py3-none-any.whl (7.8 kB view details)

Uploaded Python 3

File details

Details for the file fastapi_simple_oauth2-0.2.0.tar.gz.

File metadata

  • Download URL: fastapi_simple_oauth2-0.2.0.tar.gz
  • Upload date:
  • Size: 55.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for fastapi_simple_oauth2-0.2.0.tar.gz
Algorithm Hash digest
SHA256 970f738ca09678849d37ec43393dae66fe1b52d3b8d615a0ac579ba34d74489f
MD5 96f08baf2468b44646ad56c7d358bf04
BLAKE2b-256 9690e2053f09f2c8febb06ab80123193ec2fc24662e4f10a0e2f426271fd274a

See more details on using hashes here.

Provenance

The following attestation bundles were made for fastapi_simple_oauth2-0.2.0.tar.gz:

Publisher: release.yml on iaalm/fastapi_simple_oauth2

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file fastapi_simple_oauth2-0.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for fastapi_simple_oauth2-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1054221c7ac1e5ebf2c285b94d7d6db39bbfe41ec4b4e37b34a4eb6d26f612d7
MD5 f5de0d4bba2c0655aaf01259957696f7
BLAKE2b-256 11702a217289cc5735959e26aa80acca76dcc693d92ebf4376c16a03d8f30d08

See more details on using hashes here.

Provenance

The following attestation bundles were made for fastapi_simple_oauth2-0.2.0-py3-none-any.whl:

Publisher: release.yml on iaalm/fastapi_simple_oauth2

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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