---
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://vonvon.id/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 Vonvon instance) has not been performed yet and must be completed before production use.

Registry status: UNPUBLISHED. Install this SDK only from the repository source checkout; do not use an external package registry.

Request authentication is Bearer-only by default. An application-owned JWT cookie is read only when its exact name is configured. The opaque \_\_Host-vonvon.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 "vonvon @ git+https://github.com/StringKe/vonvon#subdirectory=sdk/python"
```

## Quick start

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

```python
from vonvon import VonvonClient

client = VonvonClient(
issuer="https://vonvon.id",
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 vonvon 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 vonvon import VonvonClient, TokenClaims

app = FastAPI()
vonvon = VonvonClient(issuer="https://vonvon.id")

@app.on_event("shutdown")
async def shutdown():
await vonvon.aclose()

async def require_auth(request: Request) -> TokenClaims:
status = await vonvon.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}
```

## VonvonClient options

| Parameter | Default | Description |
| --- | --- | --- |
| `issuer` | required | Vonvon 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://vonvon.id/sdks/python/index.mdx
