AuthZEN Deep Dive
Identiverse 2026 Masterclass
Specification internals · Implementor details · Certification · Agent authorization
Mastering the OpenID Authorization Standard
#identiverse
#identiverse | 1
What this masterclass is (and isn’t)
Assumed knowledge
We will cover
#identiverse | 2
Externalized AuthZ Speed Run
Section 0
#identiverse | 3
#identiverse | 3
Externalized Authorization Architecture
Policy Enforcement Point (PEP)
Policy Decision Point (PDP)
Policy Information Point (PIP)
Policy Decision Point (PIP)
Policy Enforcement Point (PEP)
App 1
App 2
This is the part that AuthZen standardizes
#identiverse | 4
Information Model
Section 1
#identiverse | 5
#identiverse | 5
Information model: the SARC primitives
Three required entities, each carrying type + id (+ optional properties).
{
"subject": { "type": "user", "id": "alice@example.com" },
"action": { "name": "can_read" },
"resource": { "type": "account", "id": "123" }
}
#identiverse | 6
Context: the fourth entity
{
"context": {
"time": "2025-06-27T18:03-07:00",
"ip": "192.168.1.1",
"tenant": "acme",
"session_risk": "medium"
}
}
Context carries environmental signals the PDP can’t infer:
OPTIONAL but where most real-world richness lives.
#identiverse | 7
The properties design decision
PEP pushes attributes
PDP resolves attributes
AuthZEN doesn’t pick a side. The properties field exists so you can do either.
{
"type": "user",
"id": "alice@acmecorp.com",
"properties": {
"role": ["Employee", "Manager"],
"department": "Finance"
}
}
#identiverse | 8
Access Evaluation
Section 2
#identiverse | 9
#identiverse | 9
Access Evaluation API
POST /access/v1/evaluation
Content-Type: application/json
Authorization: Bearer <token>
X-Request-ID: bfe9eb29-ab87-4ca3-be83-a1d5d8305716
Question: “Can this subject perform this action on this resource (in this context)?”
Response is a Decision: { "decision": true | false }.
Key invariants:
#identiverse | 10
Wire example - end to end
Request
POST /access/v1/evaluation HTTP/1.1
Host: pdp.example.com
Content-Type: application/json
Authorization: Bearer <token>
X-Request-ID: bfe9eb29-ab87-4ca3-be83-a1d5d8305716
{
"subject": { "type": "user", "id": "alice@example.com" },
"action": { "name": "can_read" },
"resource": { "type": "todo", "id": "1" },
"context": { "time": "2025-06-27T18:03-07:00" }
}
Response
HTTP/1.1 200 OK
Content-Type: application/json
X-Request-ID: bfe9eb29-ab87-4ca3-be83-a1d5d8305716
{ "decision": true }
#identiverse | 11
Batch - POST /access/v1/evaluations
Send N requests in one round-trip. Top-level subject/action/resource/context act as defaults.
{
"subject": { "type": "user", "id": "alice@example.com" },
"action": { "name": "can_read" },
"context": { "time": "2025-06-27T18:03-07:00" },
"evaluations": [
{ "resource": { "type": "document", "id": "doc-1" } },
{ "resource": { "type": "document", "id": "doc-2" } },
{ "action": { "name": "can_edit" },
"resource": { "type": "document", "id": "doc-3" } }
]
}
Default merge is entity-level, not field-level - a per-evaluation resource replaces the top-level resource whole.
#identiverse | 12
options.evaluations_semantic
Three modes the PEP can request:
Value
Behaviour
Use case
execute_all (default)
Run all, return all
UI render with N permission checks
deny_on_first_deny
Short-circuit on first deny &&
Pre-flight a chain of dependent ops
permit_on_first_permit
Short-circuit on first permit ||
Try multiple policy paths
{
"options": { "evaluations_semantic": "deny_on_first_deny" },
"evaluations": [ ... ]
}
Short-circuit responses are shorter than the request, array length reflects evaluation, not request, cardinality.
#identiverse | 13
Search APIs
Section 3
#identiverse | 14
#identiverse | 14
Three Search APIs: flip the question
Endpoint
Question
POST /access/v1/search/subject
Who can do X on Y?
POST /access/v1/search/resource
What Ys can this subject do X on?
POST /access/v1/search/action
What can this subject do on Y?
Rules:
• Subject Search: omit subject.id (type only)
• Resource Search: omit resource.id (type only)
• Action Search: omit action entirely
• Results SHOULD be transitive (group, role, ReBAC traversal)
• A Search result SHOULD evaluate to decision:true if reissued as an evaluation
#identiverse | 15
Search pagination: opaque-token
Initial request
{
"subject": { "type": "user", "id": "alice" },
"action": { "name": "can_read" },
"resource": { "type": "account" },
"page": { "limit": 2 }
}
Response
{
"page": {
"next_token": "a3M9NDU2O3N6PTI=",
"count": 2, "total": 3
},
"results": [
{ "type": "account", "id": "123" },
{ "type": "account", "id": "456" }
]
}
Follow-up: same query body, with page.token set to the previous next_token.
End-of-results: next_token is the empty string "".
#identiverse | 16
Search: three things implementers miss
1. Pagination is NOT a snapshot.
Items added/removed mid-pagination may be repeated or omitted. Do not build “exact diff” workflows on top of it.
2. Pagination is OPTIONAL.
A PDP MAY return all results in one response and ignore page entirely. PEPs should handle “no page object in response” gracefully.
3. Unknown entity type returns [], not 400.
Search for {type: "spaceship"} → empty results, HTTP 200. Pinned down by the certification profile.
#identiverse | 17
Decisions & Errors
Section 4
#identiverse | 18
#identiverse | 18
Beyond {"decision": true}: response context
Reasons (admin vs user-facing)
{
"decision": false,
"context": {
"reason_admin": {
"403": "Failed policy C076E82F"
},
"reason_user": {
"403": "Insufficient privileges. Contact your admin."
}
}
}
Step-up authentication
{
"decision": false,
"context": {
"acr_values": "urn:com:example:loa:3",
"amr_values": "mfa hwk"
}
}
Obligations / advice / environment metadata also ride here. Shape is implementation-specific - interoperability lives in conventions.
#identiverse | 19
Errors: two layers, don’t conflate them
Transport-level (HTTP status)
• 400 Malformed JSON, missing required field, wrong type, wrong Content-Type
• 401 PEP didn’t authenticate to the PDP
• 403 PEP authenticated but not authorized to call this PDP
• 500 PDP internal error
Payload-level (HTTP 200)
// Deny
{ "decision": false }
// Per-evaluation failure in batch
{
"decision": false,
"context": {
“error": {
"status": 404,
"message": "Resource not found"
}
}
}
A deny is NEVER a 4xx. A bad request is NEVER a decision:false.
#identiverse | 20
Discovery & Security
Section 5
#identiverse | 21
#identiverse | 21
Discovery
GET /.well-known/authzen-configuration HTTP/1.1
Host: pdp.example.com
{
"policy_decision_point": "https://pdp.example.com",
"access_evaluation_endpoint": "https://pdp.example.com/access/v1/evaluation",
"access_evaluations_endpoint": "https://pdp.example.com/access/v1/evaluations",
"search_subject_endpoint": "https://pdp.example.com/access/v1/search/subject",
"search_resource_endpoint": "https://pdp.example.com/access/v1/search/resource",
"search_action_endpoint": "https://pdp.example.com/access/v1/search/action",
"signed_metadata": "eyJhbGciOiJSUzI1NiIs..."
}
• Absence of an endpoint = PDP does not support it.
• signed_metadata is a JWS-signed JWT - trust beyond TLS when intermediaries are in the path.
#identiverse | 22
Security baseline
TLS between PEP and PDP.
Trust assumption: PDP trusts the PEP’s claims. The PEP is the enforcer.
#identiverse | 23
Interop & Certification
Section 6
#identiverse | 24
#identiverse | 24
Interop happened: across the stack
Two interop events. Real PDPs, gateways, and IdPs running the same payloads.
Same application. Same payloads. Swap the PDP, decisions stayed consistent.
TOPAZ
#identiverse | 25
Certification: making conformance verifiable
A conformance certification profile is now defined for AuthZEN PDPs.
Independent levels:
Each (except Discovery) splits Core vs Properties, wire-shape vs attribute evaluation.
Certification verifies protocol conformance, not the correctness of your policy.
Vendors can certify the levels their product needs.
#identiverse | 26
Agent Authorization
Section 7
#identiverse | 27
#identiverse | 27
Why OAuth scopes aren’t enough for MCP
OAuth handles authentication. It does not handle:
“Can this agent, acting for this user, invoke this tool with these arguments?”
That is a SARC question - and AuthZEN is the API for it.
#identiverse | 28
COAZ: Compatible with OpenID AuthZen
MCP Gateway
• Tool advertises authorization intent declaratively in inputSchema.
• PEP (gateway or server) is the enforcer, calling AuthZEN before tool execution.
• MCP client MAY pre-check, but the server is authoritative.
MCP Client
MCP Server
tools/list
tools[] with x-coaz-mapping
AuthZEN PDP
✓✗
#identiverse | 29
COAZ mapping: single-valued (get_customer)
{
"name": "get_customer",
"coaz": true,
"inputSchema": {
"type": "object",
"properties": {
"id": { "type": "string" },
"case": { "type": "string" }
},
"x-coaz-mapping": {
"resource": [{ "type": "'customer'", "id": "params.arguments.id" }],
"subject": [{ "type": "'user'", "id": "token.sub" }],
"context": [{ "agent": "token.client_id",
"case": "params.arguments.case" }]
}
}
}
• Values are CEL expressions over params.* (tools/call) and token.* (JWT claims).
• Static strings are CEL string literals, single quotes: 'customer'.
• action omitted → PEP defaults to {"name": "<tool name>"}.
• At least one field across subject/context MUST derive from the token.
#identiverse | 30
COAZ multi-valued mapping
x-coaz-mapping
"x-coaz-mapping": {
"action": [
{ "name": "'read'" },
{ "name": "'write'" }
],
"resource": [
{ "type": "'storage_object'",
"id": "params.arguments.source" },
{ "type": "'storage_object'",
"id": "params.arguments.destination" }
],
"subject": [
{ "type": "'user'", "id": "token.sub" }
],
"context": [
{ "agent": "token.client_id" }
]
}
Resulting AuthZEN call
{
"subject": { "type": "user",
"id": "alice@example.com" },
"context": {
"agent": "http://provider.com/app-id"
},
"evaluations": [
{ "action": { "name": "read" },
"resource": { "type": "storage_object",
"id": "/bucket/reports/q1.pdf" } },
{ "action": { "name": "write" },
"resource": { "type": "storage_object",
"id": "/bucket/archive/q1.pdf" } }
]
}
• Any multi-element array → batch API. Single-element fields lift to top-level defaults.
• All multi-element arrays MUST have the same length.
• PDP without /evaluations → PEP errors at discovery, not at call time.
#identiverse | 31
COAZ error model: JSON-RPC error codes
Code
Meaning
-32602
Mapping error - CEL failed, mismatched array lengths, etc.
-32401
Authorization denial - PDP returned decision:false
-32603
PDP unreachable / invalid response
• Deny in MCP is a protocol error, not a tool error. Tool MUST NOT execute.
• Message MAY surface PDP-provided context.reason.
• PEP SHOULD validate x-coaz-mapping against the tool’s inputSchema at discovery time.
• PEP MUST verify JWT signature / iss / aud / exp before extracting claims.
#identiverse | 32
Wrap
Section 8
#identiverse | 33
#identiverse | 33
What you can do with this
For implementers
For architects
#identiverse | 34
Q&A
Questions?
#identiverse | 35