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: Designed for single-tenant applications
- 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 instancevalidate_callback(Callable): Function that validates username/password and returns claimskey(str, optional): Secret key for JWT signing. If not provided, a random key will be generated.
Returns:
OAuth2PKCE: OAuth2 instance for further configuration
require_claim(required_claims)
Decorator to protect endpoints with specific claim requirements.
Parameters:
required_claims(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&client_id=your_client&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"client_id: Your client identifierredirect_uri: Where to redirect after authorizationcode_challenge: PKCE code challengecode_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&client_id=your_client&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():
return {"message": "Admin access granted!"}
@app.get("/user")
@require_claim({"role": "user"})
async def user_only():
return {"message": "User access granted!"}
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',
client_id: 'your_client_id',
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,
client_id: 'your_client_id',
redirect_uri: 'http://localhost:3000/callback'
})
});
const tokenData = await response.json();
localStorage.setItem('access_token', tokenData.access_token);
}
Security Considerations
- Secret Key: Always use a strong, unique secret key in production
- HTTPS: Use HTTPS in production to protect tokens in transit
- Token Expiration: Tokens expire after 1 hour by default
- Code Cleanup: Authorization codes are automatically cleaned up after use
- State Parameter: Use the state parameter to prevent CSRF attacks
Production Considerations
For production use, consider:
- Persistent Storage: Replace in-memory storage with Redis or a database
- Token Refresh: Implement token refresh functionality
- Rate Limiting: Add rate limiting to prevent abuse
- Logging: Add comprehensive logging for security monitoring
- CORS: Configure CORS properly for your frontend domains
License
MIT License - see LICENSE file for details.
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 fastapi_simple_oauth2-0.1.0a1.tar.gz.
File metadata
- Download URL: fastapi_simple_oauth2-0.1.0a1.tar.gz
- Upload date:
- Size: 55.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8492b1b3a94a1e2acc0194f5e5ca5ed64de003200e73e89cb0241bbef26ff006
|
|
| MD5 |
90992568563853e26c9bdbc17f437684
|
|
| BLAKE2b-256 |
c9685e8092ba268c2713fc9d3e7157fb926cb3e43036aa899735dfe9384b9d1f
|
Provenance
The following attestation bundles were made for fastapi_simple_oauth2-0.1.0a1.tar.gz:
Publisher:
release.yml on iaalm/fastapi_simple_oauth2
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fastapi_simple_oauth2-0.1.0a1.tar.gz -
Subject digest:
8492b1b3a94a1e2acc0194f5e5ca5ed64de003200e73e89cb0241bbef26ff006 - Sigstore transparency entry: 287483425
- Sigstore integration time:
-
Permalink:
iaalm/fastapi_simple_oauth2@712b1b04a88e034143f799a29f9db1add2aabbdf -
Branch / Tag:
refs/tags/v0.1.0a1 - Owner: https://github.com/iaalm
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@712b1b04a88e034143f799a29f9db1add2aabbdf -
Trigger Event:
push
-
Statement type:
File details
Details for the file fastapi_simple_oauth2-0.1.0a1-py3-none-any.whl.
File metadata
- Download URL: fastapi_simple_oauth2-0.1.0a1-py3-none-any.whl
- Upload date:
- Size: 7.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e2b0dd7d77766c3fde4b675a9e2bc45af8fb21994a52a8984d40847ea3bf0187
|
|
| MD5 |
5a6b46247258f74d4e414b1e43572337
|
|
| BLAKE2b-256 |
67f817d7d45bf835456f39137ee2161853bb5cc8039f657a50ebe1f10d28c5b3
|
Provenance
The following attestation bundles were made for fastapi_simple_oauth2-0.1.0a1-py3-none-any.whl:
Publisher:
release.yml on iaalm/fastapi_simple_oauth2
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fastapi_simple_oauth2-0.1.0a1-py3-none-any.whl -
Subject digest:
e2b0dd7d77766c3fde4b675a9e2bc45af8fb21994a52a8984d40847ea3bf0187 - Sigstore transparency entry: 287483434
- Sigstore integration time:
-
Permalink:
iaalm/fastapi_simple_oauth2@712b1b04a88e034143f799a29f9db1add2aabbdf -
Branch / Tag:
refs/tags/v0.1.0a1 - Owner: https://github.com/iaalm
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@712b1b04a88e034143f799a29f9db1add2aabbdf -
Trigger Event:
push
-
Statement type: