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
Metadata
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)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| aporthq_middleware_fastapi-0.1.0.tar.gz | 14.2 kB | Without provenance | Details for aporthq_middleware_fastapi-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| aporthq_middleware_fastapi-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for aporthq_middleware_fastapi-0.1.0-py3-none-any.whl |
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
|
Security for aporthq-middleware-fastapi 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.