1 of 35

AuthZEN Deep Dive

Identiverse 2026 Masterclass

Specification internals · Implementor details · Certification · Agent authorization

Mastering the OpenID Authorization Standard

#identiverse

#identiverse | 1

2 of 35

What this masterclass is (and isn’t)

Assumed knowledge

  • What externalized authorization is
  • PDP / PEP / PAP / PIP
  • Why standards matter at runtime

We will cover

  • API surface in implementor detail
  • Evaluations semantics, pagination, properties, discovery
  • The certification profile - what it tests
  • The COAZ profile for MCP tool authorization

#identiverse | 2

3 of 35

Externalized AuthZ Speed Run

Section 0

#identiverse | 3

#identiverse | 3

4 of 35

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

5 of 35

Information Model

Section 1

#identiverse | 5

#identiverse | 5

6 of 35

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" }

}

  • type + id are REQUIRED on Subject and Resource
  • name is REQUIRED on Action
  • properties is OPTIONAL on all three - object of arbitrary attrs

#identiverse | 6

7 of 35

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:

  • Time, location, network, device posture
  • Step-up state, session risk, delegation hints
  • JSON Schema / JSON-LD references for request validation

OPTIONAL but where most real-world richness lives.

#identiverse | 7

8 of 35

The properties design decision

PEP pushes attributes

  • PDP is stateless
  • PEP already has the data (token claims, request context)
  • Lowest latency, fewest surprises

PDP resolves attributes

  • Single source of truth (HR system, group store, graph)
  • Avoids stale data in tokens
  • PIP integration becomes a PDP concern

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

9 of 35

Access Evaluation

Section 2

#identiverse | 9

#identiverse | 9

10 of 35

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:

  • 200 + {"decision": false} is the deny path - NOT an HTTP error
  • 4xx / 5xx mean the request itself was malformed
  • X-Request-ID MUST be echoed if provided

#identiverse | 10

11 of 35

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

12 of 35

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

13 of 35

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

14 of 35

Search APIs

Section 3

#identiverse | 14

#identiverse | 14

15 of 35

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

16 of 35

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

17 of 35

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

18 of 35

Decisions & Errors

Section 4

#identiverse | 18

#identiverse | 18

19 of 35

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

20 of 35

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

21 of 35

Discovery & Security

Section 5

#identiverse | 21

#identiverse | 21

22 of 35

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

23 of 35

Security baseline

TLS between PEP and PDP.

  • PDP SHOULD authenticate the PEP: mTLS, OAuth2, or API key.
  • PDP MAY sign responses (JWS), important when proxies sit between PEP and PDP.
  • Signed metadata adds trust beyond Internet PKI.
  • I-JSON profile recommended: UTF-8, no duplicate keys, IEEE 754 safe numbers.
  • Rate-limit + payload-size guard the PDP, it is a high-fan-in component.

Trust assumption: PDP trusts the PEP’s claims. The PEP is the enforcer.

#identiverse | 23

24 of 35

Interop & Certification

Section 6

#identiverse | 24

#identiverse | 24

25 of 35

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

26 of 35

Certification: making conformance verifiable

A conformance certification profile is now defined for AuthZEN PDPs.

Independent levels:

  • Basic - Access Evaluation
  • Batch - Access Evaluations
  • Search - Subject / Resource / Action
  • Discovery - PDP metadata

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

27 of 35

Agent Authorization

Section 7

#identiverse | 27

#identiverse | 27

28 of 35

Why OAuth scopes aren’t enough for MCP

OAuth handles authentication. It does not handle:

    • Who is the human behind the agent?
      • Token issued to the agent. No standard slot for the user-on-whose-behalf.
    • What specifically does this call touch?
      • Scopes are coarse. Per-call resource depends on user identity + the args of this invocation.

“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

29 of 35

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

30 of 35

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

31 of 35

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

32 of 35

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

33 of 35

Wrap

Section 8

#identiverse | 33

#identiverse | 33

34 of 35

What you can do with this

For implementers

  • Wire it: Basic + Discovery is days, not quarters
  • Certify it: matrix is independent; pick the level your product needs
  • Profile it: discover capabilities, don’t assume them

For architects

  • One API across PDPs, gateways, IdPs, and MCP servers
  • Authorization becomes a runtime contract, not a vendor contract

#identiverse | 34

35 of 35

Q&A

Questions?

#identiverse | 35