Skip to main content

Async JWKS key fetching, caching, and JWT verification

Project description

auth-jwks

Async JWKS key fetching, caching, and JWT verification built on httpx + PyJWT.

Why

Validating JWT tokens against JWKS endpoints requires:

  • fetching keys from a discovery URL or certs endpoint
  • caching keys with TTL to avoid hitting the endpoint on every request
  • thread-safe refresh under asyncio (double-checked locking)
  • handling key rotation (kid lookup + automatic refresh)

auth-jwks solves this with a single async client that handles discovery, caching, and verification in one call.

Features

  • OpenID Connect discovery (.well-known/openid-configuration)
  • Cloudflare Access token validation + Starlette middleware
  • Async-native (httpx), no blocking I/O
  • Auto-caching with configurable TTL (default 15 min)
  • RS256 + ES256 algorithms
  • Bearer prefix auto-stripping
  • Fully typed (py.typed)

How It Works

JWKS (OIDC Discovery)

Token verification with automatic key discovery, caching, and rotation handling:

sequenceDiagram
    participant App
    participant auth-jwks
    participant OIDC as OIDC Provider

    App->>auth-jwks: verify_token(token)
    auth-jwks->>auth-jwks: strip Bearer prefix, extract kid

    alt Cache hit (kid found & TTL valid)
        auth-jwks->>auth-jwks: return cached key
    else Cache miss
        auth-jwks->>auth-jwks: acquire async lock (double-checked locking)
        auth-jwks->>OIDC: GET /.well-known/openid-configuration
        OIDC-->>auth-jwks: { issuer, jwks_uri, ... }
        auth-jwks->>OIDC: GET {jwks_uri}
        OIDC-->>auth-jwks: { keys: [...] }
        auth-jwks->>auth-jwks: parse JWK keys, cache with TTL
    end

    auth-jwks->>auth-jwks: jwt.decode(token, key, issuer, audience)
    auth-jwks-->>App: decoded payload

Cloudflare Access

Token validation against Cloudflare's certs endpoint with optional identity enrichment:

sequenceDiagram
    participant App
    participant auth-jwks
    participant CF as Cloudflare Access

    App->>auth-jwks: verify_user(token)
    auth-jwks->>auth-jwks: strip Bearer prefix, extract kid

    alt Cache hit (kid found & TTL valid)
        auth-jwks->>auth-jwks: return cached key
    else Cache miss
        auth-jwks->>auth-jwks: acquire async lock (double-checked locking)
        auth-jwks->>CF: GET /cdn-cgi/access/certs
        CF-->>auth-jwks: { keys: [...] }
        auth-jwks->>auth-jwks: parse JWK keys, cache with TTL
    end

    auth-jwks->>auth-jwks: jwt.decode(token, key, aud, issuer)
    auth-jwks-->>App: User(sub, email, country)

    opt get_identity (optional enrichment)
        App->>auth-jwks: get_identity(token)
        auth-jwks->>CF: GET /cdn-cgi/access/get-identity (cookie: CF_Authorization)
        CF-->>auth-jwks: { email, user_uuid, ip, geo, ... }
        auth-jwks-->>App: Identity(email, user_uuid, account_id, ...)
    end

Starlette Middleware

Request authentication flow with dev bypass and automatic token extraction:

sequenceDiagram
    participant Client
    participant MW as CfaAuthMiddleware
    participant auth-jwks
    participant App as App Handler

    Client->>MW: HTTP request

    alt dev_user configured (development bypass)
        MW->>MW: set request.state.user = dev_user
        MW->>App: call_next(request)
        App-->>Client: response
    else Production mode
        MW->>MW: extract token from header (Cf-Access-Jwt-Assertion) or cookie (CF_Authorization)

        alt No token found
            MW-->>Client: 401 {"detail": "Invalid CFA Token"}
        else Token present
            MW->>auth-jwks: verify_user(token)

            alt Verification succeeds
                auth-jwks-->>MW: User(sub, email, country)
                MW->>MW: set request.state.user
                MW->>App: call_next(request)
                App-->>Client: response
            else Verification fails
                auth-jwks-->>MW: raise Exception
                MW-->>Client: 401 {"detail": "CFA token verification failed"}
            end
        end
    end

Installation

pip install auth-jwks
# With Cloudflare Access support:
pip install auth-jwks[cloudflare]

Usage

OAuth2 / OpenID Connect

Validate ID Tokens or JWT Access Tokens against any OIDC provider (Ory Hydra, Keycloak, Auth0, etc.):

from auth_jwks import JWKS

jwks = JWKS(
    discovery_url="https://your-issuer/.well-known/openid-configuration",
    aud="your-client-id",
)
payload = await jwks.verify_token(token)
await jwks.close()

Cloudflare Access

Validate Cloudflare Access JWT tokens and extract user identity:

from auth_jwks.cloudflare import CloudFlareTokenValidation

cfa = CloudFlareTokenValidation(aud="your-aud", team="your-team")
user = await cfa.verify_user(token)      # -> User(sub, email, country)
email = await cfa.verify_email(token)    # -> str
identity = await cfa.get_identity(token) # -> Identity (full CF profile)
await cfa.close()

Starlette / FastAPI Middleware

Protect routes with Cloudflare Access authentication:

from auth_jwks.cloudflare import CfaAuthMiddleware, CloudFlareTokenValidation

cfa = CloudFlareTokenValidation(aud="your-aud", team="your-team")
app.add_middleware(CfaAuthMiddleware, verify_token=cfa.verify_user)
# request.state.user -> User(sub, email, country)

Configuration

JWKS

Parameter Default Description
discovery_url OpenID Connect discovery endpoint
aud None Expected audience (skip validation if None)
allowed_algorithms {"RS256", "ES256"} Accepted signing algorithms
cache_ttl 900 Key cache lifetime in seconds
leeway 30.0 Clock skew tolerance in seconds
timeout 5.0 HTTP request timeout in seconds
retries 3 HTTP retry count

CloudFlareTokenValidation

Parameter Default Description
aud Cloudflare Access application audience tag
team Cloudflare Access team name
allowed_algorithms {"RS256", "ES256"} Accepted signing algorithms
cache_ttl 900 Key cache lifetime in seconds
leeway 30.0 Clock skew tolerance in seconds

All clients must be closed after use (await client.close()). Token methods raise jwt.InvalidTokenError on validation failure.

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

auth_jwks-0.2.1.tar.gz (49.6 kB view details)

Uploaded Source

Built Distribution

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

auth_jwks-0.2.1-py3-none-any.whl (10.5 kB view details)

Uploaded Python 3

File details

Details for the file auth_jwks-0.2.1.tar.gz.

File metadata

  • Download URL: auth_jwks-0.2.1.tar.gz
  • Upload date:
  • Size: 49.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.8

File hashes

Hashes for auth_jwks-0.2.1.tar.gz
Algorithm Hash digest
SHA256 f8621f28c54f577ee330cc1d314410b67e114e0954227d0cab4cbf147578008c
MD5 4e287117d50ab1e718de9b9827558bf1
BLAKE2b-256 2214c5ba1ee67dc8835b51714ce656c9d2a72ecba64f2d740d7d03793957aa12

See more details on using hashes here.

Provenance

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

Publisher: release.yml on centum/auth-jwks

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

File details

Details for the file auth_jwks-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: auth_jwks-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 10.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.8

File hashes

Hashes for auth_jwks-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 f36a7fb86f504b175661bbca6145d3c76d6f6f9cad83bc08c7e341bc3e068805
MD5 7b4e82e686ccdf02306da162f8c2b398
BLAKE2b-256 ff772f6b7602b8743e5c0a5517d91bad488187315e02d43d9807530b52e2b25b

See more details on using hashes here.

Provenance

The following attestation bundles were made for auth_jwks-0.2.1-py3-none-any.whl:

Publisher: release.yml on centum/auth-jwks

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