# Who Authorized This Tool Call? OpenID AuthZEN, the MCP Profile, and the Standards Race to Govern AI Agents

> A valid OAuth token proves an AI agent is a legitimate caller but never authorizes the tool call. How OpenID AuthZEN and its MCP profile answer at runtime.

*Published: 2026-06-30*
*Canonical: https://paragmali.com/blog/who-authorized-this-tool-call-openid-authzen-the-mcp-profile*
*© Parag Mali. All rights reserved.*

---
<TLDR>
A valid OAuth 2.1 access token -- right scope, right audience -- proves an AI agent is a legitimate caller, but it never decides whether *this* agent, acting for *this* user, may take *this* action on *this* resource. That is exactly the question an autonomous tool call raises the instant it fires. Twenty years of Policy-Decision-Point / Policy-Enforcement-Point architecture (XACML, NIST ABAC) and a generation of fine-grained policy engines (OPA, Zanzibar, Cedar, Topaz, Cerbos) could answer it but never interoperably -- every engine spoke its own wire API.

The OpenID AuthZEN Authorization API 1.0 (a Final Specification as of January 12, 2026) fixes this the way OIDC fixed authentication: it standardizes only the wire protocol between decider and enforcer -- the Subject-Action-Resource-Context request and the Decision response -- and leaves the policy engine swappable.

Its MCP Tool Authorization Profile (COAZ, a Draft dated February 13, 2026) maps a `tools/call` into that decision before the tool runs, with the human user as the Subject and the AI agent as the Context. It is a Final API under a Draft profile so new that almost no production server ships it yet -- but the answer to "who authorized this tool call?" is now a real, runtime, interoperable one, and the "without authorization" world is what its absence already costs.
</TLDR>

## 1. The token was valid. The tool ran.

An MCP agent holds a perfectly ordinary OAuth 2.1 access token: correct issuer, unexpired, carrying the `crm:write` scope, and -- because the server did its RFC 8707 homework [@rfc8707] -- an audience bound to exactly this MCP server and no other. The agent calls `delete_customer(id="9931")`; the token check passes, the tool runs, the customer record is gone. And no component anywhere in that path ever decided whether this user's agent was allowed to delete *this particular* customer. The token answered "is this a valid caller?" It never answered "is this action, on this resource, for this user, allowed?"

The failure is invisible precisely because nothing is broken. The call is valid, in-scope, and audience-bound, and the tool did exactly what it was asked. That is not a missing check; it is a category error.

Authentication and coarse scopes answer *"is this token valid, and does the agent hold the right scope?"* They structurally cannot answer *"is THIS agent, acting for THIS user, allowed to perform THIS action on THIS resource in THIS context?"* Four distinct layers hide inside "authorization" here: OAuth 2.1 token validity, RFC 8707 audience binding, the OAuth scope, and a per-call decision on the specific arguments. The first three are real and necessary. None of them is the fourth.<Sidenote>Set the clock from the first screen: the OpenID AuthZEN Authorization API 1.0 is a Final Specification approved January 12, 2026 [@authzen-final-announce], but the MCP Tool Authorization Profile that maps tool calls to decisions is only Draft 1, dated February 13, 2026 [@authzen-mcp-profile]. Final API, draft profile -- that gap is the whole standards race.</Sidenote>

If that sounds like a hypothetical, consider the case where the answer to "who authorized this tool call?" was, literally, "no one." On June 18, 2026, Microsoft disclosed **AutoJack**: AutoGen Studio's Model Context Protocol control plane, reachable over a WebSocket, that skipped authentication entirely on its `/api/mcp/*` surface [@autojack]. This is a missing-authentication control-plane weakness (CWE-306), chained through a permissive origin check (CWE-1385) and verbatim command execution of a query parameter (CWE-78) into remote code execution on the host running the agent.

Microsoft hardened it in commit `b047730` [@autojack-commit]; it never shipped in a PyPI release and carries no CVE -- a research demonstration, not mass exploitation. It is not, and this matters for later, a skill-scanner bypass. It is the limit case of a tool call with no enforcement point at all.

<PullQuote>
"The MCP WebSocket accepted connections without any authentication regardless of the auth mode configured for the rest of the app." -- Microsoft Security, on AutoJack [@autojack]
</PullQuote>

Between the two scenarios sits this article's subject. In the first, every OAuth check passed and still no layer expressed the per-resource decision. In the second, there was no check to pass. Both point at the same missing thing: a component whose only job is to decide, freshly, at the moment of use, whether this call is allowed. That idea is not new. It is fifty years old. Who first insisted that every access be checked, and who first named the exact hazard that an autonomous program holding someone else's token represents?

## 2. Complete mediation, the confused deputy, and the birth of the decision/enforcement split

The governing principle arrived in 1975. Jerome Saltzer and Michael Schroeder, cataloguing the design principles of secure systems, stated the one that matters most here: **complete mediation** -- "every access to every object must be checked for authority" [@saltzer]. Not once at login, not cached and reused, but freshly, at the point of use, every time. Read forward half a century, a per-call authorization decision on a `tools/call` is complete mediation applied to an autonomous agent. The whole history that follows is a series of attempts to honor that principle for programs that insist on holding ambient authority.

<Definition term="Complete mediation">
A design principle from Saltzer and Schroeder (1975): every access to every object must be checked for authority, as a fresh decision at the point of use -- not a cached grant and not an ambient token carried from an earlier moment [@saltzer].
</Definition>

The vocabulary came a few years earlier. Butler Lampson's 1971 access matrix framed protection as subjects against objects against permitted operations [@lampson] -- the same subject/object/operation substrate that Attribute-Based Access Control and, later, AuthZEN's Subject-Action-Resource model still inherit. Saltzer and Schroeder added the governing principles: complete mediation and least privilege. But real systems honored complete mediation mostly in the breach. They cached decisions and handed programs *ambient* authority -- authority a program carries by virtue of who it is, independent of the request it is currently serving.

Norm Hardy named the failure that follows. In *The Confused Deputy* (1988), he described a program that "runs with authority stemming from two sources" and gets tricked into wielding the more powerful one on an attacker's behalf [@hardy-caplore] [@hardy-acm]. The concrete incident: a compiler holding a "home files license" so it could update its own statistics file was induced to overwrite the system billing file when a user named that file as the compiler's debug-output destination.<Sidenote>Hardy dates the incident to "about eleven years ago" relative to his 1988 paper -- so roughly 1977, at Tymshare, on an operating system he says was "much like Unix" [@hardy-caplore]. Two common secondary errors are worth avoiding: it was not the "early 1970s," and it was not a Burroughs B5700.</Sidenote>

Read 2026-forward, the confused deputy *is* the AI agent: an autonomous program holding the user's token, one prompt-injected instruction away from a tool call the user never intended.

<Definition term="Confused deputy">
Hardy's 1988 name for a program that holds authority from two sources and can be tricked into misusing the more powerful one, because the system cannot tell which authority it is acting under. It is the structural ancestor of an AI agent that holds a user's access token [@hardy-caplore].
</Definition>

Containing a confused deputy requires separating the act of *deciding* whether an access is allowed from the act of *enforcing* that decision. The terminology was consolidated by the Informational RFC 3198 in 2001 -- Policy Decision Point and Policy Enforcement Point [@rfc3198], which draws both definitions from the earlier RFC 2753 (2000) [@rfc2753] -- and operationalized by the eXtensible Access Control Markup Language and NIST's ABAC guidance, which add a Policy Information Point for attributes and a Policy Administration Point for authoring [@xacml30] [@nist-800-162-landing].<Sidenote>RFC 3198, "Terminology for Policy-Based Management," is an *Informational* glossary by Westerinen, Schnizlein, Strassner and others, published November 2001 -- it consolidates the terms rather than coining them, drawing both definitions from the earlier RFC 2753 (2000), and it predates XACML, which cites it directly for the PDP definition [@rfc3198] [@rfc2753] [@xacml30].</Sidenote>

<Definition term="Policy Decision Point / Policy Enforcement Point (PDP/PEP)">
The externalized-authorization split: the PEP intercepts an access and asks; the PDP decides against policy and returns a permit or deny. The terminology was consolidated by RFC 3198 (2001), drawing on the earlier RFC 2753 (2000), and operationalized by XACML and NIST ABAC, which add a Policy Information Point (attributes) and Policy Administration Point (authoring) [@rfc2753] [@rfc3198] [@xacml30].
</Definition>

<Mermaid caption="The decision/enforcement split. A PEP intercepts each access and asks a PDP, which decides using attributes from a PIP and policy authored at a PAP, then returns permit or deny for the PEP to enforce.">
flowchart LR
    A[Agent request] --> PEP[PEP intercepts the access]
    PEP -->|asks| PDP[PDP decides]
    PIP[PIP supplies attributes] --> PDP
    PAP[PAP authors policy] --> PDP
    PDP -->|permit or deny| PEP
    PEP -->|enforces| B[Tool runs or is refused]
</Mermaid>

This split is the precondition for everything that follows: you cannot standardize the interface between a decider and an enforcer until you have separated them. And yet the split was named around 2000 and had a full standard by 2003. So why, a quarter-century later, is "who authorized this tool call?" still an open question? Because the first serious attempt to answer it was too heavy to deploy, and the thing that won the world instead was too coarse to express it.

## 3. XACML's heavyweight PDP/PEP and OAuth's lightweight scopes

Two standards took opposite bets on the decision/enforcement split, and both failed the per-call agent question -- one because it was too heavy to deploy, the other because it was too coarse to express it.

The first bet was to externalize everything. The eXtensible Access Control Markup Language became an OASIS Standard in February 2003, with the still-current 3.0 ratified in January 2013 [@xacml30]. It was the serious, interoperable externalized-authorization standard of the 2000s: a full XML policy language on top of the PDP/PEP/PIP/PAP architecture, correct in principle and auditable in the center.

Its failure was deployability, not correctness -- and the evidence is the standard's own remediation. OASIS later published a JSON Profile for XACML whose stated motivation is that "it becomes important for XACML to be easily understood in order to increase the likelihood it will be adopted" [@xacml-json]: a primary-source admission that the XML itself was the adoption barrier.

A whole shorthand language, ALFA, exists mainly to escape the XML. To answer one yes-or-no question a team had to stand up a PDP, author XML policy sets, wire a PIP, and marshal an XML request and response -- so most teams never deployed it end to end.

<Aside label="Why XACML's architecture outlived its XML">
The PDP/PEP/PIP/PAP *architecture* is everywhere today -- it is the shape of every externalized authorizer. What teams routed around was the XML *policy language*, first with the OASIS JSON Profile (whose own goal was to "increase the likelihood it will be adopted") and then with shorthand languages like ALFA [@xacml-json] [@xacml30]. Keep this split in mind: it is a preview of AuthZEN's later move of standardizing less, not more.
</Aside>

The second bet was to externalize nothing. OAuth 2.0 (RFC 6749, 2012) took the opposite path: after the user authenticates, bake coarse, authorization-server-defined entitlements directly into the bearer token as `scope` -- per Section 3.3, "a list of space-delimited, case-sensitive strings ... defined by the authorization server" [@rfc6749]. It won the web because it is nearly free to enforce: a local, constant-time, string-set membership check, no PDP and no round trip.

But scopes are **static** (frozen into the token at issuance), **opaque** (`read:documents` names no specific document), and **resource-blind**. They cannot express a per-call, per-resource, user-contextual constraint. The AuthZEN working-group charter names this exact pattern -- "embedding entitlements into OAuth2 bearer tokens after user authentication" -- as the problem it exists to fix [@authzen-charter]. And worse for agents: a bearer token is ambient authority, which is precisely Hardy's confused-deputy hazard wearing a modern uniform.

<Definition term="OAuth scope">
Per RFC 6749 Section 3.3, a coarse, authorization-server-defined entitlement: a space-delimited string baked into a bearer token after authentication. It is static, opaque, and resource-blind -- it names a class of operation, never a specific object [@rfc6749].
</Definition>

<Definition term="Attribute-Based Access Control (ABAC)">
Per NIST SP 800-162, authorization decided by evaluating attributes of the subject, the resource, the requested operation, and the environment against policy -- the attribute-and-context model that AuthZEN's Subject-Action-Resource-Context request sits inside [@nist-800-162-landing].
</Definition>

Make the failure executable. A `crm:write` scope check happily returns `true` for `delete_customer(id="9931")` -- and there is no way to widen it into "only customers in this user's own tenant," because the tenant relationship is not in the token and cannot be.

<RunnableCode lang="js" title="The scope check that passes -- and authorizes nothing">{`
// A scope is a static string, frozen into the token at issuance time.
const token = { sub: "user-42", scope: "crm:read crm:write", aud: "urn:crm-api" };

function hasScope(token, needed) {
  return token.scope.split(" ").includes(needed);
}

// The check the tool call actually performs before running delete_customer:
console.log("hasScope crm:write ->", hasScope(token, "crm:write")); // true, so the call proceeds

// The decision we actually wanted to make:
function canDeleteCustomer(user, customer) {
  // Needs the customer's tenant AND the user's tenant.
  // Neither value is present in the token, and a static scope cannot carry them.
  return customer.tenantId === user.tenantId;
}

const customer = { id: "9931", tenantId: "tenant-A" };
const user     = { id: "user-42", tenantId: "tenant-B" };

console.log("canDeleteCustomer ->", canDeleteCustomer(user, customer)); // false
console.log("The scope check already said yes. The token could never carry customer.tenantId.");
`}</RunnableCode>

> **Note:** The token was valid. The tool ran. No one authorized it. Tightening this is not a matter of minting narrower scopes -- the information the decision needs (the specific resource, the user's relationship to it, the runtime context) was frozen out of the token at issuance and cannot be minted back in. The fix is not a better token; it is moving the decision out of the token, to the point of use.

Two dead ends, opposite in every way, frame the same gap: externalize the decision and it is too heavy to deploy; bake it into the token and it is too coarse to express. The obvious repair -- keep externalization but make the engine lightweight and developer-friendly -- is exactly what a whole generation built next. So why did *that* fail too?

## 4. Five generations, each forced by the last one's failure

The genealogy of externalized authorization is not a list of options; it is a forcing function. Each generation was pushed into existence by a *specific* failure of the one before, and the interesting content is precisely what each failure motivated next.

**Generation 1 -- XACML PDP/PEP (2003 to 3.0 in 2013).** It solved interoperable *policy expression* and failed on deployability, for the reasons Section 3 gave: the XML was too heavy, and the architecture survived while the language was routed around [@xacml30] [@xacml-json].

**Generation 2 -- OAuth 2.0 scopes (2012).** It solved lightweight, ubiquitous, coarse authorization and failed on granularity and dynamism: scopes are static, baked in, and resource-blind [@rfc6749]. The charter that eventually answered this names the pattern outright [@authzen-charter].

**Generation 3 -- policy-as-code engines (OPA in 2016, Zanzibar in 2019, Cedar in 2023, alongside Topaz and Cerbos).** This generation *technically succeeded*. Open Policy Agent evaluates Rego against input JSON and graduated from the CNCF in February 2021 [@opa-repo] [@cncf-opa].

Google's Zanzibar answers relationship checks at a scale nothing else has published: it "scales to trillions of access control lists and millions of authorization requests per second," and "has maintained 95th-percentile latency of less than 10 milliseconds and availability of greater than 99.999% over 3 years" (lead author Ruoming Pang) [@zanzibar-google] [@zanzibar-usenix]. AWS Cedar, open-sourced in May 2023, trades raw expressiveness for a deliberately analyzable language [@cedar-aws] [@cedar-arxiv]. Vendor authorizers -- Aserto's Topaz, Cerbos -- sit here too [@topaz-site] [@cerbos].

And yet the generation failed on *interoperability*: every engine spoke its own PDP wire API. Capability everywhere, interoperability nowhere. This is the SAML-before-OIDC moment for authorization, and the AuthZEN charter names the fragmentation directly.<Sidenote>Throughout the base MCP authorization work, OAuth 2.1 is cited as an IETF *draft* -- `draft-ietf-oauth-v2-1-13` in the base MCP spec, and draft-12 in the AuthZEN MCP profile [@oauth21-draft] [@mcp-auth-spec]. It has no RFC number, and attaching one is a common error worth not making.</Sidenote>

<PullQuote>
"Interoperability between different architectures and between different implementations are particularly challenging." -- the OpenID AuthZEN Working Group charter, on the Generation 3 fragmentation [@authzen-charter]
</PullQuote>

**Generation 4 -- the OpenID AuthZEN Authorization API 1.0.** The working group formed in 2023; the specification became an OpenID Foundation Final Specification on January 12, 2026 [@authzen-final-announce]. It standardized the PDP-to-PEP wire protocol itself, engine-neutral -- the OIDC moment for authorization [@authzen-api-spec]. Section 5 unpacks exactly how.

**Generation 5 (in flight) -- the AuthZEN MCP Tool Authorization Profile, "COAZ."** Draft 1, dated February 13, 2026, extends that wire protocol to per-tool-call, parameter-level decisions for AI agents [@authzen-mcp-profile]. Note the maturity gap right here: a Draft profile, thirty-two days after the Final API, with no production corpus of tool mappings yet. Section 6 is its mechanics.

<Mermaid caption="Five generations toward per-call agent authorization. Each generation's fatal limit forces the next, from XML weight to coarse scopes to per-engine APIs to a standardized interface, finally profiled to the tool call.">
flowchart TD
    G1["Gen 1 XACML PDP/PEP, 2003 to 2013: interoperable policy, but XML too heavy to deploy"] --> G2
    G2["Gen 2 OAuth 2.0 scopes, 2012: lightweight and ubiquitous, but too coarse to express the call"] --> G3
    G3["Gen 3 policy engines OPA, Zanzibar, Cedar, 2016 to 2023: fine-grained and fast, but every engine its own API"] --> G4
    G4["Gen 4 AuthZEN Authorization API 1.0, Final 2026: the PDP-to-PEP interface, standardized"] --> G5
    G5["Gen 5 AuthZEN MCP profile COAZ, Draft 2026: the interface, profiled to the tool call"]
</Mermaid>

| Generation | Year | Key idea | Fatal limit | Status |
|---|---|---|---|---|
| XACML PDP/PEP | 2003 to 2013 | Externalize the decision; XML policy language over PDP/PEP | XML too heavy; rarely deployed end to end | Superseded (architecture survives) |
| OAuth 2.0 scopes | 2012 | Bake coarse entitlements into a bearer token after login | Static, opaque, resource-blind | Active, insufficient alone |
| Policy-as-code engines | 2016 to 2023 | Fine-grained, dynamic decisions in code (OPA, Zanzibar, Cedar) | Every engine speaks its own wire API | Active |
| AuthZEN Authorization API 1.0 | Final, 2026 | Standardize the PDP-to-PEP wire protocol, engine-neutral | Generic; needs a profile per domain | Final Specification |
| AuthZEN MCP profile (COAZ) | Draft, 2026 | Profile the wire protocol down to the tool call | Draft; no production corpus yet | Draft 1 |

Watch the human hand-off, because it is the tell. The founders of the very engines that fragmented Generation 3 are the editors and authors of the AuthZEN specifications that unify them: Omri Gazitt of Aserto, behind Topaz [@aserto-about]; Alex Olivier of Cerbos; David Brossard, carrying the XACML and ABAC heritage; Atul Tulshibagwale of SGNL, who spans both the base API and the MCP profile [@authzen-api-spec] [@authzen-mcp-profile]. The people who built the silos are the ones standardizing the interface between them.

Four generations of *adding more* -- more policy language, more scopes, more engines -- and the interoperability gap only widened. Generation 4 closed it by doing the opposite. What is the one subtractive idea that makes AuthZEN succeed where XACML and every siloed engine did not?

## 5. AuthZEN 1.0 and the SARC model: standardize the interface, not the engine

Here is the whole idea in one sentence: AuthZEN standardizes the wire protocol between the decision point and the enforcement point, and nothing else. The specification's abstract says PDPs and PEPs "communicate authorization requests and decisions to each other without requiring knowledge of each other's inner workings" [@authzen-api-spec]. The decisive move is *subtractive*. The spec explicitly places the "policy language, architecture, and state management aspects of a PDP" beyond its own scope [@authzen-api-spec]. That omission is not a gap in the standard. It *is* the standard.

Build the mechanism in three parts.

**The information model.** The request names a **Subject**, an **Action**, a **Resource**, and a free-form **Context** -- the model the MCP profile abbreviates SARC -- and the PDP's response adds a fifth entity, the **Decision** [@authzen-api-spec] [@authzen-mcp-profile]. The canonical example makes it concrete: *Can Alice view document #123 at 16:30 on Tuesday?* A PEP builds a small JSON request -- subject with a type and id, an action name, a resource with a type and id, and a context object -- and POSTs it. The PDP answers with a boolean.

<Definition term="Subject-Action-Resource-Context (SARC)">
The AuthZEN information model that the MCP profile abbreviates SARC: a request naming the subject (who), the action (what operation), the resource (on what object), and a free-form context (under what circumstances), to which the PDP's response adds a Decision. SARC is the profile's shorthand; the core Authorization API adds the Decision entity in the response [@authzen-api-spec] [@authzen-mcp-profile].
</Definition>

**The three endpoint families.** Access Evaluation takes one subject, action, and resource and returns one decision. Access Evaluations batches many decisions into a single round trip -- "boxcarring." Search asks the inverse question: which resources, subjects, or actions are permissible [@authzen-api-spec]. The API is transport-agnostic with a normative HTTPS binding, and the PEP ships only the SARC attributes -- never the policy, never the engine's state.

**Why the omission is the breakthrough.** Because AuthZEN standardizes only the interface, Topaz, OPA, Cedar, Cerbos, or Axiomatics can all sit behind the same PEP, exactly as OIDC let any identity provider sit behind the same relying party -- an analogy the charter draws itself [@authzen-charter]. The PDP becomes *swappable*. That is the interoperability the Generation 3 engines never had, and it arrives by subtraction, not addition.

<PullQuote>
Policy Decision Points and Policy Enforcement Points "communicate authorization requests and decisions to each other without requiring knowledge of each other's inner workings." -- AuthZEN Authorization API 1.0 [@authzen-api-spec]
</PullQuote>

> **Key idea:** AuthZEN succeeds by standardizing *less* than XACML, not more. It ratifies only the PDP-to-PEP interface -- the Subject-Action-Resource-Context request and the Decision response -- and deliberately leaves the policy language, the architecture, and the state out of scope. That is precisely how OIDC beat SAML-era fragmentation for authentication: not a better identity provider, but a standard interface any identity provider could sit behind. The interoperability the fine-grained engines never had comes from the subtraction.

The milestone is what makes the next section possible. The Authorization API 1.0 became an OpenID Foundation Final Specification on January 12, 2026 -- "not subject to further revision," carried on 107 total votes [@authzen-final-announce]. A ratified, engine-neutral substrate is exactly what an agentic profile can now build on.<Sidenote>Hold the maturity gap in view even at the moment of triumph: the API is Final and "not subject to further revision" as of January 12, 2026 [@authzen-final-announce], but the profile that maps tool calls onto it is Draft 1, dated February 13, 2026 -- thirty-two days later, and still moving [@authzen-mcp-profile].</Sidenote>

<Mermaid caption="The SARC request and the Decision response. A PEP assembles Subject, Action, Resource, and Context into one request to an engine-neutral PDP, which returns a Decision. The same request shape reaches three endpoint families.">
flowchart LR
    S[Subject: the user] --> REQ[AuthZEN request]
    A[Action: the operation] --> REQ
    R[Resource: the object] --> REQ
    C[Context: agent, time, environment] --> REQ
    REQ --> PDP[AuthZEN PDP, engine-neutral]
    PDP --> D[Decision: permit or deny]
    PDP --> EP["Endpoints: Access Evaluation, Access Evaluations, Search"]
</Mermaid>

But a ratified PDP-to-PEP protocol is, by design, *generic*. Hand a PEP a raw MCP `tools/call` -- a JSON-RPC method name and an `arguments` object -- and it has no standard recipe for turning those parameters and the caller's token claims into a Subject, an Action, a Resource, and a Context. Something has to specify that mapping. That something is the profile.

## 6. The MCP Tool Authorization Profile (COAZ), in two layers

You now believe the idea. Here is the whole machine -- and it lives in two distinct layers that must never be run together into one. Layer 1 answers *"is this token even valid for THIS server?"* Layer 2 answers *"is THIS subject allowed THIS action on THIS resource in THIS context?"* They are different questions, anchored in different specifications, and the most common factual error in this whole area is putting a Layer 1 control into Layer 2.

**Layer 1 -- base MCP authorization (revision 2025-06-18).** The Model Context Protocol's own authorization specification defines the token layer: OAuth 2.1 (the IETF draft) [@oauth21-draft], plus RFC 9728 Protected Resource Metadata for discovery [@rfc9728], RFC 8414 authorization-server metadata [@rfc8414], RFC 7591 Dynamic Client Registration [@rfc7591], and RFC 8707 Resource Indicators [@rfc8707].

The load-bearing requirement: the spec says MCP clients "MUST implement Resource Indicators for OAuth 2.0 as defined in [RFC 8707]," and MCP servers "MUST validate that access tokens were issued specifically for them as the intended audience" [@mcp-auth-spec]. That is **audience binding** -- the anti-confused-deputy control that keeps a token minted for one server from being spent at another.

<Definition term="Resource Indicators (RFC 8707) and audience binding">
The Layer 1 control that binds an OAuth token to a specific MCP server as its intended audience, so a token minted for one server cannot be replayed at another. The base MCP authorization spec requires it; it is the anti-confused-deputy check, and it belongs to the base spec, not to the AuthZEN profile [@rfc8707] [@mcp-auth-spec].
</Definition>

> **Note:** Audience binding via RFC 8707 lives in the base MCP authorization spec [@mcp-auth-spec]. The AuthZEN MCP profile does *not* use RFC 8707; it maps tool calls to decisions via COAZ and a PDP, and it references Rich Authorization Requests (RFC 9396) only *informatively* [@authzen-mcp-profile] [@rfc9396]. Saying "the AuthZEN profile uses RFC 8707" conflates the two layers and is simply wrong.

**Layer 2 -- the AuthZEN MCP profile (COAZ, Draft 1).** A tool advertised in `tools/list` marks itself with `"coaz": true` and carries an `x-coaz-mapping` object inside its `inputSchema` [@authzen-mcp-profile]. The mapping's fields are Common Expression Language expressions over two inputs: `token.*`, the complete decoded claims of the JWT access token, and `params.arguments.*`, the tool-call arguments [@cel]. The profile's own worked example maps `resource.id = params.arguments.location` and `subject.id = token.sub`.

At call time, a Policy Enforcement Point -- the MCP gateway or the MCP server itself -- validates the token, evaluates the CEL to construct the SARC request, and calls the AuthZEN PDP's Access Evaluation endpoint. Only on a permit Decision does the `tools/call` execute [@authzen-mcp-profile].

<Definition term="COAZ (Compatible with OpenID AuthZen)">
The AuthZEN MCP profile's mechanism: a tool marks itself `coaz: true` and carries a declarative mapping so a PEP can turn an MCP tool call into a SARC decision, evaluated by an AuthZEN PDP before the tool runs [@authzen-mcp-profile].
</Definition>

<Definition term="x-coaz-mapping">
The extension object inside a tool's `inputSchema` whose fields are CEL expressions over the token claims and the call arguments. It defines, declaratively, how a `tools/call` becomes a Subject-Action-Resource-Context request [@authzen-mcp-profile].
</Definition>

<Definition term="Common Expression Language (CEL)">
A non-Turing-complete expression language, originally a Google project, used by `x-coaz-mapping` to derive SARC attributes from `token.*` and `params.arguments.*` -- for example `token.sub` or `params.arguments.location` [@cel].
</Definition>

The load-bearing design choice is the **Zero-Trust separation**. The profile represents the human user "as the AuthZen Subject" and the AI agent "in the AuthZen Context," so policy "can independently evaluate the trust level of both the user and the agent" [@authzen-mcp-profile]. This is the wire-format resolution of the [user-versus-agent attribution gap](/blog/agentic-identity-on-windows-when-the-process-acting-on-your-/). Walk it: `token.sub` -- the user -- becomes the Subject; `params.name` becomes the Action by default; `params.arguments.location` becomes the Resource; the agent's own identity goes into the Context; and the PDP's answer is the Decision.

<Mermaid caption="One tool call, two layers. Layer 1 validates the token and its audience per RFC 8707. Layer 2 evaluates the mapping into a SARC request and calls the PDP, and only a permit lets the tool run.">
sequenceDiagram
    participant C as MCP client
    participant P as Gateway PEP
    participant S as MCP server
    participant PDP as AuthZEN PDP
    C->>P: tools/call with access token
    Note over P: Layer 1, validate token, check audience per RFC 8707
    P->>P: evaluate x-coaz-mapping into a SARC request
    P->>PDP: Access Evaluation, subject action resource context
    PDP-->>P: Decision, permit or deny
    alt Decision is permit
        P->>S: forward tools/call
        S-->>C: tool result
    else Decision is deny
        P-->>C: request refused
    end
</Mermaid>

The second diagram shows the *data* transformation the first hides inside one step: how the mapping builds the four SARC entities.

<Mermaid caption="How an x-coaz-mapping becomes a SARC request. Token claims and call arguments feed CEL expressions that emit the four SARC entities, with the user as the Subject and the agent placed in the Context.">
flowchart TD
    T["token.* decoded JWT claims"] --> CEL[CEL evaluation]
    A["params.arguments.* tool-call arguments"] --> CEL
    CEL --> SUB["Subject from token.sub, the user"]
    CEL --> ACT["Action from params.name"]
    CEL --> RES["Resource from params.arguments.location"]
    CEL --> CTX["Context carrying the agent identity"]
    SUB --> PDP[AuthZEN PDP]
    ACT --> PDP
    RES --> PDP
    CTX --> PDP
    PDP --> D[Decision gates the tool call]
</Mermaid>

Made runnable, the "with authorization" answer mirrors the failing scope check from Section 3 -- same inputs, now assembled into a decision the engine can make.

<RunnableCode lang="js" title="From tools/call to a SARC decision">{`
// An MCP tools/call: the method name and its arguments.
const params = { name: "read_weather", arguments: { location: "Seattle" } };

// The decoded, already-validated JWT access-token claims.
const token = { sub: "user-42", client_id: "agent-7", scope: "weather:read" };

// A tiny x-coaz-mapping (in the real profile these fields are CEL expressions).
const mapping = {
  subject:  (p, t) => ({ type: "user", id: t.sub }),          // the user
  action:   (p, t) => ({ name: p.name }),                     // default: the tool name
  resource: (p, t) => ({ type: "location", id: p.arguments.location }),
  context:  (p, t) => ({ agent: t.client_id })                // the agent goes here
};

// The PEP assembles the SARC request from token claims and call arguments.
const sarc = {
  subject:  mapping.subject(params, token),
  action:   mapping.action(params, token),
  resource: mapping.resource(params, token),
  context:  mapping.context(params, token)
};
console.log(JSON.stringify(sarc, null, 2));

// A mock AuthZEN PDP: this user may read weather only for Seattle.
function accessEvaluation(req) {
  return { decision: req.subject.id === "user-42" && req.resource.id === "Seattle" };
}

const result = accessEvaluation(sarc);
console.log("decision ->", result.decision); // true -> the tool call is allowed to run
`}</RunnableCode>

Here are the same three moves as the literal artifacts a server and PEP exchange, not an analogy. First, the `x-coaz-mapping` the server publishes inside the tool's `inputSchema`; every value is a CEL expression over `token.*` and `params.arguments.*`, and static types are written as single-quoted CEL string literals [@authzen-mcp-profile]:

```json
"x-coaz-mapping": {
  "subject": [{ "type": "'user'", "id": "token.sub" }],
  "resource": [{ "type": "'location'", "id": "params.arguments.location" }],
  "context": [{ "agent": "token.client_id" }]
}
```

With `action` left unmapped it defaults to the tool name. Evaluating those expressions against the call above -- `token.sub` is `user-42`, `params.arguments.location` is `Seattle`, `token.client_id` is `agent-7` -- the PEP sends this AuthZEN Access Evaluation request to the PDP [@authzen-mcp-profile]:

```json
{
  "subject": { "type": "user", "id": "user-42" },
  "action": { "name": "read_weather" },
  "resource": { "type": "location", "id": "Seattle" },
  "context": { "agent": "agent-7" }
}
```

The PDP returns the minimal AuthZEN Decision, and only a permit lets the tool call run [@authzen-api-spec]:

```json
{
  "decision": true
}
```

| Dimension | Layer 1: base MCP authorization | Layer 2: AuthZEN MCP profile (COAZ) |
|---|---|---|
| Question answered | Is this token even valid for THIS server? | Is THIS subject allowed THIS action on THIS resource in THIS context? |
| Specs and RFCs | OAuth 2.1 draft, RFC 9728, RFC 8414, RFC 7591, RFC 8707 | AuthZEN Authorization API 1.0 plus the COAZ profile and CEL |
| Where the decision lives | In the token and its audience claim | In an external AuthZEN PDP, called per tool call |
| What it cannot do | Express per-resource, per-user, contextual decisions | Decide anything if no PEP in the path processes the mapping |

| SARC entity | CEL source example | What it represents | Constraint from the profile |
|---|---|---|---|
| Subject | `token.sub` | The human user on whose behalf the call is made | At least one field must come from the token |
| Action | `params.name` | The tool being invoked | Defaults to the tool name if unmapped |
| Resource | `params.arguments.location` | The specific object the call targets | Drawn from the call arguments |
| Context | agent identity from `token` | The AI agent and runtime circumstances | Must carry the agent identity |

<Aside label="Why standardize the interface, not the engine">
The profile inherits AuthZEN's OIDC-for-authorization bet: standardize the interface any engine can sit behind, and let the market keep its engines [@authzen-charter]. The evidence that this is deliberate, not accidental, is the author list -- the founders of Topaz (Aserto) and Cerbos, and the XACML and SGNL veterans, wrote the interface that makes their own products swappable [@aserto-about] [@cerbos].
</Aside>

Two more properties matter for later. COAZ is opt-in and backward compatible: non-COAZ clients "will simply ignore these fields." And the deployment-coverage caveat is stated in the profile itself: "If no PEP in the request path processes the mapping, no AuthZen authorization occurs and access control falls back to whatever other mechanisms are in place" [@authzen-mcp-profile]. Hold onto that sentence; it becomes a hard ceiling in Section 8.

That is one coherent, two-layer stack. But it is not the only body standardizing agent authorization in 2026. Some want to encode more in the token; most MCP servers ship Layer 1 alone; others govern an entirely different slice. Which layer does each own, where do they collide, and which one actually answers the tool-call question?

## 7. The standards race to govern AI agents

Several bodies are standardizing agent authorization at once, at different layers, and the honest framing is that most of them compose rather than collide. The genuine rivalry is narrower than the headlines suggest. My position: externalized, interoperable, per-call authorization is the architecturally correct layer for "who authorized this tool call?" -- and it is racing a deployment reality in which, by all indications, the overwhelming majority of MCP servers still authorize with scopes alone.

**AuthZEN and COAZ (externalize the decision).** The per-call decision lives in a policy engine reached over a standard API. It is the only approach that both expresses the runtime, per-resource question *and* standardizes the interface [@authzen-mcp-profile] [@authzen-api-spec].

**OAuth-native encoding (put more in the token).** Rich Authorization Requests replace coarse scope strings with a structured `authorization_details` array, so a token can carry something like "may transfer up to \$500 from account X"; the AuthZEN profile references RAR only informatively [@rfc9396].

Token Exchange lets one token be swapped for another -- an agent trading its own token for a downscoped, user-delegated one -- the leading candidate answer to "where does the user identity in the token come from?" [@rfc8693]. Both add zero hot-path round-trips, because the decision is pre-computed into the token. Both are therefore **static at issuance**: the token cannot reflect state that changes between issuance and the call.

**Base MCP OAuth-only (the deployed baseline).** The base MCP spec standardizes only scopes plus RFC 8707 audience binding and no per-call PDP [@mcp-auth-spec] -- and, though no MCP-adoption survey exists to measure this, that baseline is almost certainly what most servers currently implement. This is the baseline the article argues is structurally insufficient for the tool-call question -- necessary, cheap, and blind to the specific resource.

**Siloed engines and adjacent agentic standards.** OPA, Zanzibar, and Cedar can each *be* the AuthZEN PDP behind a COAZ PEP -- competitors that become components the moment the interface is standardized [@opa-repo] [@zanzibar-google] [@cedar-arxiv].

And a different slice entirely: Google's Agent Payments Protocol and the FIDO agentic-authentication work govern *verifiable intent for commerce*. AP2 builds trust from Mandates -- "tamper-proof, cryptographically-signed digital contracts ... signed by verifiable credentials" -- proving a human authorized a *purchase* [@ap2]. That is not a per-tool decision; it composes with AuthZEN rather than competing with it. A companion article in this series covers the AP2 and FIDO slice in depth.<Sidenote>Common Expression Language, the language the COAZ mapping is written in, is a Google project with evaluators in Go, C++, and Java. The article shows one expression but does not teach the grammar -- the mapping layer's tooling already exists across runtimes [@cel].</Sidenote>

| Dimension | AuthZEN COAZ | MCP OAuth-only | OAuth-native (RAR + exchange) | Siloed engines (OPA, Zanzibar, Cedar) | AP2 + FIDO |
|---|---|---|---|---|---|
| Added round-trips per call | One PDP call (or one boxcarred for many) | None | None (encoded at issuance) | One engine call | None (local credential verify) |
| Decision timing | Per call, runtime | Issuance (static) | Issuance (static) | Per call, runtime | Per mandate (signing time) |
| Granularity | Parameter and resource level | Coarse scope plus audience | Structured but pre-baked | Parameter and relationship level | Transaction intent |
| Expresses "this action on this object now"? | Yes | No | Partial, if known at issuance | Yes | Only for a purchase |
| User-vs-agent separation | Yes (Subject vs Context) | No | Via token exchange (indirect) | Engine-dependent | Yes (user signs mandate) |
| Interface standardized? | Yes (AuthZEN wire API) | Yes (OAuth) | Yes (RFC 9396, RFC 8693) | No (per-engine API) | Yes (AP2) |
| Maturity | Final API, Draft profile | Final (2025-06-18) | RFC-Final | Production-proven | Launched 2025 |

> **Note:** The real collision is narrower than "everyone versus everyone." It is exactly two strategies: encode more in the token (RAR and token exchange) versus externalize the decision (AuthZEN). Both are legitimate. They differ on one axis -- whether the authorization-relevant state is known at issuance or only at call time -- which is precisely why a per-call PDP is the right layer for dynamic, per-resource decisions, and pre-baked tokens are the right layer for fixed transaction limits. The rest compose.

Layered or rival, every one of these leans on the same promise: that some component can decide, at the point of use, whether a call is authorized. So ask the uncomfortable question. Even with a ratified standard, fully deployed everywhere, what can that decision never buy?

## 8. What no standard, fully deployed, can buy

This is the deliberate cold shower. A per-call PDP is the most faithful realization of complete mediation the field has yet produced -- it moves the check to the exact point of use. And it still cannot buy four things, true even if COAZ were Final and deployed everywhere tomorrow.

**The intent gap.** A PDP decides only on the attributes the PEP hands it. It cannot know the user's *true* intent. A call that is syntactically authorized -- correct subject, correct resource, permitted action -- but driven by a prompt-injected instruction is **still authorized**. No standardization closes this; it is a fundamental limit of attribute-based mediation. The whole reason agent tool calls are dangerous, prompt injection, lives in this gap, and no wire protocol reaches into it.

**The confused-deputy ceiling.** Hardy proved in 1988 that a deputy holding ambient authority can be induced to wield it for another principal [@hardy-caplore]. Audience binding and per-call decisions *contain* the confused deputy -- they narrow where and whether the authority applies -- but they do not eliminate it. The PDP trusts the PEP's SARC construction, and a compromised PEP or a maliciously authored `x-coaz-mapping` re-opens the hole. The profile concedes exactly this in its mapping-integrity discussion [@authzen-mcp-profile].

**The deployment-coverage floor.** Authorization is only as strong as the weakest enforcement point. The profile states it plainly: "If no PEP in the request path processes the mapping, no AuthZen authorization occurs" [@authzen-mcp-profile]. AutoJack is the limit case where there is no enforcement point at all -- CWE-306, zero PEPs, and "who authorized this tool call?" answered "no one" [@autojack]. No protocol can authorize a call that never reaches a decision point.

**The static-analysis ceiling.** You cannot scan your way to runtime authorization. Trail of Bits bypassed every mainstream agent-skill scanner -- ClawHub's detector, Cisco's scanner, and "all three of the scanners integrated into skills.sh" -- using tricks as blunt as prepending 100,000 newlines, and archived the working payloads publicly [@trailofbits] [@tob-repo]. Their conclusion is the ceiling: a scanner's "static nature gives an adversary unlimited bites at the apple." Pre-install scanning and per-call authorization are orthogonal; neither substitutes for the other.

<PullQuote>
A scanner's "static nature gives an adversary unlimited bites at the apple." -- Trail of Bits, on why scanning is not authorization [@trailofbits]
</PullQuote>

> **Note:** Of the four ceilings, the deployment-coverage floor is the one that bites first in practice: a per-call PDP is worthless on any path that does not route through a PEP. Sequence the work accordingly -- coverage before policy sophistication.

The two "without authorization" disclosures attack different layers and must be kept apart. One is a control-plane failure; the other is a supply-chain failure. Conflating them hides that they demand different fixes.

| Question | AutoJack | Trail of Bits |
|---|---|---|
| Layer attacked | Control plane (the MCP surface) | Supply chain (skill distribution) |
| Failure class | CWE-306 missing-authentication RCE | Detection and scanner bypass |
| What it proves | No PEP means no authorization | Scanning is not authorization |
| Fix that does NOT help | Better tokens | Better scanners |
| Primary source | Microsoft Security, June 18, 2026 [@autojack] | Trail of Bits, June 3, 2026 [@trailofbits] |

State the gap plainly. The ideal -- every call mediated, at every enforcement point, over a standard interface, informed by verified intent and live state -- is asymptotically approachable and never reachable, because true intent is not a property the PEP can observe. Being precise about that irreducible gap is what separates a real trust layer from a marketing claim. These are the *structural* ceilings, true even if the specs were finished tomorrow. But the specs are not finished. What is still genuinely unsettled as of mid-2026?

## 9. What mid-2026 leaves unsolved

This is a standards frontier, not a complexity-theory one, so each open problem is a precise engineering or governance question with a current partial state -- not a conjecture.

1. **The maturity and adoption gap.** The Authorization API is Final; the MCP profile is only Draft 1; and, as you would expect of a profile that new, effectively no production MCP server ships `x-coaz-mapping` yet [@authzen-mcp-profile]. The "with authorization" world is still largely aspirational, and, on the current evidence, the deployed majority still authorizes with scopes alone. The partial state is genuinely encouraging: a Final API is exactly the substrate on which a Draft profile can stabilize, and a multi-vendor interop program already shows the API is testable across engines [@authzen-api-spec].

2. **The user-identity-in-the-token gap.** OAuth issues the token to the *agent*, and, in the profile's own words, "there is no standard mechanism to capture the identity of the user on whose behalf the tool is being invoked" [@authzen-mcp-profile]. Yet the entire user-as-Subject separation assumes that user claim exists to map. The leading candidates are Token Exchange and identity-assertion or on-behalf-of grants [@rfc8693], none yet profiled for MCP.

3. **Mapping integrity and trust.** The `x-coaz-mapping` is authored by the MCP server, so a malicious server can craft a mapping that misrepresents the SARC request to the PDP -- substituting a benign resource id for a sensitive one. The profile requires PEPs to validate that the mapping is well-formed, but that is a *SHOULD*, not signing or attestation [@authzen-mcp-profile].

4. **PDP latency and availability on the hot path.** Every COAZ tool call adds a synchronous PDP round-trip, making the PDP a tier-0 dependency of every action. The Access Evaluations endpoint amortizes many checks into one request, and Zanzibar demonstrates the engine layer can hold 95th-percentile latency under 10 milliseconds and availability above 99.999% [@zanzibar-google] -- but there is no standardized decision-caching or time-to-live profile for MCP yet.

5. **Multi-hop agent chains.** When an agent calls another agent that calls a tool, how does the SARC Context -- which agent, acting on whose behalf -- propagate across hops? The profile fixes user-as-Subject and agent-as-Context for a single hop; nothing yet standardizes propagation across a chain [@authzen-mcp-profile].

6. **Revocation and continuous evaluation.** A decision made at call time can be stale by the next call, and a long-running agent makes thousands of calls per session. Per-call evaluation already re-checks every time -- a real advantage over decide-once-at-issuance -- but push-based revocation between the token layer and the PDP is unstandardized. This is the bridge to [Continuous Access Evaluation](/blog/the-28-hour-bargain-how-continuous-access-evaluation-made-lo/), treated in a companion article: the revocation channel that supersedes a decision before the next call.

7. **The supply-chain layer is orthogonal and unsolved.** Per-call authorization does nothing about *which* tool code got installed. Trail of Bits showed scanning does not catch malicious skills [@trailofbits] [@tob-repo], and an authorized call to a *malicious tool* is still harmful. Runtime authorization and supply-chain integrity are two separate unsolved problems, and solving one does not touch the other.

> **Note:** The user-identity-in-the-token gap is the one most likely to surprise a team standing up COAZ. The profile's whole point is separating the user (Subject) from the agent (Context) -- but OAuth hands the token to the agent, and the profile itself states there is no standard way to capture the user on whose behalf the call is made [@authzen-mcp-profile]. If the user claim is not reliably in the token, the Subject you map is not the user you think it is.

> **Note:** Notice what is *not* on this list: raw compute. The engine layer is a solved performance problem -- Zanzibar settled that years ago at planet scale [@zanzibar-google]. Every genuinely open problem here is about adoption, governance, integrity, or identity provenance. The hard part of authorizing agents in 2026 is not making the decision fast; it is agreeing on what to decide and making sure a decision happens at all.

Seven open problems -- and yet the API spec, the profile draft, and conformant PDPs are all public *today*.<Sidenote>The companion AuthZEN Access Request and Approval Profile is a separate Draft that specifies what happens on a *deny*: an approval workflow, so a refused call can escalate to a human rather than simply failing [@authzen-arap].</Sidenote> So what can an MCP server author, a gateway operator, or a platform team actually build this quarter, and what should they refuse to assume?

## 10. What you can build this quarter

Concrete guidance, keyed to the layer you occupy, each item tied to a primary source.

1. **Get Layer 1 right first.** Implement OAuth 2.1, RFC 9728 Protected Resource Metadata discovery, and RFC 8707 Resource Indicators so your tokens are audience-bound to your server [@mcp-auth-spec] [@rfc9728] [@rfc8707]. This is the cheapest, highest-impact step, and skipping it is how you become the confused deputy.

2. **Put a PEP in every tool-call path.** Never let a tool execute with no enforcement point. The AutoJack lesson is verbatim: "control planes must be authenticated, authorized, and isolated" [@autojack]. A per-call PDP upstream is worthless on any path that routes around it.

3. **For fine-grained needs, adopt the AuthZEN profile.** Mark sensitive tools `coaz: true`, author an `x-coaz-mapping` (token claims into the Subject, tool arguments into the Resource and Context -- the literal shape is the one in Section 6), and stand up a conformant AuthZEN PDP -- Topaz, OPA, Cerbos, Cedar with Verified Permissions, or Axiomatics [@authzen-mcp-profile] [@topaz-site] [@opa-repo] [@cerbos]. The engine is swappable precisely because only the interface is standardized.

4. **Separate the user from the agent.** Put the human in the Subject and the agent in the Context, per the profile's Zero-Trust guidance, so policy can evaluate the trust level of both independently [@authzen-mcp-profile].

5. **Validate the token before you map it.** Verify signature, issuer, audience, and expiry *before* extracting any claims into a SARC request [@authzen-mcp-profile]. Extracting `token.sub` from an unverified token authorizes on a forged identity -- the mapping step assumes a token that Layer 1 already vouched for.

6. **Do not treat pre-install skill scanning as authorization.** Trail of Bits proves scanning is necessary but insufficient [@trailofbits]. Pair it with runtime, per-call enforcement; the two are orthogonal controls, and neither covers the other's gap.

7. **Track the Draft and expect churn.** The profile is early -- dated February 13, 2026 -- and companion work is still moving, including the Access Request and Approval Profile for denials and the anticipated MCP Server Cards [@authzen-mcp-profile] [@authzen-arap]. Build against it, but pin versions and expect revisions.

> **Note:** If you do only two things this quarter, do these in order. First, audience-bind your tokens with RFC 8707 so a token minted for your server cannot be spent elsewhere [@mcp-auth-spec] [@rfc8707]. Second, put a PEP in front of your sensitive tools and wire it to any conformant AuthZEN PDP, mapping the user into the Subject and the agent into the Context [@authzen-mcp-profile]. Layer 1 stops the confused deputy; Layer 2 answers the per-call question.

<Aside label="What not to do">
The failure modes are as instructive as the steps. Do not let any tool-call path skip the enforcement point (that is AutoJack). Do not evaluate the mapping before validating the token (that authorizes on forged identity). Do not trust a server-authored mapping blindly -- check it is well-formed and references real schema properties. Do not collapse the user and the agent into one principal; the whole point is to reason about both. Do not treat scanning as authorization. And do not assume the Draft is stable -- it is not [@authzen-mcp-profile] [@autojack] [@trailofbits].
</Aside>

Seven things to build, and a handful of misconceptions worth dismantling before you brief your team -- several are the exact confusions this article was written to prevent.

## 11. Frequently confused, finally separated

<FAQ title="Frequently asked questions">
<FAQItem question="Doesn't OAuth already authorize the tool call?">
No. OAuth authenticates the agent and carries coarse scopes. A scope is a static, authorization-server-defined string, frozen into the token at issuance [@rfc6749]. It cannot express a per-call, per-resource, user-contextual decision -- which is exactly the pattern the AuthZEN charter names as the problem to fix [@authzen-charter]. OAuth proves you are a valid caller; it never decides whether this action on this object is allowed.
</FAQItem>
<FAQItem question="Does the AuthZEN MCP profile use RFC 8707?">
No -- and the profile's own reference list settles it: RFC 8707 appears nowhere in it, while Rich Authorization Requests (RFC 9396) is listed only as informative [@authzen-mcp-profile] [@rfc9396]. Audience binding via RFC 8707 is a Layer 1 control defined by the base MCP authorization spec, not by the profile [@mcp-auth-spec]. The profile's job is the per-call SARC decision; treating RFC 8707 as part of it collapses Layer 1 into Layer 2.
</FAQItem>
<FAQItem question="Isn't AuthZEN just XACML again?">
No. AuthZEN standardizes the PDP-to-PEP *wire protocol* -- the request and the decision -- not the policy language [@authzen-api-spec]. XACML standardized an XML policy language along with the architecture [@xacml30]. AuthZEN is deliberately engine-neutral, which is the whole reason it is deployable where XACML was not.
</FAQItem>
<FAQItem question="Is this production-ready?">
Partly. The Authorization API 1.0 is a Final Specification as of January 12, 2026 [@authzen-final-announce]. The MCP Tool Authorization Profile is Draft 1, dated February 13, 2026 [@authzen-mcp-profile]. Treat the interface as ratified and the agentic profile as emerging -- as befits a profile this new, there is effectively no production corpus of tool mappings yet.
</FAQItem>
<FAQItem question="Is AutoJack a skill-scanner bypass?">
No. AutoJack is a missing-authentication control-plane weakness (CWE-306) in AutoGen Studio that chains to remote code execution [@autojack]. The skill-scanner bypass is separate Trail of Bits research at the supply-chain layer [@trailofbits]. One is a hole where a call reaches a tool with no authorization at all; the other is malicious tool code slipping past detection. Blurring them hides which control you actually need -- enforcement coverage or supply-chain integrity.
</FAQItem>
<FAQItem question="Who is the Subject, the user or the agent?">
The profile's Zero-Trust guidance makes the human user the Subject and the AI agent the Context, so policy can evaluate both independently [@authzen-mcp-profile]. The CEL mapping is technically flexible, but that separation is the design's whole point -- it is the wire-format resolution of the user-versus-agent attribution gap.
</FAQItem>
<FAQItem question="Does the PDP see all my data?">
No. The PEP passes only the SARC attributes it constructs; the policy and the engine's state stay on the PDP side, and the API is transport-agnostic with a normative HTTPS binding [@authzen-api-spec]. The PDP sees the subject, action, resource, and context for the one decision it is asked to make -- not your data store.
</FAQItem>
<FAQItem question="How does this relate to Conditional Access and agentic identity?">
They answer different questions at different moments. Conditional Access decides "is this token good?" at login. AuthZEN decides "is this action authorized?" at each call. And the agentic-identity work named the user-versus-agent gap that COAZ's Subject-versus-Context separation resolves on the wire. Companion articles in this series treat the login-time decision and the agent-identity principal in depth.
</FAQItem>
</FAQ>

Trace the argument one last time, with the evidence now in hand. Complete mediation (1975) demanded a fresh decision at every access [@saltzer]. The confused deputy (1988) named the exact hazard an autonomous program holding someone else's token represents [@hardy-caplore]. Twenty years of PDP/PEP architecture could decide but never interoperably -- until AuthZEN's subtractive move, standardizing the interface and not the engine, finally gave "who authorized this tool call?" a runtime, interoperable answer [@authzen-api-spec]. COAZ profiles that answer down to the tool call, with the user as Subject and the agent as Context [@authzen-mcp-profile].

The industry is converging on this answer unevenly, under a live standards race -- a Final API beneath a Draft profile so new that almost no server ships it yet -- but it is converging on **externalized, per-call authorization**, not on better tokens and not on better scanners. The "without authorization" world is what its absence already costs: a control plane that authenticated no one [@autojack], and a scanner that an adversary gets unlimited tries to beat [@trailofbits].

> **Key idea:** The answer to "who authorized this tool call?" is not a better token and not a better scanner. It is an externalized, per-call authorization decision, made at the point of use, over an interface any engine can sit behind -- with the human user and the AI agent held apart on the wire, so policy can reason about both. That decision now has a standard shape. What it does not yet have, everywhere it needs to be, is a PEP to ask it.
