authzen-policy-backend
OpenID AuthZEN policy backend for the Microsoft Agent Governance Toolkit.
Connect any AuthZEN-compliant Policy Decision Point (PDP) to Microsoft's Agent Governance Toolkit as a pluggable ExternalPolicyBackend — with one add_backend() call.
┌────────────────────────────────────────────────────────────────┐
│ Your AI Agent │
│ (LangGraph, OpenAI Agents, AutoGen, custom, ...) │
└──────────────────────┬─────────────────────────────────────────┘
│ evaluate(context)
┌──────────────────────▼─────────────────────────────────────────┐
│ MS Agent Governance Toolkit │
│ PolicyEvaluator → YAML rules (fast local) │
│ → AuthZENBackend (this package) │
└──────────────────────┬─────────────────────────────────────────┘
│ POST /access/v1/evaluation
┌──────────────────────▼─────────────────────────────────────────┐
│ AuthZEN PDP │
│ (EmpowerNow ARIA, Cerbos, Aserto, or any compliant PDP) │
│ → Obligations, Constraints, Advice, Delegation, Budget │
└────────────────────────────────────────────────────────────────┘
Why
The Agent Governance Toolkit provides in-process YAML policy evaluation and optional OPA/Cedar backends. For enterprise deployments, you need:
- Centralized policy management — one PDP governs all agents, not scattered YAML files
- Standards-based authorization — OpenID AuthZEN, not proprietary policy formats
- Rich decisions — obligations, constraints, budget limits, and denial advice beyond binary allow/deny
- Delegation-aware identity — user-bound agents with scoped capabilities
- Audit-grade decisions — decision IDs that link to cryptographic receipts
This package bridges that gap with a single add_backend() call.
Installation
pip install authzen-policy-backend
Quick Start
Minimal — 3 Lines to Policy Evaluation
from agent_os.policies import PolicyEvaluator
from authzen_backend import AuthZENBackend
evaluator = PolicyEvaluator()
evaluator.add_backend(AuthZENBackend(
pdp_url="https://pdp.example.com/access/v1/evaluation",
pdp_application="my-agent-platform",
token="my-bearer-token",
))
decision = evaluator.evaluate({
"tool_name": "file_read",
"agent_id": "travel-agent-1",
"user_id": "alice",
})
print(decision.allowed) # True / False
print(decision.reason) # Human-readable reason from PDP
print(decision.evaluation_ms) # Round-trip time in ms
Client Credentials — Production Auth
from authzen_backend import AuthZENBackend, ClientCredentialsAuth
backend = AuthZENBackend(
pdp_url="https://pdp.example.com/access/v1/evaluation",
pdp_application="my-agent-platform",
client_credentials=ClientCredentialsAuth(
token_endpoint="https://idp.example.com/oauth/token",
client_id="agent-policy-client",
client_secret="...",
audience="pdp",
),
)
# Tokens are cached and refreshed automatically
decision = backend.evaluate({
"tool_name": "transfer_funds",
"agent_id": "finance-bot",
"user_id": "bob",
})
Async Backend — For Async Agent Runtimes
from authzen_backend import AsyncAuthZENBackend
async with AsyncAuthZENBackend(
pdp_url="https://pdp.example.com/access/v1/evaluation",
pdp_application="my-agent-platform",
token="my-bearer-token",
) as backend:
result = await backend.evaluate({
"tool_name": "deploy_service",
"agent_id": "ops-agent",
})
ARIA-Enhanced Context — Obligations, Constraints, Advice
When connected to an EmpowerNow ARIA PDP, extract rich governance context that generic AuthZEN backends don't provide:
from authzen_backend import AuthZENBackend
from authzen_backend.aria import extract_aria_context
backend = AuthZENBackend(
pdp_url="https://aria-pdp.example.com/access/v1/evaluation",
pdp_application="my-platform",
token="...",
)
result = backend.evaluate({
"tool_name": "transfer_funds",
"agent_id": "finance-bot",
"user_id": "bob",
"amount": 50000,
})
aria = extract_aria_context(result)
print(aria.decision_id) # Links to a signed receipt in Receipt Vault
print(aria.obligations) # Actions the caller must perform
print(aria.constraints) # Parameter-level restrictions
print(aria.advice) # Structured denial guidance with remediation
print(aria.requires_approval) # True if human-in-the-loop approval needed
How It Works
sequenceDiagram
participant Agent as Your Agent
participant PE as PolicyEvaluator
participant AZB as AuthZENBackend
participant PDP as AuthZEN PDP
Agent->>PE: evaluate(context)
PE->>AZB: evaluate(context)
Note over AZB: Map flat dict to<br/>AuthZEN 1.0 request
AZB->>PDP: POST /access/v1/evaluation
PDP-->>AZB: { decision, context }
Note over AZB: Extract reason,<br/>obligations, constraints
AZB-->>PE: BackendDecision
PE-->>Agent: Final decision
Context Mapping
The toolkit passes a flat dict to backends. This package maps it to a structured AuthZEN request:
| Toolkit Context Key | AuthZEN Field | Notes |
|---|---|---|
agent_id |
subject.id |
Normalized to ARN: auth:agent:agentmesh:{id} |
user_id |
subject.properties.bound_to_user_id |
User the agent acts for |
delegator |
subject.properties.delegator |
Delegation chain |
tool_name |
action.name, resource.id |
Also generates op:tool:{name} |
action |
action.name |
Overrides tool_name if present |
resource_type |
resource.type |
Defaults to mcp_tool |
resource_id |
resource.id |
Defaults to tool_name |
| (all others) | context.* |
Forwarded as-is |
context.pdp_application and action.properties.operation_ref_id are set automatically.
Custom Mapping
Override field names if your agents use different context keys:
from authzen_backend import AuthZENBackend, FieldMapping
backend = AuthZENBackend(
pdp_url="https://pdp.example.com/access/v1/evaluation",
pdp_application="my-platform",
token="...",
mapping=FieldMapping(
agent_id_key="principal",
tool_name_key="operation",
user_id_key="end_user",
),
)
Fail-Closed vs Fail-Open
flowchart TD
A[PDP call fails] --> B{fail_closed?}
B -->|"True (default)"| C["Return deny<br/>error = None"]
B -->|False| D["Return deny<br/>error = description"]
C --> E[Toolkit stops — hard deny]
D --> F[Toolkit tries next backend]
fail_closed=True(default): PDP errors produce an authoritative deny. The toolkit treats this as final and does not consult other backends. Use this for production.fail_closed=False: PDP errors set theerrorfield on theBackendDecision. The toolkit skips this backend and tries the next one, or falls back to its default action. Use this for multi-backend setups where another backend can decide.
Configuration Reference
| Parameter | Type | Default | Description |
|---|---|---|---|
pdp_url |
str |
(required) | AuthZEN evaluation endpoint URL |
pdp_application |
str |
(required) | Application ID for policy scoping |
token |
str |
None |
Static Bearer token (mutually exclusive with client_credentials) |
client_credentials |
ClientCredentialsAuth |
None |
OAuth client credentials config |
timeout |
float |
5.0 |
HTTP timeout in seconds |
default_resource_type |
str |
"mcp_tool" |
Fallback resource type |
default_subject_type |
str |
"ai_agent" |
Fallback subject type |
mapping |
FieldMapping |
DEFAULT_MAPPING |
Context field name overrides |
fail_closed |
bool |
True |
Hard deny on errors (True) or signal error (False) |
http_client |
httpx.Client |
None |
Inject a pre-configured HTTP client |
Security
- HTTPS enforced —
pdp_urlmust use HTTPS in production. HTTP is allowed only forlocalhost/127.0.0.1/[::1]during development. - Error sanitization —
httpxexceptions are reduced to safe descriptions (HTTP 503,PDP request timed out) so URLs, tokens, and response bodies are never leaked in logs or error messages. - No credential logging —
client_secretandtokenfields userepr=Falseso they never appear in logs, tracebacks, or__repr__output. - Injection protection —
extra_paramscannot override OAuth core fields (grant_type,client_id,client_secret).
See SECURITY.md for reporting vulnerabilities.
Testing
70 tests | 100% coverage | mypy strict | ruff
pip install -e ".[dev]"
pytest tests/ -v
ruff check src/ tests/
mypy src/
Compatibility
| Component | Version |
|---|---|
| Python | 3.10+ |
| Dependencies | httpx >=0.27, pydantic v2 |
| Agent Governance Toolkit | Any version with ExternalPolicyBackend |
| PDP | Any OpenID AuthZEN 1.0 compliant PDP |
This package has zero dependency on agent-os-kernel — it implements the ExternalPolicyBackend protocol structurally (duck typing). Install alongside the toolkit or use standalone.
Looking for AGT-native integration? If you're using AGT's
ToolCallInterceptor,AuditBackend, orPolicyProviderInterfaceextension points, seearia-agentkit— which builds on this package and provides contract-tested adapters for the full AGT surface.
Links
- PyPI
- OpenID AuthZEN Specification
- Microsoft Agent Governance Toolkit
- EmpowerNow ARIA
aria-agentkit— AGT-native ARIA integration
License
MIT — see LICENSE.
Metadata
Release files for authzen-policy-backend 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| authzen_policy_backend-0.1.2.tar.gz | 21.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| authzen_policy_backend-0.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 37.6 kB
Release files / authzen_policy_backend-0.1.2.tar.gz
| Download URL | authzen_policy_backend-0.1.2.tar.gz |
|---|---|
| Size | 21.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
faadf04b90dd06814f93d76379e42b45b02b2c993c591ec6da15814056acf6a9
|
|
BLAKE2b-256 checksum How to use checksums |
33edbbff228dbc25f5b6254f389236d69ae1728d88704ad0756f90a18a1413a0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.10
|
Release files / authzen_policy_backend-0.1.2-py3-none-any.whl
| Download URL | authzen_policy_backend-0.1.2-py3-none-any.whl |
|---|---|
| Size | 16.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7650d20071d300486180b23643b256ada970cc9de6a07354a5011b51d1252f3f
|
|
BLAKE2b-256 checksum How to use checksums |
5a741f1e9de690d95d99f5e68198ae3d95d54460445dc0ee3e7d001600d1d8cd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.10
|