| coaz-framework | February 2026 | |
| Olivier & Tulshibagwale | Standards Track | [Page] |
This specification defines COAZ (Compatible with OpenID AuthZEN, pronounced "cozy"), a framework for mapping the information model of an arbitrary protocol or interface into a request to the OpenID AuthZEN Authorization API. COAZ establishes a single, protocol-neutral model: the inputs of an incoming operation are projected, through a declarative mapping, into an AuthZEN Authorization API request — an Access Evaluation request for a single decision, or an Access Evaluations request for several — expressed using the Subject-Action-Resource-Context (SARC) model. Mapping values are either literal constants or expressions evaluated against the operation's inputs, enabling both fixed field-to-field mappings and dynamic, computed mappings. Expressions are written, by default, in the Common Expression Language (CEL); a binding MAY designate a different expression language. This specification does not define a mapping for any specific protocol; it defines the common model and the conformance contract that individual COAZ bindings fulfil — for example, COAZ-MCP for the Model Context Protocol, or bindings for HTTP APIs or OpenAPI-described routes.¶
A wide range of systems need to make fine-grained authorization decisions about operations expressed in some protocol or interface, for example:¶
The OpenID AuthZEN Authorization API [AUTHZEN] provides a standardized, interoperable way to obtain such decisions using the Subject-Action-Resource-Context (SARC) model. What it does not define is how to derive an AuthZEN request from the information model of any particular protocol.¶
COAZ (Compatible with OpenID AuthZEN) is the framework that fills this gap. It defines a single, protocol-neutral pattern:¶
an operation in any a declarative an AuthZEN information model mapping request +-------------------+ +-------------------+ +-------------------+ | operation inputs | | literals + | | Access | | (request fields, | -----> | expressions over| -----> | Evaluation(s) | | tokens, schema) | | the inputs | | (SARC) | +-------------------+ +-------------------+ +-------------------+
COAZ deliberately separates two concerns:¶
The output side is fixed. Every COAZ mapping produces a request to the AuthZEN Authorization API — an Access Evaluation request for a single decision or an Access Evaluations request for several, selected by the mapping's envelope (Section 2.3). The SARC structure, the semantics of defaults and overrides, and the decision response all belong to [AUTHZEN] and are identical for every protocol.¶
The input side is specialized. The set of available input variables, the location where a mapping is stored, and the way errors are reported back to the caller all depend on the protocol, and are defined by a COAZ binding. Expressions over those inputs are written, by default, in Common Expression Language [CEL] (Section 2.6).¶
This specification defines the output-side model and the conformance contract that every binding fulfils. It does not, by itself, define a mapping for any protocol. Companion bindings — such as COAZ-MCP [COAZMCP], the binding for the Model Context Protocol — apply this framework to a concrete information model.¶
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.¶
This specification uses the following terms:¶
Compatible with OpenID AuthZEN (pronounced "cozy"). The framework defined by this specification for mapping an information model into an AuthZEN Authorization API request.¶
A specification that binds this framework to a specific protocol or interface
by fulfilling the conformance requirements of Section 4. A binding is
designated COAZ-<PROTOCOL>: for example, COAZ-MCP for the Model Context
Protocol. Bindings for HTTP APIs and OpenAPI are anticipated.¶
A unit of work in the specific protocol or interface that requires an independent authorization decision from the AuthZEN PDP.¶
The set of data associated with an operation that a binding makes available to a mapping, such as request fields, message parameters, and security tokens.¶
A named element of the information model, exposed to expressions by a binding (for example, a variable holding a request's parameters or a variable holding decoded token claims).¶
A declarative description of how to construct an AuthZEN Authorization API request from an operation's input variables, consisting of an envelope that names the AuthZEN API to call and a template for that API's request body. Each value in a mapping is either a literal or an expression.¶
The single top-level member of a mapping. Its key names the AuthZEN API
(evaluation or evaluations) to which the constructed request is sent, and
its value is the template for that API's request body. See Section 2.3.¶
A value in a mapping that is used verbatim, as a constant, without reference to any input variable.¶
A value in a mapping that is computed by evaluating it, in the binding's expression language (Section 2.6), against the operation's input variables.¶
Policy Decision Point, as defined in [AUTHZEN]. A service that evaluates authorization requests and returns access decisions.¶
Policy Enforcement Point, as defined in [AUTHZEN]. The component that constructs the AuthZEN request from a mapping, calls the PDP, and enforces the decision.¶
A mapping defined by a binding that applies to an operation when no other mapping has been supplied for it. OPTIONAL; see Section 2.8.¶
A mapping supplied by an authority that describes an operation (for example, a server advertising a mapping for a specific operation). OPTIONAL; see Section 2.8.¶
A COAZ mapping is a template, corresponding to a distinct operation, for an AuthZEN Authorization API request, in which any value MAY be replaced by an expression over the operation's input variables. To authorize an operation, a PEP:¶
Populates the input variables defined by the applicable binding from the incoming operation (see Section 2.2).¶
Selects the mapping that applies to the operation (see Section 2.8).¶
Resolves every value in the mapping: literals are taken verbatim; expressions are evaluated against the input variables (see Section 2.5 and Section 2.6).¶
Constructs the AuthZEN request from the resolved values and sends it to the API named by the mapping's envelope (see Section 2.7).¶
Enforces the returned decisions.¶
A binding MUST define the information model it exposes to mappings as a set of named input variables. Expressions reference these variables by name. The framework does not mandate any particular variables; naming and contents are entirely binding-defined. For example, a binding for a request/response protocol might expose a variable for the request and a variable for the caller's authorization token.¶
A binding MUST specify, for each input variable, its name, the source from which the PEP populates it, and its structure, so that a mapping author knows exactly which fields an expression can reference and what they will contain.¶
A COAZ mapping is a JSON [RFC8259] object with exactly one top-level member, its envelope. The envelope's key names the AuthZEN Authorization API to which the constructed request is sent, and its value is a template shaped exactly as that API's request body. This framework defines two envelope keys:¶
evaluation:The value is a template for an Access Evaluation request as defined in
Section 6 of [AUTHZEN]: a single authorization decision, expressed with
subject, action, and resource fields and an OPTIONAL context field.¶
evaluations:The value is a template for an Access Evaluations request as defined in
Section 7 of [AUTHZEN]: one or more authorization decisions, expressed with OPTIONAL
top-level subject, action, resource, and context defaults and an
evaluations array whose entries MAY each contain their own subject,
action, resource, and context fields.¶
A mapping with no top-level member, more than one top-level member, or an envelope key not defined by this framework or by the applicable binding is malformed, and a PEP MUST treat it as a mapping error (Section 3). Future versions of this framework, and individual bindings, MAY define additional envelope keys corresponding to other AuthZEN APIs; a PEP MUST treat an envelope key it does not support as a mapping error rather than ignore it.¶
The framework does not redefine the request structures or their semantics. The
meaning of each field and — for the evaluations envelope — the rules by which
per-entry fields override top-level defaults are exactly as defined in
Sections 6 and 7 of [AUTHZEN]. In particular, custom attributes of a subject, resource, or
action MUST be nested under that object's properties key as defined by
[AUTHZEN], rather than added as sibling keys; context is a free-form object
and is the exception. COAZ's primary addition to these structures is that any
leaf value MAY be an expression rather than a literal (a binding MAY also
designate trust-anchored fields; see Section 2.9).¶
This section is non-normative. The examples use an illustrative binding that
exposes two input variables — request, holding the operation's parameters,
and token, holding the caller's validated token claims — and the framework's
defaults: CEL as the expression language and the leading-$ discriminator of
Section 2.5.¶
An operation requiring a single decision is mapped with the evaluation
envelope. Literals (here identity, read, and document) and expressions
appear side by side:¶
{
"evaluation": {
"subject": { "type": "identity", "id": "$token.sub" },
"action": { "name": "read" },
"resource": { "type": "document", "id": "$request.document_id" }
}
}
An expression can also embed literal text, using the expression language's own
operators rather than any additional mapping syntax. In CEL this is string
concatenation: the value "$'urn:doc:' + request.document_id" resolves to the
literal prefix urn:doc: followed by the value of request.document_id. The
leading $ marks the whole value as an expression; the embedded literal uses
ordinary CEL single-quoted string syntax.¶
An operation requiring several decisions — here, a move that must be authorized
as a read of the source and a write of the destination — is mapped with the
evaluations envelope. The two decisions are entries in the evaluations
array, and the shared subject sits at the top level of the request body,
applying to both per the default semantics of [AUTHZEN]:¶
{
"evaluations": {
"subject": { "type": "identity", "id": "$token.sub" },
"evaluations": [
{ "action": { "name": "read" },
"resource": { "type": "folder", "id": "$request.source" } },
{ "action": { "name": "write" },
"resource": { "type": "folder", "id": "$request.destination" } }
]
}
}
Every leaf value in a mapping is either a literal or an expression:¶
A literal is used verbatim as a constant in the constructed request.¶
An expression is evaluated, in the binding's expression language, against the operation's input variables, and its result is used in the constructed request.¶
The default discriminator, which a binding SHOULD use, is the $ prefix:¶
A string value whose first character is $ is an expression; the text
following the $ is the expression itself.¶
Any other value — a string not beginning with $, or any number, boolean,
null, list, or map — is a literal.¶
A string value beginning with $$ is a literal whose text begins with $:
the leading $$ denotes a single literal $. Doubling applies only to a
leading $; a $ anywhere else in a string has no special meaning and does
not mark an expression.¶
A binding MAY define a different discriminator where the default conflicts with its environment. Any discriminator MUST distinguish a literal from an expression unambiguously for every leaf value, and MUST include an escape mechanism such that any literal value — including one whose text resembles an expression — can be represented. The distinction MUST be lossless.¶
This framework does not use the terms "static" or "dynamic" to describe values. A field reference such as "the request's path" is an expression, not a literal, because it is computed from an input variable. Only constants are literals.¶
The default expression language for COAZ is Common Expression Language [CEL], and a binding SHOULD use it, so that mappings look and behave the same across protocols. A binding MAY designate a different expression language where CEL is a poor fit for its environment; a binding that does so MUST name the language it uses. Regardless of the language, every COAZ expression MUST satisfy the following contract, which is what allows the rest of this framework to remain language-neutral:¶
An expression is evaluated against the input variables defined by the binding, and only those variables.¶
An expression MUST evaluate to a single JSON value — a scalar (string, number, boolean, or null), a list, or a map — or to the distinguished value absent. A value is used as the value of the field in which the expression appears; absent causes that field to be omitted from the constructed request entirely.¶
A binding MUST specify how an expression yields absent, so that mapping
authors can distinguish "field deliberately omitted" from "field present with a
null value." When the expression language is CEL, the default is CEL optional
selection: the .? operator yields absent when the selected key is
missing, while plain field selection on a missing key is an evaluation error.¶
The framework relies on [AUTHZEN] for which fields are required: subject,
action, and resource are required for every evaluation, and context is
optional. If an expression yields absent or null for a required field, the
PEP cannot construct a valid request; this is a mapping error (see Section 3).¶
A value that an expression returns is always a single field value, including
when it is a list or a map. Returning a list SHALL NOT cause the request to
fan out into multiple evaluations. The number of decisions requested MUST be
determined solely by the mapping's envelope — one for evaluation, and the
number of entries in the evaluations array for evaluations (see
Section 2.7) — not by the type of a value an expression returns.¶
A PEP MUST construct, from the resolved mapping, the AuthZEN request named by
the mapping's envelope, as defined in [AUTHZEN], and send it to the
corresponding API: the Access Evaluation API for an evaluation envelope, or
the Access Evaluations API for an evaluations envelope.¶
A PDP used with COAZ MUST support the Access Evaluation API. Support for the
Access Evaluations API is additionally required wherever mappings use the
evaluations envelope. A PEP whose PDP does not support the Access Evaluations
API MAY instead satisfy an evaluations mapping by issuing one Access
Evaluation request per entry of the constructed request — applying the
top-level defaults to each entry exactly as [AUTHZEN] defines for the Access
Evaluations request — and permitting the operation only if every decision is a
permit.¶
For an evaluations envelope, the top-level and per-entry fields of the
constructed request carry exactly the default and override semantics defined by
[AUTHZEN]. COAZ does not define any additional merge behavior.¶
A binding MUST state which envelope keys it permits (Section 4).¶
A binding MAY support either or both of the following sources of mappings. This capability is OPTIONAL; a binding that supports neither requires every operation to carry its own mapping by some binding-defined means.¶
Default mappings. A binding MAY define one or more mappings that apply to operations when no declared mapping has been supplied. A binding that defines default mappings MUST enumerate the operations in scope and MUST explicitly identify any operations that are not authorized by COAZ (pass-through operations), so that the absence of a mapping is never interpreted as an implicit deny.¶
Declared mappings. A binding MAY allow an authority that describes an operation to supply a mapping for it. A binding that supports declared mappings MUST define who may author them, where they are carried, and how a declared mapping relates to any default mapping for the same operation (for example, whether it overrides the default).¶
As a non-normative illustration, using the same illustrative binding as Section 2.4: a binding for an HTTP API might define a default mapping that authorizes any request from its method and path,¶
{
"evaluation": {
"subject": { "type": "identity", "id": "$token.sub" },
"action": { "name": "$request.method" },
"resource": { "type": "http_route", "id": "$request.path" }
}
}
while allowing the authority that describes a specific route (for example, its OpenAPI definition) to declare a mapping that overrides the default for that route with domain-specific semantics:¶
{
"evaluation": {
"subject": { "type": "identity", "id": "$token.sub" },
"action": { "name": "approve" },
"resource": { "type": "expense_report", "id": "$request.path_params.report_id" }
}
}
Inputs to a COAZ mapping are considered to be untrusted, because they may be provided by the same entity whose operation is being authorized. Allowing such a mapping to set the fields that establish identity would let the authorized party assert who the subject is, inverting the trust model.¶
To prevent this, and to prevent mapping developers from accidentally trusting inputs to the mapping, a binding MAY designate certain request fields as trust-anchored: their value MUST be derived from the PEP's trusted inputs (such as an independently verifiable authorization token), not asserted by a mapping. A trust-anchored field MAY be as specific as a single identifying attribute — for example, the subject's identifier — leaving the remaining attributes of the same object available to a declared mapping. A binding that admits declared mappings from a party other than the PEP MUST specify which fields are trust-anchored.¶
A binding MUST require the PEP to enforce each trust-anchored field by one of:¶
Verification. The mapping sets the field, and the PEP verifies that its resolved value matches the corresponding trusted input, treating a mismatch as a mapping error. This keeps the field's provenance visible in the mapping.¶
Substitution. The PEP sets the field from its trusted inputs, ignoring any value a declared mapping supplied.¶
Sibling fields that are not trust-anchored remain available to the mapping. An attribute does not become trustworthy merely by appearing in the constructed request: a binding and its deployments MUST treat any attribute that originates from a declared mapping — including non-trust-anchored attributes of an object whose identifier is trust-anchored — as untrusted input, and policy MUST NOT rely on such an attribute as authoritative for identity or privilege.¶
A binding MAY define a mechanism by which a caller can obtain, in advance of invoking an operation, the mapping that will be applied to it. This enables a caller to understand how authorization will be performed and to shape its request accordingly. Discoverability is OPTIONAL and is not always achievable: some protocols provide no channel through which a mapping can be advertised. Bindings MUST NOT assume discoverability unless they define a mechanism for it.¶
COAZ defines three categories of outcome in which a PEP refuses an operation. An authorization denial is not an error: it is the normal course of policy enforcement. It is categorized alongside the two error categories only because it shares their consequence — the operation does not proceed. The framework defines the categories and their meaning; the transport used to report each category to the caller is binding-defined, because it depends on the protocol.¶
The PEP cannot construct a valid AuthZEN request from the mapping and the operation's inputs — for example, an expression references a non-existent field, an expression fails to evaluate, the mapping is malformed, or a required field is missing. The PEP MUST NOT permit the operation.¶
The PDP returns a deny decision for one or more requested decisions. This is the normal operation of policy enforcement, not a fault. The PEP MUST NOT permit the operation.¶
The PEP cannot reach the PDP or receives an invalid or unparseable response. The PEP MUST NOT permit the operation.¶
In all three categories the PEP MUST fail closed: the operation MUST NOT proceed unless an explicit permit decision is obtained for every decision requested by the constructed request.¶
A specification conforms to this framework as a COAZ binding if, and only if, it specifies all of the following:¶
Information model. The named input variables it exposes to expressions, and, for each, its source and structure (Section 2.2).¶
Mapping location. Where a mapping is stored or carried for an operation, and how the PEP obtains it.¶
Literal/expression discriminator. Whether the binding uses the default
$ discriminator and $$ escape of Section 2.5, or the
alternative unambiguous syntax and escape mechanism it defines.¶
Expression language. The expression language used — CEL [CEL] unless the binding designates otherwise — satisfying the expression contract of Section 2.6.¶
Envelopes. The envelope keys it permits in mappings, drawn from those defined in Section 2.3 or by the binding itself (Section 2.7).¶
Operations in scope. The set of operations the binding authorizes and, where a default mapping is provided, the explicit set of pass-through operations (Section 2.8).¶
Default mapping behavior. The default mappings it defines, if any, or a statement that it defines none (Section 2.8).¶
Declared mapping behavior. Whether declared mappings are permitted, who authors them, where they are carried, and how they relate to default mappings (Section 2.8).¶
Trust-anchored fields. Where declared mappings are permitted from a party other than the PEP, the fields that are trust-anchored, and whether each is enforced by verification or substitution (Section 2.9).¶
Error transport. The mechanism by which each category of Section 3 is reported to the caller.¶
Discoverability. The discoverability mechanism it defines, if any (Section 2.10). OPTIONAL.¶
A binding MUST NOT redefine the AuthZEN request structures, the semantics of defaults and overrides, or the envelope rule of Section 2.7, all of which are fixed by this framework and [AUTHZEN].¶
As required by Section 3, a PEP MUST fail closed. A mapping error, a denial, or a PDP communication failure all result in the operation being refused. Bindings and deployments MUST NOT define fallbacks that permit an operation in the absence of an explicit permit decision.¶
A mapping determines how an operation is authorized; tampering with it can weaken or bypass authorization. A PEP SHOULD validate that a mapping is well-formed and that its expressions reference only defined input variables before relying on it. Where mappings are supplied by a party other than the PEP (for example, declared mappings), the trust placed in that party MUST be considered in the threat model.¶
All communication between the PEP and the PDP MUST use TLS, as specified in the transport requirements of [AUTHZEN].¶
If no PEP in an operation's path evaluates the applicable mapping, no AuthZEN authorization occurs and access control falls back to whatever other mechanisms are in place. Deployments SHOULD be validated to confirm that COAZ mappings are consumed by an appropriate enforcement point for every in-scope operation.¶
This specification has no IANA actions.¶
This specification is the common foundation for a family of bindings, each of which binds the framework to a specific protocol or interface by fulfilling the conformance requirements of Section 4. COAZ-MCP [COAZMCP] is the reference binding. Bindings for HTTP APIs [RFC9110] and for OpenAPI-described routes [OPENAPI] are anticipated.¶
A mapping names the AuthZEN API it targets through a single outer key rather than assuming one API for all mappings. The Access Evaluation API is the baseline capability of every AuthZEN PDP, so the common case — one operation, one decision — asks no more of the PDP than [AUTHZEN] itself requires. The Access Evaluations API is engaged only by mappings that genuinely need several decisions for a single operation. The envelope also leaves room for growth: future versions of this framework, or individual bindings, can bind additional keys to other AuthZEN APIs without changing the shape of existing mappings, and a PEP treats an envelope key it does not support as a mapping error, so unknown request types fail closed.¶
The central design choice of COAZ is to fix the output and vary the input. The AuthZEN request is the same regardless of the source protocol, so PDPs, policy authors, and tooling work identically across protocols. Only the projection from each protocol's information model differs, and that projection is exactly what a binding specifies.¶
COAZ describes values as either literals or expressions. It avoids the "static/dynamic" framing because a field reference — which appears to be a simple mapping — is in fact computed from an input variable and is therefore an expression. Reserving "literal" for true constants keeps the model precise and avoids the contradiction of a "static" mapping that is full of computed values.¶
The framework names CEL as its default expression language so that mappings
look the same across bindings and a mapping author carries one skill between
them. Alongside the default it fixes an expression contract, so that a
binding whose environment genuinely cannot host a CEL evaluator can substitute
another language without destabilizing the rest of the model: the contract in
Section 2.6 defines exactly what any replacement must guarantee.
The same reasoning applies to the default $ discriminator and $$ escape of
Section 2.5: one default syntax across bindings, overridable only
where a protocol's environment forces it.¶
The authors would like to thank the members of the OpenID AuthZEN Working Group for their ongoing work on the Authorization API standard, and Martin Besozzi for the original proposal on AuthZEN integration for fine-grained authorization that motivated this line of work.¶
Copyright (c) 2025 The OpenID Foundation.¶
The OpenID Foundation (OIDF) grants to any Contributor, developer, implementer, or other interested party a non-exclusive, royalty free, worldwide copyright license to reproduce, prepare derivative works from, distribute, perform and display, this Implementers Draft, Final Specification, or Final Specification Incorporating Errata Corrections solely for the purposes of (i) developing specifications, and (ii) implementing Implementers Drafts, Final Specifications, and Final Specification Incorporating Errata Corrections based on such documents, provided that attribution be made to the OIDF as the source of the material, but that such attribution does not indicate an endorsement by the OIDF.¶
The technology described in this specification was made available from contributions from various sources, including members of the OpenID Foundation and others. Although the OpenID Foundation has taken steps to help ensure that the technology is available for distribution, it takes no position regarding the validity or scope of any intellectual property or other rights that might be claimed to pertain to the implementation or use of the technology described in this specification or the extent to which any license under such rights might or might not be available; neither does it represent that it has made any independent effort to identify any such rights. The OpenID Foundation and the contributors to this specification make no (and hereby expressly disclaim any) warranties (express, implied, or otherwise), including implied warranties of merchantability, non-infringement, fitness for a particular purpose, or title, related to this specification, and the entire risk as to implementing this specification is assumed by the implementer. The OpenID Intellectual Property Rights policy (found at openid.net) requires contributors to offer a patent promise not to assert certain patent claims against other contributors and against implementers. OpenID invites any interested party to bring to its attention any copyrights, patents, patent applications, or other proprietary rights that may cover technology that may be required to practice this specification.¶