---
title: "sdk/python"
description: "Async Python server SDK for networkless JWT verification, request authentication, and webhook signature validation."
locale: "en"
---

> Documentation Index
> Fetch the relevant documentation index at: https://xid.fngx.org/sdks/llms.txt
> Use this file to discover all available pages before exploring further.

# sdk/python

## 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.

XID publishes no packages to any registry. This SDK is distributed as source in the XID repository; use it from a checkout of the repository.

Request authentication is Bearer-only by default. An application-owned JWT cookie is read only when its exact name is configured. The opaque \_\_Host-xid.rt.\* Core cookie is never scanned or verified locally; exchange it by forwarding the complete Cookie header to exact same-origin POST /v1/sessions/token with redirects disabled, and accept only a response containing the token field.

## Install

```shell
pip install "xid @ git+https://github.com/StringKe/xid#subdirectory=sdk/python"
```

## Quick start

Construct one `XidClient` at startup and reuse it. The client caches JWKS internally.

```python
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 (Bearer-only by default)
status = await client.authenticate_request(headers=dict(request.headers))
if not status.authenticated:
raise Unauthorized()
user_id = status.claims.sub

# Explicit same-origin Core session -> JWT exchange
token = await client.exchange_session_token(
incoming_request_url="https://app.example.com/account",
cookie_header=request.headers["cookie"],
)
```

## Verify webhook

```python
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

```python
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` | `disabled` | Application-owned JWT cookie name; disabled unless explicitly configured |
| `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.8` and `httpx >=0.27`. Python 3.10+ required.
- Multi-worker deployments share no JWKS cache across processes. A shared cache (Redis) is a planned improvement.

Source: https://xid.fngx.org/sdks/python/index.mdx
