Identity and authorization engineer validating MCP OAuth resources, token audiences, issuers, and tool-access boundaries

An MCP Login Succeeded. Which Resource Did You Authorize?

Fri, Sep 18, 2026

Consider a synthetic HTTP deployment with one Model Context Protocol (MCP) client, two separately identified protected resources, and a shared identity-provider host exposing two controlled authorization-server contexts. The client can reach https://accounts.example/mcp and https://inventory.example/mcp. The authorization servers are https://identity.example/as/accounts and https://identity.example/as/inventory. A user signs in successfully. That proves an authentication event happened at an authorization server; it does not yet prove which protected resource received delegated authority, which token this server should admit, or which tool, tenant and object the caller may touch.

This playbook pins normative MCP behavior to the July 28, 2026 authorization specification and applies only to HTTP-based transports. The specification says HTTP implementations that support authorization should follow this flow, while STDIO implementations should not use it and should instead obtain credentials from the environment (Model Context Protocol, Authorization: specification 2026-07-28, specification revision July 28, 2026; accessed September 17, 2026).

The goal is an evidence-backed allow, reject or hold decision. The evidence set is a synthetic trust ledger: selected resource, validated issuer, per-request state and Proof Key for Code Exchange (PKCE) ownership, intended token audience, application-policy result, and downstream credential owner. Every positive exchange has a near-match negative case. Nothing here treats a successful browser redirect, a valid signature, or tool discovery as sufficient authorization.

Separate login, protocol authorization and tool permission

OAuth authorization is not the same decision as user authentication, and neither decision replaces application authorization. MCP’s pinned specification describes authorization at the transport level for HTTP-based transports; it does not standardize every tenant, object or business-operation permission an MCP tool might enforce. That distinction matters because a token can be valid for the right server and still be insufficient for a particular action. The broader architectural idea of identity-based cloud trust boundaries is useful context, but it is not evidence that this MCP exchange is correct.

The operating model in this article is proposed, not an MCP protocol requirement. It converts documented protocol requirements into reviewable test gates and adds organization-specific controls where the specifications deliberately leave policy open.

Layer

Question to prove

Synthetic failure that must be distinguishable

Identity-provider login

Did the intended account authenticate at the expected authorization context?

User authenticates to the wrong issuer context

OAuth/MCP authorization

Was authority requested and issued for the selected MCP resource?

Code flow succeeds but resource A was replaced by resource B

Protected-resource admission

Is this token valid and intended for this server on this request?

Token is active but audience is another MCP server

Application authorization

May this principal perform this operation on this tenant/object?

Read token is used for a write or cross-tenant object

The decision discipline is simple: do not let success at an earlier layer short-circuit a later one. A login proves login. A token proves only what the resource server has actually validated about that token. A tool call is allowed only after application policy independently approves the requested operation and target data.

Build a trust and ownership ledger

Before opening a browser, inventory the things that can change the authorization boundary. For this synthetic fixture, record the MCP client build identifier, HTTP transport, both canonical protected-resource URIs, the exact protected-resource metadata locations, each permitted authorization-server issuer, the chosen registration mechanism, registered redirect URI, token-validation component and any downstream API. Record an accountable owner for every trust edge. This is a narrower artifact than a general agent registry; agent ownership and governance can supply the surrounding lifecycle model, while this ledger is specific to one authorization path.

A useful trust ledger separates observed values from approved relationships. RFC 9728 lets protected-resource metadata advertise authorization servers, but it explicitly leaves secure determination of the appropriate authorization servers to the application or deployment. In other words, a syntactically valid metadata document is not an organizational trust grant (IETF / RFC Editor, RFC 9728: OAuth 2.0 Protected Resource Metadata, published April 2025; accessed September 17, 2026).

Ledger field

Synthetic value

Evidence owner

Decision use

Client

client-web-17

Client maintainer

Reproduce the tested build

Resource A

https://accounts.example/mcp

Accounts MCP owner

Exact resource selected

Resource B

https://inventory.example/mcp

Inventory MCP owner

Negative cross-resource case

Allowed issuer for A

https://identity.example/as/accounts

Identity owner

Discovery and callback binding

Allowed issuer for B

https://identity.example/as/inventory

Identity owner

Reject issuer swapping

Redirect URI

https://client.example/oauth/callback

Client owner

Registration/callback control

Token validator

mcp-token-gate-v3

Resource-server owner

Audience, validity and issuer checks

Downstream credential

accounts-api-service-credential

API owner

Prohibit inbound-token forwarding

Record what was selected before the browser redirect

The pinned MCP specification requires the client, before redirecting the user agent, to record the issuer from the selected authorization server’s validated metadata and associate it with the same per-request record that stores the PKCE verifier and, when used, state. The point is not merely to remember “which provider”; it is to bind the callback to the authorization context chosen before untrusted response parameters arrive.

For the proposed ledger, create a random request handle such as req_7f..., then retain: selected resource, expected issuer, redirect URI identifier, PKCE-verifier ownership marker, state hash, client identifier and creation time. Do not put the verifier, state value, authorization code, access token or refresh token in ordinary audit logs. Store secret material in the client’s protected transaction/session store; log only safe identifiers or one-way derived evidence needed to prove that the same per-request record was used.

Keep two resources and two authorization contexts distinct

The fixture deliberately uses the same identity-provider host for two issuer paths. That catches implementations that collapse issuers to a host name or “provider” label. Resource A is allowed only with the accounts issuer; resource B is allowed only with the inventory issuer. The forbidden combinations are first-class tests, not accidental errors.

Resource

Permitted issuer

Tenant fixture

Expected decision

Accounts MCP

/as/accounts

tenant-alpha

Allow only after all gates pass

Inventory MCP

/as/inventory

tenant-beta

Allow only after all gates pass

Accounts MCP

/as/inventory

any

Reject before authorization-code exchange

Inventory MCP

/as/accounts

any

Reject before authorization-code exchange

Use reserved example domains and synthetic object identifiers only. The lab should not depend on public targets or live customer data. Cleanup means deleting synthetic grants, invalidating test tokens through the controlled authorization system where supported, removing temporary client registrations, and retaining only redacted evidence plus the approved trust-ledger version.

Validate protected-resource discovery before trusting it

Start from the MCP resource URL the operator intended to configure, not from an authorization server suggested by a callback or error page. Under the pinned MCP revision, protected MCP servers must implement OAuth Protected Resource Metadata, and clients must use it for authorization-server discovery. Clients must support both the WWW-Authenticate resource_metadata route and the RFC 9728 well-known route; when the challenge supplies resource_metadata, the client uses that URL, otherwise it falls back to the well-known locations defined by the specification.

RFC 9728 defines the protected-resource metadata resource value and its validation. If metadata is fetched from a well-known URI, the returned resource must be identical to the protected-resource identifier from which that URI was constructed. If discovery came from a WWW-Authenticate resource_metadata link, the returned resource must be identical to the URL the client used for the protected-resource request. If not, the metadata must not be used.

Use this synthetic discovery contract:

# Illustrative, not executed
GET https://accounts.example/mcp

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://accounts.example/.well-known/oauth-protected-resource/mcp"

GET https://accounts.example/.well-known/oauth-protected-resource/mcp

{
  "resource": "https://accounts.example/mcp",
  "authorization_servers": ["https://identity.example/as/accounts"]
}

The first assertion is metadata.resource == selected_resource. The second is deployment policy: every advertised issuer that the client might select must also be permitted for that resource by the trust ledger. This additional allowlist is justified because RFC 9728 says secure determination of suitable authorization servers is application-dependent; metadata publication alone is not proof of trust.

Run at least these discovery cases:

Case

Input change

Expected assertion

Baseline

Resource and metadata agree

Continue to approved issuer selection

Resource mismatch

Metadata says https://inventory.example/mcp

Reject metadata; do not redirect

Unexpected issuer

Metadata advertises /as/unapproved

Hold/reject under organization trust policy

Redirected metadata

Discovery URL redirects to another host

Validate each destination under egress policy; do not blindly follow

Metadata changed

New challenge points to changed metadata

Re-fetch, revalidate, and re-evaluate trust before use

The security rationale is not hypothetical protocol folklore. RFC 9728 identifies server-side request forgery (SSRF) risk because discovery can cause clients to fetch authorization-server metadata from previously unknown locations, and the MCP security guidance likewise recommends controls such as HTTPS, private-address blocking where appropriate and validation of redirect targets (Model Context Protocol, Security Best Practices: 2026-07-28 documentation, versioned documentation for July 28, 2026; accessed September 17, 2026).

Do not “prove” SSRF by making requests to cloud metadata addresses or private infrastructure. In an authorized lab, simulate the rejection with reserved or locally stubbed destinations and assert that the HTTP client refuses the disallowed route before network access. Failure handling is fail-closed: preserve the redacted request ID, discovery URL category and reason code, then stop before registration or browser redirect.

Qualify client registration and the authorization request

The July 2026 MCP revision supports three registration paths and gives them a selection order for clients that support all options: use pre-registered information when available; otherwise use Client ID Metadata Documents (CIMD) when the authorization server advertises support; otherwise use Dynamic Client Registration (DCR) when supported; otherwise prompt for client information. The same revision labels DCR deprecated and retained for backward compatibility, while MCP clients and authorization servers SHOULD support CIMD. That language is specific to this pinned revision and should not be generalized to every server implementation.

The specification itself references an OAuth Client ID Metadata Document Internet-Draft, not a final RFC. Likewise, the MCP authorization page references OAuth 2.1 as an IETF draft; it is not correct to call OAuth 2.1 a numbered final RFC in this article.

For the fixture, choose pre-registration so registration variables do not obscure the resource and issuer tests. Register exactly https://client.example/oauth/callback; bind the client record to the accounts authorization server; and keep credentials, if any, in the lab secret store. Then issue a redacted authorization request:

# Illustrative request outline; not executed
GET https://identity.example/as/accounts/authorize?
  response_type=code&
  client_id=<registered-client-id>&
  redirect_uri=https%3A%2F%2Fclient.example%2Foauth%2Fcallback&
  code_challenge=<derived-value>&
  code_challenge_method=S256&
  state=<single-use-value>&
  resource=https%3A%2F%2Faccounts.example%2Fmcp&
  scope=accounts.read

The client-side assertion set is compact:

Control

Expected result

Registration binding

Client record belongs to expected issuer context

Redirect URI

Exact registered callback selected

PKCE

Request record owns verifier used to derive challenge

State

Single-use value is bound to same request record

Resource

Exact selected MCP resource is present

Scope

Minimum operation-relevant scope is requested

RFC 9700 requires exact string matching for registered redirect URIs, except the specific native-app localhost-port case it describes, and recommends restricting token privileges to what the use case needs (IETF / RFC Editor, RFC 9700: Best Current Practice for OAuth 2.0 Security, published January 2025; accessed September 17, 2026).

A registration failure is not a reason to weaken callback checks or invent an undocumented SDK fallback. Record which supported mechanism failed, keep the resource and issuer selection unchanged, and stop. Cleanup removes any temporary registration and its credentials; it does not leave an orphaned client secret in logs or test configuration.

Validate the response issuer before exchanging the code

A callback is the critical point where a client can accidentally confuse one authorization server for another. The pinned MCP revision requires the client to record the expected issuer from validated authorization-server metadata before the user-agent redirect, then apply the RFC 9207 issuer checks before sending an authorization code to any token endpoint. The authorization server iss response parameter and the protected-resource resource identifier are therefore different values with different jobs: iss identifies the authorization server that produced the response; resource identifies the target protected resource.

RFC 9207 defines iss as authorization-server issuer identification and requires simple string comparison when clients validate a present value. The MCP revision adds a concrete four-case matrix that accommodates servers emitting iss before advertising support. Do not replace that matrix with a blanket “missing iss always fails” rule. (IETF / RFC Editor, RFC 9207: OAuth 2.0 Authorization Server Issuer Identification, published March 2022; accessed September 17, 2026.)

Test advertised support and missing issuer values separately

Use the recorded issuer from validated metadata as the expected value. Never set the expected issuer from the callback you are currently testing.

Metadata says authorization_response_iss_parameter_supported

iss in callback

Pinned MCP action

true

Present

Compare with recorded issuer; reject mismatch

true

Absent

Reject response

false or absent

Present

Compare with recorded issuer; reject mismatch

false or absent

Absent

Proceed under the pinned protocol rule

This matrix applies to successful and error callbacks. The MCP authorization specification says that on an issuer mismatch the client must not act on or display error, error_description or error_uri, because it has not established that the error originated from the intended authorization server.

An organization may choose a stronger policy, such as permitting only authorization servers that advertise and return iss, but that is additional policy, not the pinned MCP rule. Record it separately in the trust ledger so an interoperability defect is not confused with a local hardening choice.

Do not normalize away an issuer mismatch

The July 2026 MCP text is unusually explicit here. After form decoding, clients must not case-fold the scheme or host, elide default ports, add/remove a trailing slash, or apply percent-encoding normalization before the issuer comparison; it requires simple string comparison. Resource-URI robustness guidance elsewhere in the same specification does not grant permission to normalize the issuer.

In the controlled fixture, with expected issuer https://identity.example/as/accounts, test:

Callback iss

Expected decision

https://identity.example/as/accounts

Continue

https://identity.example/as/accounts/

Reject

https://IDENTITY.example/as/accounts

Reject

https://identity.example:443/as/accounts

Reject

https://identity.example/as/inventory

Reject

For every rejected row, assert no token-endpoint request occurs. That assertion is more valuable than a UI error string because it proves the authorization code was not disclosed to an endpoint selected under a confused issuer context. After the test, invalidate the synthetic authorization transaction and clear callback state; retain only request ID, expected issuer, received issuer hash or safely redacted value, comparison result and reason code.

Bind resource selection to both token-related requests

Resource indicators predate the July 2026 MCP revision. RFC 8707, published in February 2020, defines the resource request parameter so a client can tell an authorization server which protected resource it intends to access (IETF / RFC Editor, RFC 8707: Resource Indicators for OAuth 2.0, published February 2020; accessed September 17, 2026). The pinned MCP revision makes that boundary concrete for its HTTP authorization profile: clients MUST include resource in both the authorization request and the token request, and it must identify the MCP server the client intends to use the token with.

For the synthetic accounts flow, the token request is therefore:

# Illustrative request outline; not executed
POST https://identity.example/as/accounts/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&
code=<redacted>&
redirect_uri=https%3A%2F%2Fclient.example%2Foauth%2Fcallback&
code_verifier=<secret-from-request-store>&
resource=https%3A%2F%2Faccounts.example%2Fmcp

The key test is not “the parameter exists”; it is “the selected resource did not change between the authorization request, callback transaction and token request.” Inject https://inventory.example/mcp at the token step and expect the client-side transaction guard to stop the request before transmission. Even if an authorization server would reject that change, the client should not rely on remote behavior to catch loss of its own transaction binding.

RFC 8707 also explains why a resource request is not the same thing as the resulting token audience. The authorization server may use the exact resource URI as the audience, or map it to another URI or abstract identifier. It SHOULD audience-restrict issued tokens, but the client’s request does not prove what the server actually issued.

Evidence

Meaning

Not sufficient to prove

resource in authorization request

Intended destination for the grant

Actual token audience

resource in token request

Intended destination for this token

Resource-server admission

Granted scope

Delegated privilege label

Tenant/object ownership

Token audience validation

Token is meant for this server

Permission for every tool/action

A changed-resource failure should close the transaction, never silently substitute a “default” resource, and require a fresh authorization attempt. Cleanup destroys the code verifier and any unredeemed code. The ledger keeps the resource identifiers and the reject decision, not the code itself.

Validate the token for this server on each request

Once a token is issued, the MCP server becomes the enforcement point. The pinned specification says the MCP server must validate access tokens and must validate that they were issued specifically for that server as the intended audience; invalid or expired tokens receive HTTP 401, while insufficient scope or permission is represented separately as HTTP 403 in the authorization error guidance. It also says clients must include authorization on every HTTP request and servers must not accept or transit tokens for other resources.

RFC 9700 reinforces the model: access tokens should be audience-restricted, and each resource server is obliged to verify on every request that the presented token was meant for it; if not, the resource server refuses the request. That is the OAuth security boundary behind MCP resource audience validation, not a JWT-decoding exercise. General API security and observability practices help with operations, but this admission check needs explicit MCP resource evidence.

Do not assume every access token is a JSON Web Token (JWT). OAuth token values can be opaque to clients. If the deployment uses opaque access tokens, validate them through the authorization mechanism the issuer and resource server actually support, for example, an authorized token-introspection endpoint when that mechanism is part of the deployment. RFC 7662 defines introspection as a way for a protected resource to learn whether a token is active and obtain relevant metadata, including audience where supplied (IETF / RFC Editor, RFC 7662: OAuth 2.0 Token Introspection, published October 2015; accessed September 17, 2026).

Use this admission contract on every MCP HTTP request:

Check

Expected evidence

Failure class

Token present in header

Bearer credential received via Authorization

401: missing/invalid authentication

Trusted validation path

Signature/key validation or verified issuer-side validation, as deployed

401

Token active/time-valid

Not expired/revoked; timing rules satisfied

401

Intended resource/audience

Validator proves token is for this MCP server

401

Issuer relationship

Token validation is anchored to an issuer trusted for this resource

401

Scope/permission

Required protocol-level permission is present

403 when authenticated but insufficient

Application policy

Operation/tenant/object allowed

Application 403/deny result

For a JWT deployment, “base64 decode succeeded” is not validation. For an opaque deployment, “the token exists in a cache” is not validation unless that cache is itself part of a documented, secure validation design. The implementation must prove active status, issuer/resource binding and permitted use through the mechanism chosen for that deployment.

Negative tests include an expired token, a token issued for inventory presented to accounts, a token from an untrusted issuer and a valid accounts token lacking the write permission. Record the exact gate that failed. A server that returns 401 for an insufficient application permission or 403 for an invalid token makes incident evidence harder to interpret; preserve the protocol distinctions rather than flattening every denial into “auth failed.”

Keep downstream credentials behind a separate boundary

An MCP server that calls another API crosses a second authorization boundary. The July 2026 MCP security guidance defines token passthrough as an anti-pattern where the server accepts a token from the MCP client without validating that it was issued for the MCP server and then forwards that token to a downstream API. The guidance states that MCP servers must not accept tokens that were not explicitly issued for them and describes audit, confused-deputy and trust-boundary risks when an inbound credential is reused downstream.

Therefore the accounts MCP fixture must never do this:

# Forbidden pattern: illustrative only
Inbound:  Authorization: Bearer <token-for-accounts-mcp>
Outbound: Authorization: Bearer <same-token>

Instead, define a separate downstream credential policy appropriate to the actual deployment. That might be a service credential, delegated downstream token, token-exchange result, workload identity or another mechanism supported by the downstream system. There is no universal MCP requirement that token exchange exists, so do not invent one. The control is the separation: the inbound MCP credential is validated for the MCP server, while the downstream credential is acquired, scoped and audited under a distinct trust relationship. External API integration boundaries provide useful engineering context, but they do not replace this OAuth/MCP credential ownership rule.

Hop

Credential owner

Audience/destination

Audit identity

Client → Accounts MCP

Authorization server for Accounts MCP

Accounts MCP

Delegated caller + MCP client

Accounts MCP → Accounts API

Downstream credential issuer/owner

Accounts API

MCP service identity and, if supported, delegated context

The proposed test asserts three things. First, the outbound Authorization value must not byte-match the inbound bearer token. Second, the downstream credential must be obtained from an approved credential component, not copied from client input. Third, the downstream audit event must be linkable to the originating MCP invocation through a non-secret correlation identifier.

Be explicit about what the downstream API sees. In a service-credential design it may see only the MCP service identity, so tenant/user authorization must remain enforced at the MCP layer and any delegated context must be carried in a documented, integrity-protected way if the API needs it. In a delegated downstream-token design, the API may see a delegated subject. Both are deployment choices; neither justifies forwarding the original MCP token unchanged.

On failure, do not fall back to the inbound bearer token. Fail the tool invocation, record a downstream-credential acquisition or policy reason code, and preserve the upstream admission result separately. Cleanup revokes or expires any lab downstream credentials through their owning system and removes transient correlation state.

Authorize the operation, tenant and object after admission

A correct MCP token gets the caller through the transport authorization gate; it does not automatically authorize every tool or every record reachable behind that tool. This is where application policy begins. The base MCP authorization specification provides transport-level authorization and scope/error behavior, while tenant membership, object ownership, business rules and tool semantics remain deployment concerns unless another application-specific contract defines them. The distinction also matters alongside semantic contracts for agent tools: knowing what a tool means does not establish who may invoke it or which tenant’s object it may read.

Build a small synthetic policy fixture around resource A:

Principal context

Tool

Target

Expected result

Policy reason

user-alpha in tenant-alpha

account.read

acct_alpha_001

Allow

Member + read permission + object in tenant

user-alpha in tenant-alpha

account.update

acct_alpha_001

Deny

Write permission absent

user-alpha in tenant-alpha

account.read

acct_beta_001

Deny

Object belongs to different tenant

user-alpha in tenant-alpha

admin.export_all

any

Deny

Tool not permitted for role

The server should derive the authenticated principal and relevant token context from the verified admission result, not from tool arguments such as user_id or tenant_id supplied by the model. Arguments can identify a requested object, but they are not proof that the caller owns that object or belongs to the tenant.

Test the same identity against a different operation

Use one admitted identity and change only the operation. account.read against acct_alpha_001 should pass; account.update against the same object should fail unless an independent policy grants write. This catches systems that treat “tool is visible” or “scope exists somewhere in the token” as blanket authority.

The expected evidence is not merely HTTP 403. Record the policy decision ID, policy version, authenticated principal key, resource owner, requested operation, target-object classification and reason code such as WRITE_NOT_GRANTED. Avoid storing sensitive object fields in the evidence record.

A useful assertion set is:

  •        The allow decision is reproducible from the same policy version and synthetic inputs.

  •        The write denial happens after token admission but before any downstream mutation.

  •        No retry or “helpful” scope escalation is triggered unless the server intentionally emits an MCP insufficient-scope challenge and the client follows the specified step-up behavior.

Test the correct audience against the wrong tenant

Now keep the token audience correct and change only the object to acct_beta_001. The request must still fail. RFC 8707 notes that multi-tenant deployments may use resource identifiers specific enough to distinguish tenants where appropriate, but audience design does not remove the need for application authorization over tenant-owned objects.

Evidence question

Safe answer to retain

Was the token valid for Accounts MCP?

yes, validator decision ID

Which tenant was authenticated?

Synthetic tenant key tenant-alpha

Which tenant owned the target?

Synthetic classification tenant-beta

Why denied?

TENANT_BOUNDARY_MISMATCH

Was downstream called?

no

This negative case is especially important because a reviewer can otherwise stop at “audience correct” and miss the application boundary. Keep denial evidence free of customer payloads: object identifiers should be synthetic in the lab and production audit records should prefer stable internal IDs or classifications over raw record contents.

Cleanup restores fixture data to its initial state and verifies that forbidden write/cross-tenant cases caused no side effects. The evidence owner for these decisions should be the application/resource owner, not the identity team alone, because the identity layer cannot invent business authorization rules it does not own.

Collect evidence without collecting bearer tokens

A security review needs enough evidence to reconstruct the decision path without turning the audit system into a credential repository. Access tokens are bearer credentials unless sender-constrained by the deployment, and RFC 9700 treats access tokens as sensitive secrets that resource servers must not store or transfer in plaintext. The audit design should therefore preserve decisions and identifiers, not reusable credentials.

A compact redacted trace for one synthetic invocation can look like this:

# Illustrative audit record; no bearer values
request_id=inv_42c1
client_build=client-web-17
selected_resource=https://accounts.example/mcp
expected_issuer=https://identity.example/as/accounts
auth_response_iss=match
resource_auth_request=match
resource_token_request=match
token_validation=valid_for_accounts
principal=user-alpha
tenant=tenant-alpha
tool=account.read
object_class=tenant-alpha/account
authorization=allow
policy_version=accounts-policy-12
downstream_credential_owner=accounts-api-service
downstream_outcome=200

For stronger minimization, hash or tokenize identifiers that do not need to be human-readable, provided investigators can still correlate them under controlled access. Do not hash low-entropy secrets as a substitute for protecting them; simply avoid logging bearer tokens, authorization codes, PKCE verifiers, refresh tokens and client secrets.

Evidence class

Retain

Do not retain in ordinary audit logs

Transaction

Request ID, timestamp, client build

state, code verifier, authorization code

Trust

Selected resource, expected issuer, metadata version/hash

Raw secrets or cookies

Token decision

Validator result, safe token fingerprint if policy allows

Full access/refresh token

App policy

Tool, tenant/object classification, reason, policy version

Sensitive record contents

Downstream

Credential owner/type, destination, outcome, correlation ID

Downstream bearer credential

Retention period, access roles and log destination are organization-specific. The proposed model requires named owners: identity engineering owns issuer/resource trust evidence; MCP server owners own token-admission evidence; application owners own tool/tenant/object policy; security operations owns controlled investigative access. Example retention durations should be set by the organization’s legal, incident-response and privacy requirements, not copied from this article as a vendor requirement.

Failure handling also belongs in the audit contract. A rejected issuer should produce no token-exchange event. A wrong audience should produce no tool-execution event. A cross-tenant denial should produce no downstream call. These absence assertions make the trace useful for proving containment, not merely recording that an error message appeared.

Run the rejection matrix before declaring interoperability

Interoperability is not demonstrated by one green browser flow. The acceptance suite needs positive cases and controlled near-matches that prove each boundary fails closed. Mark every row as proposed until actual execution evidence exists; this article does not claim these synthetic tests were run.

Test

Expected outcome

Classification

Protected-resource metadata resource mismatch

Reject metadata; no redirect

Protocol requirement

Advertised issuer not in trust ledger

Hold/reject

Additional organization policy

iss mismatch

Reject; no token request

Protocol requirement

Advertised iss support, callback missing iss

Reject

Protocol requirement

Unadvertised support, callback missing iss

Proceed unless stricter local policy

Protocol behavior

Resource changed before token request

Reject transaction

Proposed client invariant supporting protocol binding

Valid token, wrong audience

401; no tool execution

Protocol/security requirement

Expired/invalid token

401

Protocol requirement

Valid token, forbidden action

403/deny; no mutation

Application policy

Valid token, wrong tenant object

Deny; no downstream call

Application policy

Inbound bearer copied downstream

Reject test/build

MCP security boundary

When a row fails, classify the cause before fixing it. A server violating a MUST is an implementation defect. A client that rejects an allowed protocol case because local policy requires iss everywhere may be behaving correctly under that policy, but the evidence should say so. A server that correctly rejects cross-tenant access is not “OAuth incompatible”; it is enforcing application authorization.

The release gate is therefore not “all tests returned 200.” It is “every case returned the expected allow or reject decision, and the trace proves that forbidden follow-on actions did not happen.” Keep the matrix versioned with the trust ledger so a later metadata, issuer, client or policy change can be tied to the exact suite that needs rerunning.

Deploy by client-server cohort with a secure rollback

A boundary change is easiest to control when rollout units are explicit client-server cohorts rather than a fleet-wide toggle. The following 30-day schedule is a proposed example, not an MCP requirement or universal acceptance threshold. Its purpose is to sequence evidence gathering, not to prescribe calendar duration to every organization.

Period

Proposed activity

Exit evidence

Days 1–5

Inventory client builds, MCP resource IDs, issuers, registration mode, validators and downstream credentials

Approved trust ledger

Days 6–10

Run isolated discovery, issuer, resource and token fixtures

Positive/negative protocol matrix

Days 11–18

Add tool, tenant, object and downstream-boundary tests

Application-policy and passthrough evidence

Days 19–25

Roll out one compatible client-server cohort

Production-like trace with synthetic accounts

Days 26–30

Rehearse incident isolation and secure rollback

Recovery record and owner sign-off

A cohort is ready only when the client and server agree on the pinned authorization behavior, the required metadata and registration path are supported, issuer checks are active, resource parameters survive both requests, and the server validates the intended audience. Do not solve incompatibility by disabling audience checks, allowing any advertised issuer, suppressing callback validation or forwarding the inbound bearer token downstream.

Rollback deserves the same rigor as rollout. Roll back to a known-secure compatible pair, not merely to “the previous version.” If client build C17 correctly sends resource indicators but server S9 mishandles them, restoring client C16 is safe only if C16 still satisfies the pinned profile and the server validates the intended audience. Otherwise the cohort should be held until a compatible pair is available.

Preserve the trust-ledger delta during rollback. If protected-resource metadata changed the approved issuer, do not restore software while silently keeping stale credentials bound to the old issuer. The pinned MCP client-registration guidance says persisted client credentials obtained through DCR, and pre-registered credentials, are associated with the specific authorization server; when the authorization server changes, credentials from a different issuer must not simply be reused.

Incident rehearsal should cover one boundary failure at a time: metadata unexpectedly points to an unapproved issuer; iss differs by a trailing slash; a resource-B token reaches resource A; a cross-tenant object reference is attempted; or the downstream credential service is unavailable. The expected response is containment, not a weaker fallback.

Use three rollout gates:

  •        Protocol gate: discovery, issuer, resource and token-admission cases match the pinned specification.

  •        Application gate: operation, tenant and object denials are deterministic and side-effect free.

  •        Recovery gate: rollback preserves secure checks, invalidates stale transaction state and leaves a complete redacted evidence chain.

If any gate lacks an accountable evidence owner, the cohort is hold, even when manual login appears to work.

Build the agent-engineering foundations

This playbook assumes practitioners can reason about tool calls, external APIs, observability, deployment and safety controls before specializing in MCP/OAuth boundary validation. The verified Refonte Learning Agentic AI Engineer Program page lists a three-month program at 12–14 hours per week and names LLM tool use and function calling, API integration and external tool use, agent evaluation and observability, deployment, safety guardrails, LangChain, LangGraph and AutoGen.

That is relevant foundation, but the live program text cited here does not establish that this exact July 28, 2026 MCP authorization revision, authorization-response iss validation, a particular authorization server or a dedicated adversarial OAuth lab is included. Treat those specialized capabilities as separate evidence requirements, not implied curriculum promises.

A readiness check for engineers is short:

  •        Can you trace a tool call across client, MCP server and downstream API without logging credentials?

  •        Can you distinguish OAuth resource/audience binding from application scope, tenant and object authorization?

  •        Can you write a negative test that proves a forbidden call never crossed the next boundary?

Those foundations make the identity-specific lab productive; they do not replace the source-level MCP and OAuth work required here.

Approve only the resource boundary you verified

Closing question

Decision

Does login prove tool access?

No. Login is upstream of resource, token and application authorization.

Can inbound tokens be forwarded?

No. Do not use the unchanged inbound MCP bearer token as a downstream credential.

What makes a cohort ready?

Matching discovery, issuer, resource, token, application-policy and rollback evidence with named owners.

Decision: allow only the specific client-resource-issuer-policy cohort whose evidence ledger passes the positive and rejection matrix. Otherwise hold. The identity owner signs issuer/resource trust; the MCP owner signs token admission; the application owner signs tool, tenant and object policy; and the downstream API owner signs credential separation.