Skip to main content

Agent Passport Middleware - FastAPI

FastAPI middleware for The Passport for AI Agents verification and policy enforcement.

Installation

pip install agent-passport-middleware-fastapi

Getting Started

Key Facts:

  • Agent ID Required: Every policy check needs an agent ID
  • Two Options: Pass agent ID as function parameter (preferred) or use X-Agent-Passport-Id header
  • Resolution Priority: Function parameter > Header > Fail with 401
  • Registry: Defaults to https://aport.io (configurable)
  • Policies: Choose from finance.payment.refund.v1, data.export.create.v1, messaging.message.send.v1, code.repository.merge.v1
Method Agent ID Source Security Use Case
Explicit Parameter Function argument ✅ Highest Production, explicit control
Header Fallback X-Agent-Passport-Id ⚠️ Medium Backward compatibility
Global Middleware X-Agent-Passport-Id ⚠️ Medium All routes, same policy

Quick Start

1. Global Policy Enforcement

from fastapi import FastAPI
from aporthq_middleware_fastapi import agent_passport_middleware, AgentPassportMiddlewareOptions

app = FastAPI()

# Enforce specific policy globally
app.add_middleware(
    agent_passport_middleware,
    options=AgentPassportMiddlewareOptions(
        policy_id="finance.payment.refund.v1",  # Enforces refunds policy
        fail_closed=True
    )
)

# All routes now require finance.payment.refund.v1 policy compliance
@app.post("/api/refunds")
async def process_refund(request: Request):
    # Policy already verified - safe to process
    body = await request.json()
    return {"success": True, "agent_id": request.state.agent.agent_id}

2. Route-Specific Policy Enforcement

from aporthq_middleware_fastapi import require_policy, require_policy_with_context

AGENT_ID = "ap_a2d10232c6534523812423eec8a1425c45678"  # Your agent ID

# Explicit agent ID (preferred)
@app.post("/api/refunds")
async def process_refund(request: Request):
    # Policy verified with explicit agent ID
    return {"success": True}

# Add the policy middleware
app.middleware("http")(require_policy("finance.payment.refund.v1", AGENT_ID))

# Header fallback
@app.post("/api/export")
async def export_data(request: Request):
    # Policy verified via header
    return {"success": True}

# Add the policy middleware
app.middleware("http")(require_policy("data.export.create.v1"))  # Uses X-Agent-Passport-Id header

3. Multiple Policies

# Different policies for different routes
app.middleware("http")(require_policy("finance.payment.refund.v1", AGENT_ID))
@app.post("/api/refunds")
async def refunds(request: Request):
    return {"message": "Refund processed"}

app.middleware("http")(require_policy("data.export.create.v1", AGENT_ID))
@app.post("/api/data/export")
async def export(request: Request):
    return {"message": "Export created"}

app.middleware("http")(require_policy("messaging.message.send.v1", AGENT_ID))
@app.post("/api/messages/send")
async def messaging(request: Request):
    return {"message": "Message sent"}

API Reference

agent_passport_middleware(options)

Global middleware that enforces a specific policy on all routes.

Parameters:

  • options.policy_id (str): Policy ID to enforce (e.g., "finance.payment.refund.v1")
  • options.fail_closed (bool): Fail if agent ID missing (default: True)
  • options.base_url (str): Registry base URL (default: "https://aport.io")
  • options.timeout (int): Request timeout in seconds (default: 5)

Returns: Middleware instance

require_policy(policy_id, agent_id=None)

Route-specific middleware that enforces a specific policy.

Parameters:

  • policy_id (str): Policy ID to enforce (e.g., "finance.payment.refund.v1")
  • agent_id (str, optional): Explicit agent ID (preferred over header)

Returns: Middleware function

Agent ID Resolution:

  1. Function parameter (if provided)
  2. X-Agent-Passport-Id header (fallback)
  3. Fail with 401 error (if neither provided)

require_policy_with_context(policy_id, context, agent_id=None)

Route-specific middleware with custom context.

Parameters:

  • policy_id (str): Policy ID to enforce
  • context (dict): Custom context data
  • agent_id (str, optional): Explicit agent ID

Returns: Middleware function

Request Object

After successful policy verification, the request object contains:

@app.post("/api/refunds")
async def process_refund(request: Request):
    # request.state.agent - Verified agent passport data
    print(request.state.agent.agent_id)        # "ap_a2d10232c6534523812423eec8a1425c45678"
    print(request.state.agent.assurance_level) # "L2"
    print(request.state.agent.capabilities)    # ["finance.payment.refund"]
    
    # request.state.policy_result - Policy evaluation result
    print(request.state.policy_result.evaluation.decision_id)
    print(request.state.policy_result.evaluation.remaining_daily_cap)

Available Policies

finance.payment.refund.v1

  • Capabilities: ["finance.payment.refund"]
  • Assurance: L2 minimum
  • Fields: order_id, customer_id, amount_minor, currency, region, reason_code, idempotency_key
  • Rules: Currency support, region validation, reason code validation, idempotency handling

data.export.create.v1

  • Capabilities: ["data.export"]
  • Assurance: L1 minimum
  • Fields: rows, format, contains_pii
  • Rules: Row limits, PII handling

messaging.message.send.v1

  • Capabilities: ["messaging.send"]
  • Assurance: L1 minimum
  • Fields: channel, message_count, mentions
  • Rules: Rate limits, channel restrictions

code.repository.merge.v1

  • Capabilities: ["repo.pr.create", "repo.merge"]
  • Assurance: L2 minimum
  • Fields: repository, base_branch, pr_size_kb
  • Rules: Repository access, branch protection, PR size limits

Error Handling

The middleware returns appropriate HTTP status codes:

# 401 - Missing or invalid agent ID
{
    "error": "missing_agent_id",
    "message": "Agent ID is required. Provide it as X-Agent-Passport-Id header."
}

# 403 - Policy violation
{
    "error": "policy_violation",
    "message": "Policy violation",
    "agent_id": "ap_a2d10232c6534523812423eec8a1425c45678",
    "policy_id": "finance.payment.refund.v1"
}

# 400 - Field validation failed
{
    "error": "field_validation_failed",
    "message": "Field validation failed: Required field 'order_id' is missing"
}

Configuration

Environment Variables

# Registry base URL (optional)
AGENT_PASSPORT_BASE_URL=https://aport.io

# Default agent ID for development (optional)
AGENT_PASSPORT_AGENT_ID=ap_a2d10232c6534523812423eec8a1425c45678

Skip Paths

app.add_middleware(
    agent_passport_middleware,
    options=AgentPassportMiddlewareOptions(
        policy_id="finance.payment.refund.v1",
        skip_paths=["/health", "/metrics", "/status"]
    )
)

Examples

E-commerce Refund System

from fastapi import FastAPI, Request
from aporthq_middleware_fastapi import require_policy

app = FastAPI()

AGENT_ID = "ap_a2d10232c6534523812423eec8a1425c45678"

# Refund processing with policy enforcement
app.middleware("http")(require_policy("finance.payment.refund.v1", AGENT_ID))

@app.post("/api/refunds")
async def process_refund(request: Request):
    body = await request.json()
    amount = body.get("amount")
    currency = body.get("currency")
    order_id = body.get("order_id")
    
    # Policy already verified - safe to process
    return {
        "success": True,
        "refund_id": f"ref_{int(time.time() * 1000)}",
        "amount": amount,
        "currency": currency,
        "order_id": order_id,
        "agent_id": request.state.agent.agent_id
    }

Data Export System

# Data export with policy enforcement
app.middleware("http")(require_policy("data.export.create.v1", AGENT_ID))

@app.post("/api/data/export")
async def export_data(request: Request):
    body = await request.json()
    rows = body.get("rows")
    format = body.get("format")
    contains_pii = body.get("contains_pii")
    
    # Policy verified - safe to export
    return {
        "success": True,
        "export_id": f"exp_{int(time.time() * 1000)}",
        "rows": rows,
        "format": format,
        "contains_pii": contains_pii,
        "agent_id": request.state.agent.agent_id
    }

Messaging System

# Messaging with policy enforcement
app.middleware("http")(require_policy("messaging.message.send.v1", AGENT_ID))

@app.post("/api/messages/send")
async def send_message(request: Request):
    body = await request.json()
    channel = body.get("channel")
    message_count = body.get("message_count")
    mentions = body.get("mentions")
    
    # Policy verified - safe to send
    return {
        "success": True,
        "message_id": f"msg_{int(time.time() * 1000)}",
        "channel": channel,
        "message_count": message_count,
        "mentions": mentions,
        "agent_id": request.state.agent.agent_id
    }

License

MIT


Last Updated: 2025-01-16 00:00:00 UTC

Release files for aporthq-middleware-fastapi 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for aporthq-middleware-fastapi 0.1.0
File Size Uploaded
aporthq_middleware_fastapi-0.1.0.tar.gz 14.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for aporthq-middleware-fastapi 0.1.0
File Interpreter ABI Platform
aporthq_middleware_fastapi-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 25.5 kB

Release files / aporthq_middleware_fastapi-0.1.0.tar.gz

Download URL aporthq_middleware_fastapi-0.1.0.tar.gz
Size 14.2 kB
Tags Source
SHA-256 checksum
How to use checksums
03cf93d35306788557ecbf29d0f18ea832674d0040789c5e60b3fa5a9e7a5491
BLAKE2b-256 checksum
How to use checksums
4f55b15a4e69bd598b9aed2a4622c7714041de507d7f0d54837a37c40cff3a1a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.10.11

Release files / aporthq_middleware_fastapi-0.1.0-py3-none-any.whl

Download URL aporthq_middleware_fastapi-0.1.0-py3-none-any.whl
Size 11.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
40382b032f494a4ed53a8b2415bb5cff76dd1ca1584da4ffbae33abf4f7da3bf
BLAKE2b-256 checksum
How to use checksums
079bb5c8859cde1bbead847fe823f7d66d5700665b57708103479df6fb51f3d7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.10.11

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page