Skip to main content

A minimal, spec-first, framework-agnostic, async-only JWT validator with zero network dependencies.

Project description

async-jwt-core

A minimal, spec-first, framework-agnostic, async-only JWT validator with zero network dependencies.

PyPI version License: MIT


🚀 Why async-jwt-core? (The Problem We Solve)

In the modern Python async ecosystem, validating JWTs using JSON Web Key Sets (JWKS) usually forces you into one of two bad situations:

  1. Framework Lock-in: Libraries tied directly to FastAPI, Starlette, or Django.
  2. Opinionated I/O: Libraries that insist on making network calls for you (often using specific HTTP clients) to fetch keys, making it hard to implement custom caching (like Redis) or use your own HTTP session.

async-jwt-core solves this by doing exactly one thing perfectly: Pure Cryptographic Validation without I/O.

We provide the core validation logic. You bring the keys. This gives you absolute control over how keys are fetched, cached, and stored, while ensuring your event loop never blocks.

✨ Key Features

  • 🔒 Zero Network I/O – Keys are fetched externally. The validator only does the heavy lifting (crypto and claim checks).
  • Async-Only API – Designed from the ground up for asyncio.
  • 🧩 Framework Agnostic – Works with FastAPI, Sanic, aiohttp, or even pure Python background workers.
  • 🛠️ Highly Modular & Extensible – Want to add a custom algorithm? Just inherit from Algorithm and register it.
  • 🎯 Custom Claim Validation – Pass your own validation functions to enforce business rules during the validation phase.
  • 📦 Ultra Lightweight – Only depends on cryptography for secure signature verification.

🔐 Supported Algorithms

We support a vast range of modern cryptographic algorithms out of the box:

Type Algorithms
HMAC (Symmetric) HS256, HS384, HS512
RSA (Asymmetric) RS256, RS384, RS512
RSA-PSS (Asymmetric) PS256, PS384, PS512
ECDSA (Elliptic Curve) ES256, ES384, ES512

🌟 Extra Features to Help You

To make your life easier, we include features that other libraries charge you in complexity:

1. JSON Web Encryption (JWE) Support

We don't just do signing. We now support JWE decryption (RSA-OAEP with AES-GCM). This allows you to handle encrypted tokens where the payload is hidden.

from async_jwt_core import JWE

# Decrypt an encrypted token
decrypted_payload = await JWE.decrypt(token, private_key)

2. Unverified Decoding

Need to check the claims or header before you validate the token? No problem.

header = await validator.get_unverified_header(token)
payload = await validator.decode_unverified(token)

3. Built-in Async JWKS Cache

Even though we don't do I/O, we provide a simple async in-memory cache with TTL so you don't have to write your own.

from async_jwt_core import JWKSCache

cache = JWKSCache(ttl=3600)

# In your request handler:
jwks = await cache.get()
if not jwks:
    jwks = await fetch_jwks_from_web()
    await cache.set(jwks)

🛠️ Installation

uv add async-jwt-core
# or
pip install async-jwt-core

📖 Usage Examples

The Standard Use Case (With External Fetching)

import asyncio
import aiohttp
from async_jwt_core import Validator, ValidationError

# Initialize once
validator = Validator(
    algorithms=["RS256", "ES256"],
    issuer="https://YOUR-AUTH-DOMAIN.com",
    audience="YOUR-API-AUDIENCE"
)

async def fetch_jwks(url: str) -> dict:
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as resp:
            return await resp.json()

async def authenticate(token: str):
    jwks = await fetch_jwks("https://YOUR-AUTH-DOMAIN.com/.well-known/jwks.json")
    
    try:
        claims = await validator.validate(token, jwks)
        print(f"Authenticated user: {claims.sub}")
        return claims
    except ValidationError as e:
        print(f"Auth failed: {e}")
        return None

⚖️ Why We Are Better

Feature Standard PyJWT Framework Libs async-jwt-core
Async Native ❌ (Sync only) 🟡 (Sometimes)
Zero I/O ❌ (Often fetches keys)
No Lock-in ❌ (FastAPI/Django only)
Extensible Algs ❌ (Hard to add) ❌ (Hard to add)

🤝 Contributing

We built this for the community! If you want to add support for more algorithms (like EdDSA) or improve the claims system, check out our modular folder structure in src/async_jwt_core/algorithms/. It is designed to be easy to extend.

📄 License

MIT

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

async_jwt_core-0.2.2.tar.gz (10.1 kB view details)

Uploaded Source

Built Distribution

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

async_jwt_core-0.2.2-py3-none-any.whl (13.5 kB view details)

Uploaded Python 3

File details

Details for the file async_jwt_core-0.2.2.tar.gz.

File metadata

  • Download URL: async_jwt_core-0.2.2.tar.gz
  • Upload date:
  • Size: 10.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.13

File hashes

Hashes for async_jwt_core-0.2.2.tar.gz
Algorithm Hash digest
SHA256 456a4eeaa31a202c8236ad64c6377a47c40bd24e345da836019dfbeddc3ddc2e
MD5 5aa7f57525029fec960a23df138349e2
BLAKE2b-256 2136ebb9e3a79eb2b76d97995468fcce890186405c2188a68e09147f5e0e4ce0

See more details on using hashes here.

File details

Details for the file async_jwt_core-0.2.2-py3-none-any.whl.

File metadata

  • Download URL: async_jwt_core-0.2.2-py3-none-any.whl
  • Upload date:
  • Size: 13.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.13

File hashes

Hashes for async_jwt_core-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 3a19e05f3d6d514e1a124ed6f7fa9c744ce80862593332f8dd9c41429f7ec5d4
MD5 79cf0fce31ab64672dd4e04c023cde7a
BLAKE2b-256 9e8bbab0dde8a9c18fde5542f902417c43ff2cfae7e70725f3bdbe68dd9ac834

See more details on using hashes here.

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