FastAPI middleware for The Passport for AI Agents
Project description
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-Idheader - 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:
- Function parameter (if provided)
X-Agent-Passport-Idheader (fallback)- 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 enforcecontext(dict): Custom context dataagent_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
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file aporthq_middleware_fastapi-0.1.0.tar.gz.
File metadata
- Download URL: aporthq_middleware_fastapi-0.1.0.tar.gz
- Upload date:
- Size: 14.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
03cf93d35306788557ecbf29d0f18ea832674d0040789c5e60b3fa5a9e7a5491
|
|
| MD5 |
3fa11e8279fd3f26b5beef069c718547
|
|
| BLAKE2b-256 |
4f55b15a4e69bd598b9aed2a4622c7714041de507d7f0d54837a37c40cff3a1a
|
File details
Details for the file aporthq_middleware_fastapi-0.1.0-py3-none-any.whl.
File metadata
- Download URL: aporthq_middleware_fastapi-0.1.0-py3-none-any.whl
- Upload date:
- Size: 11.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
40382b032f494a4ed53a8b2415bb5cff76dd1ca1584da4ffbae33abf4f7da3bf
|
|
| MD5 |
cce1c9054213453887ff801d2d5d9e66
|
|
| BLAKE2b-256 |
079bb5c8859cde1bbead847fe823f7d66d5700665b57708103479df6fb51f3d7
|