Skip to main content

JWT Auth Library

A lightweight, framework-agnostic JWT token validation library with support for multiple authentication providers and easy integration with web frameworks.

Features

  • 🔐 Framework Agnostic - Works with FastAPI, Flask, Django, CLI tools, background workers, and any Python application
  • 🎯 Provider Pattern - Support for multiple JWT providers (Generic JWT, Casdoor, extensible for others)
  • ⚡ Async/Await Support - Built for modern Python async applications
  • 🛡️ Type Safe - Full type hints and dataclass-based configuration
  • 🔧 Easy Integration - Simple adapters for popular web frameworks
  • 🚨 Comprehensive Errors - Detailed error codes and messages for all failure scenarios

Installation

# Basic installation
pip install jwt-auth-lib

# With Casdoor support
pip install jwt-auth-lib[casdoor]

# With FastAPI adapter
pip install jwt-auth-lib[fastapi]

# With all optional dependencies
pip install jwt-auth-lib[all]

Quick Start

Generic JWT Validation

import asyncio
from jwt_auth import JWTProvider, JWTConfig

async def main():
    # Configure JWT validation
    config = JWTConfig(
        certificate="-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----",
        algorithms=["RS256"],
        audience="your-audience",
        issuer="your-issuer"
    )
    
    # Create provider
    auth_provider = JWTProvider(config)
    
    # Validate token
    try:
        user = await auth_provider.validate_token("your.jwt.token")
        print(f"Authenticated user: {user.email}")
        print(f"User roles: {user.roles}")
    except AuthError as e:
        print(f"Authentication failed: {e.code} - {e.message}")

asyncio.run(main())

Casdoor Integration

from jwt_auth import CasdoorProvider, CasdoorConfig

# Configure Casdoor
config = CasdoorConfig.from_env()  # Loads from environment variables

# Create provider  
auth_provider = CasdoorProvider(config)

# Validate token
user = await auth_provider.validate_token(token)

FastAPI Integration

from fastapi import FastAPI, Depends
from jwt_auth import CasdoorProvider, CasdoorConfig, User
from jwt_auth.adapters.fastapi import create_auth_dependency

app = FastAPI()

# Setup authentication
config = CasdoorConfig.from_env()
auth_provider = CasdoorProvider(config)
get_current_user = create_auth_dependency(auth_provider)

@app.get("/profile")
async def get_profile(user: User = Depends(get_current_user)):
    return {
        "user_id": user.id,
        "email": user.email,
        "name": user.name,
        "roles": user.roles
    }

@app.get("/admin")
async def admin_only(user: User = Depends(get_current_user)):
    if not user.has_role("admin"):
        raise HTTPException(403, "Admin role required")
    return {"message": "Admin access granted"}

Configuration

Environment Variables (Casdoor)

CASDOOR_ENDPOINT=https://auth.your-domain.com
CASDOOR_CLIENT_ID=your-client-id
CASDOOR_CLIENT_SECRET=your-client-secret
CASDOOR_CERTIFICATE="-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"
CASDOOR_ORG_NAME=your-org
CASDOOR_APP_NAME=your-app

Programmatic Configuration

from jwt_auth import CasdoorConfig

config = CasdoorConfig(
    endpoint="https://auth.your-domain.com",
    client_id="your-client-id", 
    client_secret="your-client-secret",
    certificate="-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----",
    org_name="your-org",
    app_name="your-app"
)

Framework-Agnostic Usage

CLI Tool

#!/usr/bin/env python3
import asyncio
import sys
from jwt_auth import CasdoorProvider, CasdoorConfig, AuthError

async def validate_token_cli():
    if len(sys.argv) != 2:
        print("Usage: python validate.py <token>")
        return
    
    config = CasdoorConfig.from_env()
    auth_provider = CasdoorProvider(config)
    
    try:
        user = await auth_provider.validate_token(sys.argv[1])
        print(f"✅ Valid token for: {user.email}")
    except AuthError as e:
        print(f"❌ Invalid token: {e.code}")

if __name__ == "__main__":
    asyncio.run(validate_token_cli())

Background Worker

from jwt_auth import CasdoorProvider, CasdoorConfig

class AuthenticatedWorker:
    def __init__(self):
        config = CasdoorConfig.from_env()
        self.auth_provider = CasdoorProvider(config)
    
    async def process_message(self, message_data):
        try:
            user = await self.auth_provider.validate_token(
                message_data["auth_token"]
            )
            await self.do_work(user, message_data["payload"])
        except AuthError as e:
            print(f"Skipping message: {e.code}")

Error Handling

The library provides specific exception types for different failure scenarios:

from jwt_auth import (
    AuthError,           # Base exception
    TokenExpiredError,   # Token has expired
    InvalidSignatureError, # Invalid signature
    InvalidTokenError,   # Malformed token
    MissingTokenError,   # No token provided
    InvalidAudienceError, # Wrong audience
    InvalidIssuerError,  # Wrong issuer
)

try:
    user = await auth_provider.validate_token(token)
except TokenExpiredError:
    # Handle expired token - maybe redirect to login
    pass
except InvalidSignatureError:
    # Handle invalid signature - security concern
    pass
except AuthError as e:
    # Handle any other auth error
    print(f"Auth failed: {e.code} - {e.message}")

User Object

The library returns a standard User object regardless of the authentication provider:

@dataclass
class User:
    id: str                    # Unique user identifier
    email: str                 # User email address
    name: str                  # User's name
    display_name: Optional[str] # Display name (if different from name)
    avatar: Optional[str]      # Avatar URL
    roles: List[str]           # User roles
    permissions: List[str]     # User permissions  
    metadata: Dict[str, Any]   # Provider-specific data
    
    # Utility methods
    def has_role(self, role: str) -> bool
    def has_permission(self, permission: str) -> bool
    def has_any_role(self, roles: List[str]) -> bool
    def has_all_roles(self, roles: List[str]) -> bool

Publishing to PyPI

This library includes a Docker-based publishing system for consistent, isolated uploads to PyPI.

Quick Publishing

# Navigate to library directory
cd lib/jwt_auth

# Test upload (recommended first)
./publish.sh --test

# Production upload  
./publish.sh --prod

# Both Test PyPI and Production PyPI
./publish.sh --both

Requirements

  • Docker installed and running
  • PyPI API tokens configured in ~/.pypirc

See DOCKER_PUBLISH.md for detailed instructions.

Development

# Install development dependencies
pip install -e .[dev]

# Run tests
pytest

# Run linting
black .
flake8 .
mypy .

# Run type checking
mypy jwt_auth/

# Validate package before publishing
./publish.sh --dry-run

License

MIT License - see LICENSE file for details.

Contributing

Contributions are welcome! Please read CONTRIBUTING.md for guidelines.

Support

Metadata

Release files for andspace-jwt-auth 1.2.7

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for andspace-jwt-auth 1.2.7
File Size Uploaded
andspace_jwt_auth-1.2.7.tar.gz 17.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for andspace-jwt-auth 1.2.7
File Interpreter ABI Platform
andspace_jwt_auth-1.2.7-py3-none-any.whl Python 3 none any Details

Total release size: 38.7 kB

Release files / andspace_jwt_auth-1.2.7.tar.gz

Download URL andspace_jwt_auth-1.2.7.tar.gz
Size 17.8 kB
Tags Source
SHA-256 checksum
How to use checksums
6716525973d7a261e5faf49ae6916f064b8ec87413a0bc6a990ff92ca27ad94c
BLAKE2b-256 checksum
How to use checksums
ad4403985c899e05eeea67a839e502dbee2121808db48e41dfde37b9fb279f92
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.13

Release files / andspace_jwt_auth-1.2.7-py3-none-any.whl

Download URL andspace_jwt_auth-1.2.7-py3-none-any.whl
Size 20.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c75c8d091a64ceed228cc451ce63f9de666608a626819bfda742f9f8816ecc00
BLAKE2b-256 checksum
How to use checksums
d81b363d8c566e10f2a51db9c3e9a1f49dff3d6e73760c61dc09ddeb36384c5c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.13

Release history Release notifications | RSS feed

This release

1.2.7 This release

2 release files

1.2.6

2 release files

1.2.3

2 release files

1.2.2

2 release files

1.2.1

2 release files

1.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