Skip to main content

FastAPI JWT Harmony 🎵

Python 3.11+ FastAPI PyPI version License: MIT Code Quality

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"}

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:

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass
  5. 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

Metadata

Release files for fastapi-jwt-harmony 0.4.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-jwt-harmony 0.4.0
File Size Uploaded
fastapi_jwt_harmony-0.4.0.tar.gz 56.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for fastapi-jwt-harmony 0.4.0
File Interpreter ABI Platform
fastapi_jwt_harmony-0.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 83.0 kB

Release files / fastapi_jwt_harmony-0.4.0.tar.gz

Download URL fastapi_jwt_harmony-0.4.0.tar.gz
Size 56.8 kB
Tags Source
SHA-256 checksum
How to use checksums
2b7979d3d3576f8c9e3807269fc73252ae5c708638779257c6698e429b2b4f4d
BLAKE2b-256 checksum
How to use checksums
aebd1b83bb533822bbc1cf594a39213d491e943bbc7f49b71408f6f7629406ef
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Sep 16, 2026.

Transparency log

Release files / fastapi_jwt_harmony-0.4.0-py3-none-any.whl

Download URL fastapi_jwt_harmony-0.4.0-py3-none-any.whl
Size 26.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
59757ca1b0d042f361fd7710910c501775f4c2f35c4e976d42185f61097106de
BLAKE2b-256 checksum
How to use checksums
19553a1ec5fdf7e85a705358a1254fcf2bc295679601142e4858391e41964fd1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Sep 16, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.4.0 This release

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

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