Skip to main content

FastAPI JWT authentication library with Pydantic integration - bringing harmony to your auth flow

Project description

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

🍪 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:

  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

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_jwt_harmony-0.2.1.tar.gz (46.8 kB view details)

Uploaded Source

Built Distribution

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

fastapi_jwt_harmony-0.2.1-py3-none-any.whl (23.6 kB view details)

Uploaded Python 3

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

Hashes for fastapi_jwt_harmony-0.2.1.tar.gz
Algorithm Hash digest
SHA256 b6a704f0c465e25da241370a7c73e942c47a34087f991764a1cf003b0f1b3f81
MD5 26697f2bcb90c66caad0bd59b3554737
BLAKE2b-256 a5cc4faf7bcfb7c7b157ad0f64c11fb500368e4fdcf72bd5b0a2715e80b2af53

See more details on using hashes here.

Provenance

The following attestation bundles were made for fastapi_jwt_harmony-0.2.1.tar.gz:

Publisher: release.yml on GaijinEntertainment/fastapi-jwt-harmony

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_jwt_harmony-0.2.1-py3-none-any.whl.

File metadata

File hashes

Hashes for fastapi_jwt_harmony-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 3562b6f5381f5af5ad394d9ec1cc125b399f84b1c411198c3ec3902e671fc59e
MD5 2d5b572f78e5ba7a16dc4cebfe5b5ce8
BLAKE2b-256 29b238ca64646c2c7c607cb88e12a7ad6423b35eb1858e8b86af7575857820f5

See more details on using hashes here.

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

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