FastAPI JWT authentication library with Pydantic integration - bringing harmony to your auth flow
Project description
FastAPI JWT Harmony 🎵
A modern, type-safe JWT authentication library for FastAPI with Pydantic integration - bringing harmony to your auth flow! 🎶
🔑 Key Features
- 🔒 Type-safe JWT authentication with full Pydantic model support
- 🚀 FastAPI dependency injection - automatic JWT validation
- 📍 Multiple token locations - headers, cookies, or both
- 🛡️ CSRF protection for cookie-based authentication
- 🌐 WebSocket support with dedicated authentication methods
- 👤 User claims as Pydantic models - strongly typed user data
- 🚫 Token denylist/blacklist support for logout functionality
- 🔐 Asymmetric algorithms support (RS256, ES256, etc.)
- ✅ 100% test coverage with comprehensive test suite
🚀 Quick Start
Installation
pip install fastapi-jwt-harmony
For asymmetric algorithm support:
pip install fastapi-jwt-harmony[asymmetric]
Basic Example
from fastapi import FastAPI, Depends
from pydantic import BaseModel
from fastapi_jwt_harmony import JWTHarmony, JWTHarmonyDep, JWTHarmonyBare
app = FastAPI()
# Define your user model
class User(BaseModel):
id: str
username: str
email: str
# Configure JWT (simple way with dict)
JWTHarmony.configure(
User,
{
"secret_key": "your-secret-key", # pragma: allowlist secret
"token_location": {"headers", "cookies"} # Support both
}
)
# Or use JWTHarmonyConfig for advanced configuration
# from fastapi_jwt_harmony import JWTHarmonyConfig
# JWTHarmony.configure(
# User,
# JWTHarmonyConfig(
# secret_key="your-secret-key", # pragma: allowlist secret
# token_location={"headers", "cookies"}
# )
# )
@app.post("/login")
def login(Authorize: JWTHarmony[User] = Depends(JWTHarmonyBare)):
# Authenticate user (your logic here)
user = User(id="123", username="john", email="john@example.com")
# Create tokens
access_token = Authorize.create_access_token(user_claims=user)
refresh_token = Authorize.create_refresh_token(user_claims=user)
# Set cookies (optional)
Authorize.set_access_cookies(access_token)
Authorize.set_refresh_cookies(refresh_token)
return {"access_token": access_token}
@app.get("/protected")
def protected_route(Authorize: JWTHarmony[User] = Depends(JWTHarmonyDep)):
# JWT automatically validated by JWTHarmonyDep
current_user = Authorize.user_claims # Typed User model!
return {"user": current_user, "message": f"Hello {current_user.username}!"}
📦 Dependencies Overview
FastAPI JWT Harmony provides several dependency types for different authentication needs:
from fastapi_jwt_harmony import (
JWTHarmonyDep, # Requires valid access token
JWTHarmonyOptional, # Optional JWT validation
JWTHarmonyRefresh, # Requires valid refresh token
JWTHarmonyFresh, # Requires fresh access token
JWTHarmonyBare, # No automatic validation
)
@app.get("/public")
def public_endpoint(Authorize: JWTHarmony[User] = Depends(JWTHarmonyOptional)):
if Authorize.user_claims:
return {"message": f"Hello {Authorize.user_claims.username}!"}
return {"message": "Hello anonymous user!"}
@app.post("/sensitive-action")
def sensitive_action(Authorize: JWTHarmony[User] = Depends(JWTHarmonyFresh)):
# Requires fresh token (just logged in)
return {"message": "Sensitive action performed"}
🍪 Cookie Authentication
Enable secure cookie-based authentication with CSRF protection:
from fastapi import Response
JWTHarmony.configure(
User,
{
"secret_key": "your-secret-key", # pragma: allowlist secret
"token_location": {"cookies"},
"cookie_csrf_protect": True,
"cookie_secure": True, # HTTPS only
"cookie_samesite": "strict"
}
)
@app.post("/login")
def login(response: Response, Authorize: JWTHarmony[User] = Depends(JWTHarmonyBare)):
user = User(id="123", username="john", email="john@example.com")
access_token = Authorize.create_access_token(user_claims=user)
# Set secure cookies
Authorize.set_access_cookies(access_token, response)
return {"message": "Logged in successfully"}
@app.post("/logout")
def logout(response: Response, Authorize: JWTHarmony[User] = Depends(JWTHarmonyDep)):
Authorize.unset_jwt_cookies(response)
return {"message": "Logged out successfully"}
🌐 WebSocket Authentication
Authenticate WebSocket connections with dedicated methods:
from fastapi import WebSocket, Query
from fastapi_jwt_harmony import JWTHarmonyWS, JWTHarmonyWebSocket
@app.websocket("/ws")
async def websocket_endpoint(
websocket: WebSocket,
token: str = Query(...),
Authorize: JWTHarmonyWS = Depends(JWTHarmonyWebSocket)
):
await websocket.accept()
try:
# Validate JWT token
Authorize.jwt_required(token)
user = Authorize.user_claims
await websocket.send_text(f"Hello {user.username}!")
except Exception as e:
await websocket.send_text(f"Authentication failed: {str(e)}")
await websocket.close()
🚫 Token Denylist (Logout)
Implement secure logout with token blacklisting:
# In-memory denylist (use Redis in production)
denylist = set()
def check_if_token_revoked(jwt_payload: dict) -> bool:
jti = jwt_payload.get("jti")
return jti in denylist
# Configure with denylist callback
JWTHarmony.configure(
User,
{
"secret_key": "your-secret-key", # pragma: allowlist secret
"denylist_enabled": True,
"denylist_token_checks": {"access", "refresh"}
},
denylist_callback=check_if_token_revoked
)
@app.post("/logout")
def logout(Authorize: JWTHarmony[User] = Depends(JWTHarmonyDep)):
jti = Authorize.get_jti()
denylist.add(jti) # Add to denylist
return {"message": "Successfully logged out"}
⚙️ Configuration Options
Comprehensive configuration with sensible defaults:
from datetime import timedelta
JWTHarmonyConfig(
# Core settings
secret_key="your-secret-key", # Required for HS256 # pragma: allowlist secret
algorithm="HS256", # JWT algorithm
token_location={"headers"}, # Where to look for tokens
# Token expiration
access_token_expires=timedelta(minutes=15),
refresh_token_expires=timedelta(days=30),
# Headers
header_name="Authorization",
header_type="Bearer",
# Cookies
cookie_secure=False, # Set True for HTTPS
cookie_csrf_protect=True, # CSRF protection
cookie_samesite="strict",
# Asymmetric keys (for RS256, ES256, etc.)
private_key=None, # For signing
public_key=None, # For verification
# Denylist
denylist_enabled=False,
denylist_token_checks={"access", "refresh"},
# Validation
decode_leeway=0, # Clock skew tolerance
decode_audience=None, # Expected audience
decode_issuer=None, # Expected issuer
)
🔐 Asymmetric Algorithms
Support for RS256, ES256, and other asymmetric algorithms:
# Generate keys (example)
private_key = """-----BEGIN PRIVATE KEY----- # pragma: allowlist secret
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC7...
-----END PRIVATE KEY-----""" # pragma: allowlist secret
public_key = """-----BEGIN PUBLIC KEY----- # pragma: allowlist secret
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu7...
-----END PUBLIC KEY-----""" # pragma: allowlist secret
JWTHarmony.configure(
User,
JWTHarmonyConfig(
algorithm="RS256",
private_key=private_key, # For signing tokens # pragma: allowlist secret
public_key=public_key, # For verifying tokens # pragma: allowlist secret
)
)
🧪 Testing
Run the comprehensive test suite:
# Install development dependencies
uv sync --dev
# Run tests
uv run pytest
# Run with coverage
uv run pytest --cov=fastapi_jwt_harmony
🛠️ Development
# Clone the repository
git clone https://github.com/ivolnistov/fastapi-jwt-harmony.git
cd fastapi-jwt-harmony
# Install with development dependencies
uv sync --dev
# Run tests
uv run pytest
# Run linting
uv run ruff check src/
uv run mypy src/fastapi_jwt_harmony
uv run pylint src/fastapi_jwt_harmony
📊 Project Status
- ✅ 111 tests passing - Comprehensive test coverage
- ✅ Type-safe - Full mypy compatibility
- ✅ Modern Python - Supports Python 3.11+
- ✅ Production ready - Used in production applications
🤝 Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🙏 Acknowledgments
- FastAPI for the amazing web framework
- Pydantic for data validation and settings management
- PyJWT for JWT implementation
- Original fastapi-jwt-auth for inspiration
Made with ❤️ for the FastAPI community
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_jwt_harmony-0.2.1.tar.gz.
File metadata
- Download URL: fastapi_jwt_harmony-0.2.1.tar.gz
- Upload date:
- Size: 46.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b6a704f0c465e25da241370a7c73e942c47a34087f991764a1cf003b0f1b3f81
|
|
| MD5 |
26697f2bcb90c66caad0bd59b3554737
|
|
| BLAKE2b-256 |
a5cc4faf7bcfb7c7b157ad0f64c11fb500368e4fdcf72bd5b0a2715e80b2af53
|
Provenance
The following attestation bundles were made for fastapi_jwt_harmony-0.2.1.tar.gz:
Publisher:
release.yml on GaijinEntertainment/fastapi-jwt-harmony
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fastapi_jwt_harmony-0.2.1.tar.gz -
Subject digest:
b6a704f0c465e25da241370a7c73e942c47a34087f991764a1cf003b0f1b3f81 - Sigstore transparency entry: 772190005
- Sigstore integration time:
-
Permalink:
GaijinEntertainment/fastapi-jwt-harmony@ab3f5a63832963fedaa0b74e68b39a9f562a54cf -
Branch / Tag:
refs/heads/main - Owner: https://github.com/GaijinEntertainment
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ab3f5a63832963fedaa0b74e68b39a9f562a54cf -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file fastapi_jwt_harmony-0.2.1-py3-none-any.whl.
File metadata
- Download URL: fastapi_jwt_harmony-0.2.1-py3-none-any.whl
- Upload date:
- Size: 23.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3562b6f5381f5af5ad394d9ec1cc125b399f84b1c411198c3ec3902e671fc59e
|
|
| MD5 |
2d5b572f78e5ba7a16dc4cebfe5b5ce8
|
|
| BLAKE2b-256 |
29b238ca64646c2c7c607cb88e12a7ad6423b35eb1858e8b86af7575857820f5
|
Provenance
The following attestation bundles were made for fastapi_jwt_harmony-0.2.1-py3-none-any.whl:
Publisher:
release.yml on GaijinEntertainment/fastapi-jwt-harmony
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fastapi_jwt_harmony-0.2.1-py3-none-any.whl -
Subject digest:
3562b6f5381f5af5ad394d9ec1cc125b399f84b1c411198c3ec3902e671fc59e - Sigstore transparency entry: 772190009
- Sigstore integration time:
-
Permalink:
GaijinEntertainment/fastapi-jwt-harmony@ab3f5a63832963fedaa0b74e68b39a9f562a54cf -
Branch / Tag:
refs/heads/main - Owner: https://github.com/GaijinEntertainment
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ab3f5a63832963fedaa0b74e68b39a9f562a54cf -
Trigger Event:
workflow_dispatch
-
Statement type: