OpenBotAuth Verifier SDK for Python
Python SDK for verifying OpenBotAuth-signed HTTP requests (aligned with the Web Bot Auth IETF draft) using RFC 9421 HTTP Message Signatures.
OpenBotAuth is an open-source implementation that follows the approach described in the Web Bot Auth draft. The draft is evolving; this SDK focuses on practical interoperability by calling the OpenBotAuth verifier service (hosted or self-hosted). It enables Python origins (FastAPI, Starlette, Flask) to verify AI agent and bot signatures.
Installation
pip install openbotauth-verifier
For framework-specific middleware:
# FastAPI/Starlette
pip install openbotauth-verifier[fastapi]
# Flask
pip install openbotauth-verifier[flask]
# All extras including dev dependencies
pip install openbotauth-verifier[all]
Quick Start
FastAPI / Starlette
from fastapi import FastAPI, Request
from openbotauth_verifier import OpenBotAuthASGIMiddleware
app = FastAPI()
# Add middleware (observe mode - logs verification but allows all requests)
app.add_middleware(OpenBotAuthASGIMiddleware)
# Or enforce verification:
# app.add_middleware(OpenBotAuthASGIMiddleware, require_verified=True)
@app.get("/protected")
async def protected(request: Request):
oba = request.state.oba
if oba.signed and oba.result.verified:
return {
"message": "Verified bot access",
"agent": oba.result.agent,
}
return {"message": "Unverified access"}
Flask
from flask import Flask, request, g
from openbotauth_verifier.middleware.wsgi import OpenBotAuthWSGIMiddleware
app = Flask(__name__)
app.wsgi_app = OpenBotAuthWSGIMiddleware(app.wsgi_app)
@app.before_request
def load_oba():
g.oba = request.environ.get("openbotauth.oba")
@app.route("/protected")
def protected():
if g.oba and g.oba.signed and g.oba.result.verified:
return {
"message": "Verified bot access",
"agent": g.oba.result.agent,
}
return {"message": "Unverified access"}
Direct Client Usage
from openbotauth_verifier import VerifierClient
client = VerifierClient()
# Async
result = await client.verify(
method="GET",
url="https://example.com/api",
headers={
"signature-input": 'sig=("host");created=1699900000',
"signature": "base64signature==",
"signature-agent": "https://registry.openbotauth.org/jwks/mybot.json",
"host": "example.com",
},
)
if result.verified:
print(f"Verified agent: {result.agent['client_name']}")
else:
print(f"Verification failed: {result.error}")
# Sync
result = client.verify_sync(method="GET", url="...", headers={...})
Configuration
Verifier URL
By default, the SDK uses the hosted verifier at https://verifier.openbotauth.org/verify.
For local development or self-hosted verifiers:
# Middleware
app.add_middleware(
OpenBotAuthASGIMiddleware,
verifier_url="http://localhost:8081/verify",
)
# Client
client = VerifierClient(verifier_url="http://localhost:8081/verify")
Middleware Modes
Observe Mode (default): Attaches verification state but allows all requests.
app.add_middleware(OpenBotAuthASGIMiddleware, require_verified=False)
Require Mode: Returns 401 for unsigned or failed verification.
app.add_middleware(OpenBotAuthASGIMiddleware, require_verified=True)
Timeout
app.add_middleware(OpenBotAuthASGIMiddleware, timeout_s=10.0)
client = VerifierClient(timeout_s=10.0)
Verification State
The middleware attaches an OBAState object:
@dataclass
class OBAState:
signed: bool # Request had signature headers
result: VerificationResult | None
@dataclass
class VerificationResult:
verified: bool # Signature was valid
agent: dict | None # Agent info (kid, jwks_url, client_name)
error: str | None # Error message if failed
created: int | None # Signature creation timestamp
expires: int | None # Signature expiration timestamp
Access in your handlers:
# FastAPI/Starlette
oba = request.state.oba
# Flask
oba = request.environ.get("openbotauth.oba")
Security
Sensitive Header Protection
The SDK never forwards sensitive headers to the verifier service:
cookieauthorizationproxy-authorizationwww-authenticate
If any of these headers are covered by Signature-Input, the verification fails immediately with a ValueError - no network call is made.
Header Forwarding
Only these headers are forwarded to the verifier:
signature-input,signature,signature-agent(always)- Headers explicitly listed in the
Signature-Inputcovered components - Derived components (starting with
@) are parsed but not forwarded as headers
Testing with bot-cli
Use the Node.js bot-cli from the OpenBotAuth monorepo to test your Python server:
1. Start your Python server
cd sdks/python
python -m venv .venv
source .venv/bin/activate
pip install -e ".[fastapi]"
uvicorn examples.fastapi_demo:app --port 8009
2. Test with bot-cli
# From the monorepo root
pnpm --filter @openbotauth/bot-cli dev fetch http://localhost:8009/protected -v
Notes
-
Hosted verifier: Requires your JWKS URL (from
Signature-Agent) to be publicly reachable. Use an OpenBotAuth registry JWKS URL if you have one. -
Local verifier: Start the verifier service locally and configure:
app.add_middleware( OpenBotAuthASGIMiddleware, verifier_url="http://localhost:8081/verify", )
API Reference
VerifierClient
VerifierClient(
verifier_url: str = "https://verifier.openbotauth.org/verify",
timeout_s: float = 5.0,
)
Methods:
async verify(method, url, headers, body=None) -> VerificationResultverify_sync(method, url, headers, body=None) -> VerificationResult
OpenBotAuthASGIMiddleware
OpenBotAuthASGIMiddleware(
app,
verifier_url: str = "https://verifier.openbotauth.org/verify",
require_verified: bool = False,
timeout_s: float = 5.0,
)
Sets request.state.oba: OBAState
OpenBotAuthWSGIMiddleware
OpenBotAuthWSGIMiddleware(
app,
verifier_url: str = "https://verifier.openbotauth.org/verify",
require_verified: bool = False,
timeout_s: float = 5.0,
)
Sets environ["openbotauth.oba"]: OBAState
Header Utilities
from openbotauth_verifier import parse_covered_headers, extract_forwarded_headers
# Parse covered headers from Signature-Input
headers = parse_covered_headers('sig=("host" "content-type");created=123')
# Returns: ["host", "content-type"]
# Extract safe headers to forward
forwarded = extract_forwarded_headers(request_headers)
# Raises ValueError if sensitive headers are covered
Development
cd sdks/python
python -m venv .venv
source .venv/bin/activate
pip install -e ".[all]"
# Run tests
pytest
# Type checking
mypy src
# Linting
ruff check src tests
Resources
- OpenBotAuth Website: https://openbotauth.org
- Main Repository: https://github.com/OpenBotAuth/openbotauth
- Web Bot Auth (IETF Draft): https://datatracker.ietf.org/doc/draft-kelsey-httpbis-web-bot-auth/
- RFC 9421 (HTTP Message Signatures): https://www.rfc-editor.org/rfc/rfc9421.html
- PyPI Package: https://pypi.org/project/openbotauth-verifier/
License
Apache-2.0
Metadata
Release files for openbotauth-verifier 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| openbotauth_verifier-0.1.0.tar.gz | 19.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| openbotauth_verifier-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 41.1 kB
Release files / openbotauth_verifier-0.1.0.tar.gz
| Download URL | openbotauth_verifier-0.1.0.tar.gz |
|---|---|
| Size | 19.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
09ae259e73ddd887ea73907339bd8406930416a6261132413f2abdcfc5200d19
|
|
BLAKE2b-256 checksum How to use checksums |
4c1c1bf27cba517de29e15d94be2f21e05622a7e3921ccd2464bc0e4c90fce9f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.2
|
Release files / openbotauth_verifier-0.1.0-py3-none-any.whl
| Download URL | openbotauth_verifier-0.1.0-py3-none-any.whl |
|---|---|
| Size | 21.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
75db2f214cf6dc5001ed1f672b22e91491add3e3b784f82b19a6bcf05c481924
|
|
BLAKE2b-256 checksum How to use checksums |
b424885b7768e98ae6ea03397439f4158fca2c254da8bec36fbbd7623c8a2481
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.2
|