Shared, hardened JWT verification core for OIDC/JWKS issuers
Project description
oidc-jwt-verifier
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) - 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)
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, andnbf(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, orcrit. - Supports Auth0-style multi-audience tokens (
audas 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 reasonstatus_code:401(authentication) or403(authorization)www_authenticate_header(): an RFC 6750 compatibleWWW-Authenticatevalue 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
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file oidc_jwt_verifier-0.1.5.tar.gz.
File metadata
- Download URL: oidc_jwt_verifier-0.1.5.tar.gz
- Upload date:
- Size: 37.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6575304e6b6bfdd20a0a11d0803c841bd7ae0dc8ce3dc98e1abb88ad4a8fe336
|
|
| MD5 |
aeb3866ebf0a717ee1c996ab9a6cdf92
|
|
| BLAKE2b-256 |
3e377a050ddb6412514cf628145231a32a899bff5f4f997609413b1ac520615d
|
Provenance
The following attestation bundles were made for oidc_jwt_verifier-0.1.5.tar.gz:
Publisher:
release.yml on BjornMelin/oidc-jwt-verifier
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
oidc_jwt_verifier-0.1.5.tar.gz -
Subject digest:
6575304e6b6bfdd20a0a11d0803c841bd7ae0dc8ce3dc98e1abb88ad4a8fe336 - Sigstore transparency entry: 943892831
- Sigstore integration time:
-
Permalink:
BjornMelin/oidc-jwt-verifier@52ab44ed9a470533a6e52ba0234dcf9f1563d30f -
Branch / Tag:
refs/heads/main - Owner: https://github.com/BjornMelin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@52ab44ed9a470533a6e52ba0234dcf9f1563d30f -
Trigger Event:
push
-
Statement type:
File details
Details for the file oidc_jwt_verifier-0.1.5-py3-none-any.whl.
File metadata
- Download URL: oidc_jwt_verifier-0.1.5-py3-none-any.whl
- Upload date:
- Size: 29.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
448fc98c61faa0860b519c620cf011b4557b64d016ca524d1b7fa0dbfe7b30b4
|
|
| MD5 |
8ca31092150980c67a5d7090cf0a88b2
|
|
| BLAKE2b-256 |
f84710193fb67854849363c9bb16e7e8cfe085fe78a3cafaad280d44ba02ce30
|
Provenance
The following attestation bundles were made for oidc_jwt_verifier-0.1.5-py3-none-any.whl:
Publisher:
release.yml on BjornMelin/oidc-jwt-verifier
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
oidc_jwt_verifier-0.1.5-py3-none-any.whl -
Subject digest:
448fc98c61faa0860b519c620cf011b4557b64d016ca524d1b7fa0dbfe7b30b4 - Sigstore transparency entry: 943892834
- Sigstore integration time:
-
Permalink:
BjornMelin/oidc-jwt-verifier@52ab44ed9a470533a6e52ba0234dcf9f1563d30f -
Branch / Tag:
refs/heads/main - Owner: https://github.com/BjornMelin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@52ab44ed9a470533a6e52ba0234dcf9f1563d30f -
Trigger Event:
push
-
Statement type: