MCP Client Registration Against Entra ID: The OAuth Proxy Pattern
An OAuth proxy can connect MCP clients to an Entra-backed service while keeping downstream registration separate from upstream application provisioning. This conceptual walkthrough explains the two authorization transactions, per-client consent, token validation and refresh limits that make the proxy a security-critical authorization server.
Connecting an application to your organisation’s identity provider usually starts with a known client. You register the application, configure its redirect URIs and permissions, and give users a way to sign in. With one application and one provider, it is fairly clear which relationship you are setting up.
An MCP service can make that arrangement less straightforward. The service may be used from several independently developed clients, and you may not want to provision a separate upstream application for every client you support. The user still signs in through the organisation’s identity provider, but the client connecting to your service is no longer necessarily an application you own.
Suppose Naledi wants a desktop agent to read her team’s notes through an MCP server. We will call the fictional client Notes Desktop and the resource https://notes.example/mcp. Her organisation uses Microsoft Entra ID. The integration needs to accept supported MCP clients while keeping the Entra application relationship under the organisation’s control.
One way to do that is to put an OAuth proxy at https://auth.notes.example. Notes Desktop becomes a client of the proxy, and the proxy becomes a confidential client of Entra. Registration on the client-facing side can then be handled separately from application provisioning upstream.
That separation is the appeal of the pattern. It also makes the proxy responsible for deciding which downstream client Naledi approved and preserving that decision through the Entra flow. The diagram shows how those two authorization transactions fit together:
sequenceDiagram
participant U as Naledi and browser
participant C as Notes Desktop
participant M as Notes MCP server
participant P as OAuth proxy
participant E as Entra ID
C->>M: Request protected notes
M-->>C: 401 and protected-resource metadata location
C->>P: Discover AS and select client registration method
C->>U: Open downstream authorization request
U->>P: Client, redirect, resource, scopes, state, PKCE challenge
P->>U: Establish user session and request per-client consent
U->>P: Explicitly approve Notes Desktop for notes.read
P->>P: Create browser-bound upstream state and separate PKCE
P->>U: Redirect to Entra with proxy client identity
U->>E: Upstream authorization request
E-->>U: Return upstream code and state
U->>P: Proxy callback
P->>E: Redeem code with client authentication and upstream verifier
E-->>P: Upstream tokens as permitted
P-->>U: Registered downstream redirect, new code, original state
U->>C: Deliver downstream authorization response
C->>P: Redeem one-use code with downstream verifier
P-->>C: Proxy-issued token for Notes MCP
C->>M: Call with proxy token
M->>M: Validate token and authorise access to the note
This is a conceptual overview, not a production implementation. The consent step is deliberately before the upstream authorization request. Removing it changes the security of everything that follows.
Registration Is the First Decision, Not the Whole Design#
Before adding this component, follow the MCP draft’s registration order: use existing pre-registration first, then Client ID Metadata Documents (CIMD) when supported, then Dynamic Client Registration (DCR) as a deprecated compatibility fallback, and finally manual registration with user-entered client details. The deprecation belongs to this MCP draft. It does not retire DCR across OAuth.
For a known set of clients, pre-registration may be sufficient. Nor does pre-registration necessarily mean clicking through a portal: Microsoft Graph can create Entra applications. That is privileged, provider-specific provisioning. It is different from an interoperable registration endpoint that arbitrary clients can use without administrative access. Decide which problem you have before adding another token issuer.
In our example, the MCP server’s protected-resource metadata identifies the proxy as its authorization server. Notes Desktop discovers the proxy’s capabilities and selects a supported registration mechanism. A downstream DCR registration does not create an Entra application. A CIMD client identifier can instead be a hosted HTTPS metadata URL. Neither is the Entra application identifier used by the proxy upstream.
Client IDs are identifiers, not secrets. The upstream confidential client’s authentication material needs protection; hiding its client ID is not that protection.
The companion article, what MCP’s draft deprecation of DCR changes, explains this choice in more detail. Briefly, DCR can be protected and governed; CIMD makes metadata portable and origin-associated, but does not authenticate the running public client or verify its branding. Neither choice removes the need for consent.
Entra Consent Does Not Approve Every Downstream Client#
Imagine Naledi has already authorised the proxy’s Entra application. Later, a different desktop client starts a new request through the same proxy. Entra may recognise the existing session and shared application grant. That does not mean Naledi approved this new client to read her notes.
This is the confused-deputy problem documented by MCP: a downstream client can exploit the proxy’s upstream relationship if the proxy treats shared provider consent as permission for everyone behind it.
Consent to the proxy is not consent to every client that can reach it.
The proxy must obtain explicit consent from the user for each downstream client before forwarding its authorization request upstream. Naledi needs to see which client is asking, which resource it wants and which permissions she is granting. A stored approval must belong to that user-client relationship and cover the requested access; a cookie saying she previously approved the shared Entra app is insufficient.
This requires an authenticated proxy session. When none exists, establish the user’s identity through a separate sign-in flow before approving the pending downstream request. Do not silently forward that request upstream as a shortcut to obtaining consent. Bind the later upstream result to the same user and permitted tenant, rather than accepting an account switch into someone else’s pending grant.
Registration metadata helps describe the request, but a client name remains untrusted display information. Even a CIMD document hosted at a legitimate origin does not prove that the program initiating this browser flow is the publisher’s software. Consent is necessary; it is not application attestation.
The Callback Must Rejoin the Right Transaction#
After Naledi approves Notes Desktop’s notes.read request, the proxy can create the upstream transaction. Its random, short-lived state must be bound to her browser session and the approved pending request, created only after consent, and consumed once. The original downstream state is separate: preserve it and return it unchanged to Notes Desktop in the downstream response.
There are also two independent PKCE pairs. Notes Desktop retains its verifier while the proxy records its challenge. The proxy generates a different verifier and challenge for its Entra request. RFC 9700 section 2.1.1 mandates PKCE for public clients and recommends it for confidential clients. Using it independently on both hops is advisable here; claiming the published standard universally mandates it for all clients would overstate that requirement.
On the upstream callback, the proxy validates and consumes its own state before redeeming the Entra code with its confidential-client authentication and upstream verifier. It does not send that code to Notes Desktop. It creates a separate, short-lived, one-use downstream code.
That new code belongs to the authenticated user, downstream client, validated registered redirect URI, target resource, approved scopes and downstream PKCE challenge. At redemption, the proxy checks those bindings and consumes the code atomically. Otherwise a correct verifier can still be attached to the wrong grant. The authorization-code protections in RFC 9700 are relevant at both boundaries.
Notes Desktop includes the resource parameter in both its authorization and token requests, as required by the MCP draft. The proxy validates it against the approved grant and issues a token for that resource, rather than trusting any audience the client supplies at redemption.
The browser response must use the previously validated redirect destination and a structured URL builder that preserves the protocol response correctly, including downstream state. Concatenating a callback string with ?code= is not a redirect-validation strategy. PKCE protects code redemption, but it does not prove that a localhost listener is the genuine Notes Desktop application.
The MCP Server Still Has an Authorization Job#
The result Notes Desktop receives is a proxy-issued access token intended for the Notes MCP resource. It is not an Entra bearer token passed through, or an Entra token hidden inside another token. Any upstream tokens retained by the proxy stay within that upstream relationship.
At the MCP server, validation includes the trusted issuer, signature verification or authenticated introspection as appropriate, intended audience, expiry and required scopes. Access tokens need not be JWTs. A claim called sub is not a universal user-mapping contract either; establish the user and tenant from the validated token contract and your application’s identity mapping.
Then check the requested object. If Notes Desktop asks for a note ID from another team, a valid notes.read token does not automatically authorise it. The service must enforce tenant boundaries, ownership and sharing rules for that note. Client-supplied user IDs cannot override the authenticated identity. Registration gets the client into the flow; it does not decide which records Naledi may read.
Refresh Extends a Grant, Not a Promise#
A long-running agent benefits from refresh tokens, but it cannot assume uninterrupted access. Entra tokens can expire or be revoked, and policy can require interaction. For the Entra v2 authorization-code flow, request offline_access when a refresh token is needed and satisfy the applicable consent requirements. Its issuance is not permission to run forever.
When the proxy needs to refresh upstream, it authenticates as its confidential Entra client. Microsoft’s refresh-token guidance describes replacement tokens: securely persist the replacement, handle concurrent refresh attempts safely, and delete the old stored token once the replacement is secured. Replacement does not itself mean Microsoft immediately revoked the previous token.
Downstream refresh is a separate lifecycle. Bind it to the approved user, client, resource and scopes, without silently widening access. For public clients, RFC 9700 section 4.14 requires sender-constrained refresh tokens or rotation; a rotating design needs reuse detection and grant-bound token families. Revocation and upstream reauthentication requirements must affect downstream renewal according to an explicit policy, not leave an independent forever-session.
When silent renewal fails, stop and provide a path back through interactive authorization. An agent that can recover from reauthentication is more useful than one that keeps retrying a revoked credential.
Choose the Component You Are Prepared to Operate#
The useful separation is simple: downstream clients register and obtain narrowly scoped grants at the proxy; the proxy uses its separately provisioned Entra identity upstream; the notes service enforces access to actual notes. Consent, transaction binding and token lifecycle management connect those parts.
The implementation is not simple. Choose an established, maintained authorization-server or broker implementation and verify that it supports these MCP-specific consent and registration requirements. A generic forwarding script is not a substitute. You are taking custody of credentials and operating an authorization server, not adding a convenient redirect handler.
Use the proxy when that responsibility solves a real interoperability problem. Keep direct integration when it already meets your needs. The extra boundary earns its place only when you can enforce it.
Further Reading#
- MCP draft: Client Registration.
- MCP security best practices: Confused Deputy Problem.
- RFC 9700: OAuth 2.0 Security Best Current Practice.
- Microsoft: Authorization-code flow and refresh tokens.
- Microsoft Graph: Create application.
Sources checked on 5 September 2026. Draft pending author review. MCP draft URLs are mutable; the registration discussion refers to the reviewed draft, not every published MCP version. This is a conceptual walkthrough, not production-ready code or a security certification.