Status
Implemented and verified locally. Real IdP round-trip verification (JWKS fetch, token sign/verify against a live XID instance) has not been performed yet and must be completed before production use.
Install
pip install xidQuick start
Construct one XidClient at startup and reuse it. The client caches JWKS internally.
from xid import XidClient
client = XidClient(
issuer="https://xid.dev",
audience="https://api.yourapp.com", # optional
)
# Verify a token
claims = await client.verify_token("eyJ...")
print(claims.sub, claims.email, claims.scope)
# Authenticate a request
status = await client.authenticate_request(
headers=dict(request.headers),
cookies=dict(request.cookies),
)
if not status.authenticated:
raise Unauthorized()
user_id = status.claims.subVerify webhook
from xid import WebhookVerificationError
try:
webhook = client.verify_webhook(
payload=request.body,
headers=dict(request.headers),
secret="whsec_xxx",
)
import json
event = json.loads(webhook.body)
except WebhookVerificationError as exc:
raise BadRequest(str(exc))FastAPI integration
from fastapi import FastAPI, Depends, HTTPException, Request
from xid import XidClient, TokenClaims
app = FastAPI()
xid = XidClient(issuer="https://xid.dev")
@app.on_event("shutdown")
async def shutdown():
await xid.aclose()
async def require_auth(request: Request) -> TokenClaims:
status = await xid.authenticate_request(dict(request.headers))
if not status.authenticated:
raise HTTPException(status_code=401)
return status.claims
@app.get("/me")
async def me(claims: TokenClaims = Depends(require_auth)):
return {"sub": claims.sub, "email": claims.email}XidClient options
| Parameter | Default | Description |
|---|---|---|
issuer |
required | XID issuer URL |
audience |
None |
Expected aud claim; None skips validation |
jwks_ttl |
3600 |
JWKS in-memory cache TTL in seconds |
http_timeout |
10.0 |
JWKS fetch timeout in seconds |
cookie_name |
__session |
Cookie key for session token extraction |
leeway |
0 |
Clock skew tolerance in seconds |
Core API
| Method | Description |
|---|---|
await client.verify_token(token) |
Verify JWT string; raises TokenVerificationError on failure. |
await client.authenticate_request(headers, cookies) |
Extract and verify token from headers/cookies. Returns AuthStatus; does not raise. |
client.verify_webhook(payload, headers, secret) |
Synchronous. Validates svix HMAC-SHA256 + 5-minute replay window. Raises WebhookVerificationError on failure. |
await client.aclose() |
Release underlying HTTP client resources. |
Platform notes
- Async-first. Sync callers (Django/Flask) can wrap with
asyncio.run(). - Depends on
pyjwt[crypto] >=2.8andhttpx >=0.27. Python 3.10+ required. - Multi-worker deployments share no JWKS cache across processes. A shared cache (Redis) is a planned improvement.