OpenID AuthZEN Authorization API · draft-gazitt-oauth-authzen-issuance · draft-gazitt-oauth-authzen-token-exchange
Every authorization system makes a binary decision: allow or deny. OAuth 2.0, OpenID Connect, and RFC 8693 Token Exchange all say the decision is "governed by local policy" — without defining what that policy looks like or how it's evaluated. AuthZEN fills exactly that gap: a standard API for externalising the authorization decision to any Policy Decision Point.
Every AuthZEN evaluation request carries five fields. The PDP evaluates these and returns a single boolean decision.
type + idtype + idnameproperties{ "decision": bool }AuthZEN formalises a pattern that Envoy and Kubernetes already use — AuthZEN adds a standard API so any PDP can be plugged in.
/access/v1/evaluation instead of a custom proto.
kube-apiserver before they're persisted.
The webhook is a PDP — it receives the AdmissionRequest (subject, resource, operation) and returns allow/deny in an AdmissionResponse.
AuthZEN is the same pattern at the OAuth layer — the token issuance request is the admission request, the AuthZEN PDP is the webhook.
Construct a POST /access/v1/evaluation request and evaluate it against the in-memory PDP. All cryptographic operations run in Go/WASM — no server involved.
The same authorization policy yields different decisions depending on the strength of the subject's authentication. A passkey (WebAuthn L4) unlocks actions that an anonymous caller or API key cannot perform — without changing the policy, just the subject type.
webauthn subject → classified-q3-report can_read → PERMIT ✓
The webauthn subject type that unlocked the PERMIT decision above comes from a specific registration ceremony. AuthZEN does not define how that credential is created — it only consumes the subject type. Here is what happens on the authentication side to produce a credential the PDP can trust at tier 4.
challenge (32 bytes)publicKey optionscredentials.create()PublicKeyCredentialnavigator.credentials.create()const credential = await navigator.credentials.create({ publicKey: { rp: { name: "authzen-poc.example", id: "authzen-poc.example" }, user: { id: new Uint8Array(16), // opaque user handle name: "alice@example.com", displayName: "Alice" }, challenge: crypto.getRandomValues(new Uint8Array(32)), // from RP — binds ceremony pubKeyCredParams: [{ type: "public-key", alg: -7 }], // -7 = ES256 (COSE) authenticatorSelection: { residentKey: "required", // device-bound credential → tier 4 userVerification: "required" // biometric or PIN mandatory }, attestation: "none" // no device attestation needed here } }); // What you get back: credential.id // base64url credential ID — store this credential.response.attestationObject // CBOR: authData + credentialPublicKey credential.response.clientDataJSON // challenge echo + origin (JSON)
rpIdHash — SHA-256 of the origin domainflags — UP (user present) + UV (verified)credentialId — random opaque identifiercredentialPublicKey — CBOR-COSE EC P-256sig — authenticator self-attestation
credentialId — links future assertions to this keypublicKey — EC P-256 — verifies assertion signaturescrypto/ecdsa generates the P-256 key pair and crypto/rand produces the credential ID, giving cryptographically valid output. The private key lives in WASM memory rather than a hardware secure enclave — the ceremony structure and the math are identical.
type: "webauthn" without doing the ceremony?{"subject":{"type":"webauthn","id":"fake"}} directly — the authorization layer does not verify that the ceremony was actually completed.amr: ["hwk"] (hardware key) and acr: "L4".type: "webauthn" claim without a valid token signature is rejected.amr:["hwk"]) → AuthZEN PDP checks JWT sig → trusts tier 4
amr claim is the attestation. The JWT signature is the tamper-evident seal.
Edit the in-memory PDP rules. Changes immediately affect evaluations in all tabs. Rules are evaluated by priority (higher = first); ties broken by position (later wins). No match → default deny.
| # | Label | Subject type | Subject ID (glob) | Resource type | Resource ID (glob) | Action (glob) | Decision | Priority |
|---|
Export the current rules to OpenFGA DSL or OPA Rego. Open the respective playground tab to run them interactively.
POST /access/v1/evaluationsAuthZEN §5.2: a single request carries a shared Subject plus an array of (Resource, Action) pairs. The PDP evaluates each independently and returns an array of decisions. Useful for permission checks across multiple resources in one round-trip.
Implements the mapping defined in draft-gazitt-oauth-authzen-token-exchange-00
When an Authorization Server receives an RFC 8693 Token Exchange request, it must decide whether to issue the requested token. This simulation shows how the AS constructs an AuthZEN evaluation request from the exchange parameters and calls the PDP to get its decision — replacing the "local policy" black box with a standard API call.
subject_token.sub → subject.id ·
audience → resource.id ·
"exchange" → action.name ·
subject type = "workload" ·
resource type = "token-audience"
openfga.dev/docs/interacting/authzen · play.fga.dev · OpenFGA DSL schema 1.1
OpenFGA exposes a built-in AuthZEN-compatible endpoint at POST /stores/{id}/access/v1/evaluation.
The same five-tuple request you send to this PoC's in-memory PDP can be sent directly to OpenFGA — the difference is the backend evaluates using a relationship graph (Google Zanzibar model) rather than glob rules.
subject.type:subject.id → user ·
action.name → relation ·
resource.type:resource.id → object
The current InMemoryPDP rules are exported as an OpenFGA DSL model and YAML tuple set. Copy both into play.fga.dev to run the same evaluations in OpenFGA.
play.openpolicyagent.org · Rego v1 language reference
OPA evaluates AuthZEN five-tuple requests as Rego policy input. The generated policy accepts the same
{ subject, resource, action } structure and returns decision := true/false.
Use the OPA playground to test the generated policy interactively.
Lambda Authorizers are a natural PEP — AuthZEN standardises exactly what happens inside that function.
{decision:bool} to IAM policy doc.AuthZEN defines the API contract, not the deployment topology. Choose your resilience pattern.
Adding a synchronous AuthZEN call to the critical path has cost. Here are the mitigation patterns.
hash(subject_id + resource_type + resource_id + action_name)
Last checked: 2026-08-07 ·
standards-baseline.json ·
standards-tracker.yml
| Standard | WG / Body | Status | PoC impl. | Rev | Files | Impact |
|---|---|---|---|---|---|---|
| AuthZEN Authorization API | OpenID Foundation | ● WG Draft | ✓ Implemented | 2.0-ID2 |
pkg/authzen/types.go, handler.go, pdp.go |
Core spec — five-tuple types, /access/v1/evaluation, /access/v1/evaluations endpoints, decision response shape |
| AuthZEN Profile for OAuth 2.0 Token Issuance | IETF OAuth WG | ● Individual draft | ◌ Monitoring | -00 |
pkg/authzen/pdp.go |
Defines the canonical AuthZEN five-tuple for OAuth token issuance decisions. Our InMemoryPDP implements the decision evaluation model this draft formalises. |
| AuthZEN Binding for OAuth 2.0 Token Exchange | IETF OAuth WG | ● Individual draft | ✓ Implemented (AS Bridge) | -00 |
cmd/demo-wasm/main.go |
The AS Bridge tab implements the exact five-tuple mapping: sub→subject.id, audience→resource.id, "exchange"→action.name |
| RFC 8693 — OAuth 2.0 Token Exchange | IETF OAuth WG | ● Published RFC | ✓ Implemented (AS Bridge) | RFC |
cmd/demo-wasm/main.go |
Foundational. AS Bridge simulates the exact RFC 8693 request format (grant_type, subject_token, audience). AuthZEN fills the "governed by local policy" gap. |
| OpenFGA DSL — Authorization Model | OpenFGA / CNCF | ● Stable | ✓ Export implemented | 1.1 |
pkg/authzen/export.go |
ToOpenFGA() generates schema 1.1 DSL model + YAML tuples from InMemoryPDP rules. OpenFGA tab links to play.fga.dev. |
| OPA Rego — Policy Language | CNCF OPA | ● Stable (v1) | ✓ Export implemented | v1 |
pkg/authzen/export.go |
ToRego() generates Rego v1 policy from InMemoryPDP rules. OPA tab generates policy + tries to open play.openpolicyagent.org with it pre-loaded. |
authzen-issuance and authzen-token-exchange revisions. Opens a labelled issue when any revision changes.standards-baseline.json + check_standards.py pattern as wimse-agent-fabric and wimse-identity-fabric. Consistent issue format across all three repos.