Skip to main content

Python SDK for verifying Web Bot Auth (OpenBotAuth) signed requests using RFC 9421 HTTP Message Signatures

Project description

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:

  • cookie
  • authorization
  • proxy-authorization
  • www-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:

  1. signature-input, signature, signature-agent (always)
  2. Headers explicitly listed in the Signature-Input covered components
  3. 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) -> VerificationResult
  • verify_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

License

Apache-2.0

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

openbotauth_verifier-0.1.0.tar.gz (19.8 kB view details)

Uploaded Source

Built Distribution

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

openbotauth_verifier-0.1.0-py3-none-any.whl (21.4 kB view details)

Uploaded Python 3

File details

Details for the file openbotauth_verifier-0.1.0.tar.gz.

File metadata

  • Download URL: openbotauth_verifier-0.1.0.tar.gz
  • Upload date:
  • Size: 19.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.2

File hashes

Hashes for openbotauth_verifier-0.1.0.tar.gz
Algorithm Hash digest
SHA256 09ae259e73ddd887ea73907339bd8406930416a6261132413f2abdcfc5200d19
MD5 f556554492a2842fcffd7ecefaa74701
BLAKE2b-256 4c1c1bf27cba517de29e15d94be2f21e05622a7e3921ccd2464bc0e4c90fce9f

See more details on using hashes here.

File details

Details for the file openbotauth_verifier-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for openbotauth_verifier-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 75db2f214cf6dc5001ed1f672b22e91491add3e3b784f82b19a6bcf05c481924
MD5 13213bd0602243fc9537230eacfe924e
BLAKE2b-256 b424885b7768e98ae6ea03397439f4158fca2c254da8bec36fbbd7623c8a2481

See more details on using hashes here.

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