Skip to main content

Shared, hardened JWT verification core for OIDC/JWKS issuers

Project description

oidc-jwt-verifier

Release PyPI Python Versions Tests Coverage Docs License

oidc-jwt-verifier is a small, framework-agnostic JWT verification core for OIDC/JWKS issuers.

It provides:

  • A hardened sync verifier (JWTVerifier)
  • A native async verifier (AsyncJWTVerifier)
  • Public JWKS lifecycle/readiness APIs on both sync and async clients
  • First-party FastAPI and Starlette integration helpers

Install

pip install oidc-jwt-verifier

For async/FastAPI/Starlette support:

pip install "oidc-jwt-verifier[async]"
pip install "oidc-jwt-verifier[fastapi]"
pip install "oidc-jwt-verifier[starlette]"

For development:

uv pip install -e ".[dev]"

Quickstart

from oidc_jwt_verifier import AuthConfig, JWTVerifier

config = AuthConfig(
    issuer="https://example-issuer/",
    audience="https://example-api",
    jwks_url="https://example-issuer/.well-known/jwks.json",
    allowed_algs=("RS256",),
    required_scopes=("read:users",),
)

verifier = JWTVerifier(config)
claims = verifier.verify_access_token(token)

Async quickstart

from oidc_jwt_verifier import AuthConfig
from oidc_jwt_verifier.async_verifier import AsyncJWTVerifier

config = AuthConfig(
    issuer="https://example-issuer/",
    audience="https://example-api",
    jwks_url="https://example-issuer/.well-known/jwks.json",
    allowed_algs=("RS256",),
)

async def verify(token: str) -> dict[str, object]:
    async with AsyncJWTVerifier(config) as verifier:
        return await verifier.verify_access_token(token)

JWKS lifecycle and readiness

Use the verifier-level helpers for startup validation and cache priming:

from oidc_jwt_verifier import AuthConfig, JWTVerifier

config = AuthConfig(
    issuer="https://example-issuer/",
    audience="https://example-api",
    jwks_url="https://example-issuer/.well-known/jwks.json",
)

verifier = JWTVerifier(config)
if not verifier.healthcheck(refresh=True):
    raise RuntimeError("JWKS endpoint is not ready")

signing_keys = verifier.get_signing_keys()

If you need direct client access, use JWKSClient or AsyncJWKSClient from oidc_jwt_verifier.jwks and oidc_jwt_verifier.async_jwks. The sync and async lifecycle/readiness APIs are intentionally parallel public surfaces.

These are startup/readiness APIs. Do not call them on every request.

FastAPI integration

from fastapi import Depends, FastAPI
from oidc_jwt_verifier import AuthConfig
from oidc_jwt_verifier.async_verifier import AsyncJWTVerifier
from oidc_jwt_verifier.integrations.fastapi import create_async_bearer_dependency

app = FastAPI()
verifier = AsyncJWTVerifier(
    AuthConfig(
        issuer="https://example-issuer/",
        audience="https://example-api",
        jwks_url="https://example-issuer/.well-known/jwks.json",
    )
)
auth = create_async_bearer_dependency(verifier, realm="api")

@app.get("/protected")
async def protected(claims: dict = Depends(auth)):
    return {"sub": claims.get("sub")}

Starlette integration

from starlette.applications import Starlette
from starlette.requests import Request
from starlette.responses import JSONResponse
from starlette.routing import Route

from oidc_jwt_verifier.integrations.starlette import BearerAuthMiddleware

async def protected(request: Request) -> JSONResponse:
    claims = request.state.auth_claims
    return JSONResponse({"sub": claims.get("sub")})

app = Starlette(routes=[Route("/protected", protected)])
app.add_middleware(BearerAuthMiddleware, verifier=verifier, realm="api")

Secure-by-default behavior

The verifier:

  • Verifies signature, iss, aud, exp, and nbf (when present).
  • Uses an explicit algorithm allowlist and rejects alg=none.
  • Enforces minimum cryptographic key lengths by default (configurable via enforce_minimum_key_length).
  • Fails closed on malformed tokens, JWKS fetch errors, timeouts, missing keys, and missing kid.
  • Never derives a JWKS URL from token headers, and rejects tokens that include jku, x5u, or crit.
  • Supports Auth0-style multi-audience tokens (aud as an array) and enforces required scopes and permissions.

Auth0 guidance for API token validation calls out validating the JWT and then checking aud and scopes in the scope claim. See the Auth0 docs for details.

Error handling

The public exception type is AuthError.

AuthError carries:

  • code: stable, machine-readable reason
  • status_code: 401 (authentication) or 403 (authorization)
  • www_authenticate_header(): an RFC 6750 compatible WWW-Authenticate value for Bearer auth
from oidc_jwt_verifier import AuthError

try:
    claims = verifier.verify_access_token(token)
except AuthError as err:
    status = err.status_code
    www_authenticate = err.www_authenticate_header()

Why this library

JWT verification for APIs is easy to get mostly right while still missing important security and interoperability details. This library is a small, framework-agnostic core that centralizes conservative verification policy (claims, algorithms, header handling) and authorization checks (scopes/permissions) so you can reuse it across projects.

For comparisons against common alternatives (PyJWT directly, discovery-driven verifiers, framework integrations), see docs/alternatives.md.

Documentation

Primary docs are built with MkDocs in docs/.

  • Getting started: docs/getting-started.md
  • Sync usage: docs/usage/sync.md
  • Async usage: docs/usage/async.md
  • FastAPI integration: docs/integrations/fastapi.md
  • Starlette integration: docs/integrations/starlette.md
  • Configuration and security: docs/configuration.md, docs/security.md
  • API reference: docs/reference.md
  • Migration guidance: docs/migration.md

Contributing

Use Conventional Commits.
Release-specific commit guidance for maintainers is documented in AGENTS.md.

References

  • Auth0: Validate Access Tokens: https://auth0.com/docs/secure/tokens/access-tokens/validate-access-tokens
  • Auth0: Validate JSON Web Tokens: https://auth0.com/docs/secure/tokens/json-web-tokens/validate-json-web-tokens
  • RFC 8725: JSON Web Token Best Current Practices: https://datatracker.ietf.org/doc/html/rfc8725
  • RFC 9700: Best Current Practice for OAuth 2.0 Security: https://www.rfc-editor.org/info/rfc9700
  • PyJWT docs and examples: https://pyjwt.readthedocs.io/en/stable/usage.html

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

oidc_jwt_verifier-0.1.6.tar.gz (42.0 kB view details)

Uploaded Source

Built Distribution

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

oidc_jwt_verifier-0.1.6-py3-none-any.whl (31.7 kB view details)

Uploaded Python 3

File details

Details for the file oidc_jwt_verifier-0.1.6.tar.gz.

File metadata

  • Download URL: oidc_jwt_verifier-0.1.6.tar.gz
  • Upload date:
  • Size: 42.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for oidc_jwt_verifier-0.1.6.tar.gz
Algorithm Hash digest
SHA256 43126d633bed2ce9dbdb3c48251912a5ddf3bb3c13c99e8ea82788d7d605794e
MD5 3dd327a40b92a1fff335c8641ddcfabe
BLAKE2b-256 36c8c65cea758ff2b93973d55ea381e6ba6ec0a86fd1417dcc11d54a561310ed

See more details on using hashes here.

Provenance

The following attestation bundles were made for oidc_jwt_verifier-0.1.6.tar.gz:

Publisher: release.yml on BjornMelin/oidc-jwt-verifier

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file oidc_jwt_verifier-0.1.6-py3-none-any.whl.

File metadata

File hashes

Hashes for oidc_jwt_verifier-0.1.6-py3-none-any.whl
Algorithm Hash digest
SHA256 2bdcbfb1149f6da8a42b468988235bc1bcfc8e093b6d3bba42195bbf245b9a6a
MD5 3fdb7698b3cbe2bfa83f918a45e96912
BLAKE2b-256 b517474c9f727fedaeef09b0d08acace28dbb6710f3821cc117a91a79d797ab7

See more details on using hashes here.

Provenance

The following attestation bundles were made for oidc_jwt_verifier-0.1.6-py3-none-any.whl:

Publisher: release.yml on BjornMelin/oidc-jwt-verifier

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