Enterprise AI 10 min read

The Enterprise MCP Identity Crisis: Solving OAuth2 for AI Tools

The Enterprise MCP Identity Crisis: Solving OAuth2 for AI Tools
Deploying Model Context Protocol (MCP) in the enterprise hits a wall with standard OAuth2. Learn why Identity is the bottleneck and how an OAuth2 Proxy unlocks Microsoft Copilot, GitHub Copilot, Copilot CLI, Claude Code, and other AI tools.

The Model Context Protocol (MCP) is rapidly becoming the standard for connecting AI models to local data and tools. However, when you attempt to deploy MCP servers in a secure enterprise environment, you hit an unexpected wall: Identity and Access Management (IAM).

Most developers are familiar with “Simple OAuth” (like logging into a personal SaaS app), but rely on a fuzzier understanding of enterprise OAuth2 architecture. Without understanding the strict security boundaries of tenants like Microsoft Entra ID (formerly Azure AD), it is impossible to deploy production-ready MCP servers.

In this article, we’ll dissect why standard enterprise auth breaks the new wave of AI tools and how an OAuth2 Proxy architecture bridges the gap.

The Foundation: Enterprise OAuth2 Architecture

Before we look at the specific problems MCP introduces, let’s establish how a standard internal application is protected in a tenant like Entra ID.

The Example: HR Portal & HR API

Imagine you have an HR Web Portal (a frontend SPA) and an HR Application API (the backend). For these to work together securely, security best practices dictate registering two separate applications in your identity provider.

1. The API Registration (HR App)

The backend API represents the “Resource” being protected.

  • App ID URI: A unique identifier (e.g., api://hr-app-backend).
  • Scopes: It defines permissions like access_as_user that clients can request.
  • Context: It runs as a service and validates tokens, but never initiates the login flow itself.

2. The Client Registration (Web Portal)

The frontend portal is the “OAuth2 Client.”

  • Redirect URI: This is the critical security control—an allow-list of URLs where the identity provider is permitted to send tokens (e.g., https://portal.company.com/callback).
  • PKCE: Because it’s a browser-side app, it uses Proof Key for Code Exchange instead of a static client secret.
  • Permissions: It is explicitly granted permission to call the HR API on behalf of the user.

The Two-Step Authentication Flow

Complexity arises because the portal actually performs two distinct handshakes:

  1. User Authentication (OIDC): The user proves who they are (MFA/Password). Entra ID sends an ID Token to the portal’s Redirect URI.
  2. Resource Authorization (OAuth2): The portal requests an Access Token specifically for the HR API. Entra ID checks if the portal is allowed to talk to the API, and if so, issues a token scoped to the API’s audience.
Code
  +--------------+        +-------------------+       +----------------+       +-------------------+
  |    User      |        |      Portal       |       |      IDP       |       |       API         |
  |  (Employee)  |        |  (HR Web SPA)     |       |   (Entra ID)   |       |   (HR Backend)    |
  +------+-------+        +---------+---------+       +--------+-------+       +---------+---------+
         |                          |                          |                         |
         |    Opens Application     |                          |                         |
         | -----------------------> |                          |                         |
         |                          |                          |                         |
         |                          | 1. Redirect to Login     |                         |
         |                          |    (PKCE)                |                         |
         |                          | -----------------------> |                         |
         |                          |                          |                         |
         |                          | 2. Auth Code             |                         |
         |                          |    (Sent to Redirect URI)|                         |
         |                          | <----------------------- |                         |
         |                          |                          |                         |
         |                          | Exchange Code for        |                         |
         |                          | Portal Token             |                         |
         |                          | -----------------------> |                         |
         |                          |                          |                         |
         |                          | Access Token             |                         |
         |                          | (Audience: HR API)       |                         |
         |                          | <----------------------- |                         |
         |                          |                          |                         |
         |                          | 3. Fetch Data with       |                         |
         |                          |    HR API Token          |                         |
         |                          | -------------------------------------------------> |
         |                          |                          |                         |
         |                          |                          |                  +------+------+
         |                          |                          |                  | Validate    |
         |                          |                          |                  | Token &     |
         |                          |                          |                  | Scopes      |
         |                          |                          |                  +------+------+
         |                          |                          |                         |
         |                          |    Return User Data      |                         |
         |                          | <------------------------------------------------- |
         v                          v                          v                         v
💡

Crucial Concept: In this strict model, the Redirect URI is the anchor. If a client attempts to start a login flow but asks for the token to be sent to a URL not in the allow-list, Entra ID blocks the request immediately.

The Manual Bottleneck

In a traditional setup, IT administrators manually configure this:

  1. Create App Registrations: Setup identity objects.
  2. Hardcode Redirect URIs: Whitelist every specific callback URL.
  3. Manage Secrets: Rotate credentials periodically.

This works for one portal. It fails for the MCP ecosystem.

The MCP Problem: The “Thousand Clients” Reality

MCP changes the consumption model. You are no longer building The Client (like the HR Portal). You are building the Server, and you have no control over the clients connecting to you.

Today, a single MCP server might be accessed by:

ProductConnector SupportThe Challenge
GitHub Copilot CLINativeRuns on localhost with ephemeral ports or specific CLI-based callbacks.
Microsoft 365 CopilotVia StudioRequires a public-facing domain validation and specific Microsoft callbacks.
Cursor & VS CodeNativeoften use local servers or internal loopback addresses (127.0.0.1) which IT admins hate whitelisting.
Claude DesktopNativeExpects a standard OAuth flow but might use a custom protocol handler scheme (e.g., claude://).

Each client has a “shape” you don’t control. To make matters worse, they all want to perform Dynamic Client Registration (DCR). They want to say: “Hi, I’m Cursor on John’s Laptop, please give me a Client ID and let me log in.”

The Block: Enterprise Identity Providers (Entra ID, Okta, Google) generally do NOT support Dynamic Client Registration for internal apps. They require strict, pre-registered static clients.

You cannot ask your IT admin to manually register a new App ID for every developer’s laptop or every new AI tool that appears on the market.

The Solution: The OAuth2 Proxy

To solve this, we must invert the model. Instead of the MCP Client talking directly to Entra ID, we introduce an OAuth2 Proxy that lives inside your MCP deployment.

”Polyfilling” Dynamic Registration

The Proxy acts as a middleman that simulates a DCR-compliant identity provider to the MCP clients, while behaving like a standard, static application to Entra ID.

Instead of diverse clients (VS Code, Cursor) trying to satisfy Entra ID’s strict rules, they talk to the Proxy’s flexible rules.

Code
                          FULL MCP OAUTH2 EXECUTION FLOW
                          ==============================

[ CLIENT ]                  [  OAUTH2 PROXY  ]         [ IDENTITY PROVIDER ]    [ PROXY CONFIG STORE ]
(VS Code/Cursor)            (The "Shim" Layer)            (Entra ID/Google)      (Internal Database)
    |                             |                             |                      |
    |  1. DISCOVERY (DCR)         |                             |                      |
    |---------------------------->|                             |                      |
    |  POST /register             |                             |                      |
    |  { "client_name": "Cursor", |                             |                      |
    |    "redirect_uris": [...] } |                             |                      |
    |                             |  2. LOAD OAUTH CONFIG       |                      |
    |                             |--------------------------------------------------->|
    |                             |  Fetch Client ID/Secret     |                      |
    |                             |<---------------------------------------------------|
    |                             |                             |                      |
    |  <--------------------------|                             |                      |
    |  201 Created                |                             |                      |
    |  { "client_id": "cursor_1", |                             |                      |
    |    "auth_uri": "..." }      |                             |                      |
    |                             |                             |                      |
    |  3. AUTHORIZATION           |                             |                      |
    |---------------------------->|                             |                      |
    |  GET /cursor_1/authorize    |  4. REDIRECT TO IDP         |                      |
    |                             |---------------------------->|                      |
    |                             |                             |                      |
    |                             |  5. USER AUTHENTICATES      |                      |
    |                             |<--------------------------->|                      |
    |                             |                             |                      |
    |                             |  6. IDP CALLBACK            |                      |
    |                             |<----------------------------|                      |
    |                             |  ?code=...                  |                      |
    |                             |                             |                      |
    |                             |  7. TOKEN EXCHANGE          |                      |
    |                             |---------------------------->|                      |
    |                             |  <--------------------------|                      |
    |                             |  Access Token (Entra)       |                      |
    |                             |                             |                      |
    |  <--------------------------|                             |                      |
    |  Redirect with Proxy Code   |                             |                      |
    |                             |                             |                      |
    |  8. GET PROXY TOKEN         |                             |                      |
    |---------------------------->|                             |                      |
    |  POST /cursor_1/token       |                             |                      |
    |  <--------------------------|                             |                      |
    |  Bearer: eyJ... (Local JWT) |                             |                      |
    |                             |                             |                      |
    |  9. TOOL EXECUTION          |                             |                      |
    |---------------------------->|                             |                      |
    |  POST /cursor_1/mcp         |  10. INJECT ENTRA TOKEN     |                      |
    |  (with Local JWT)           |                             |                      |
    |                             |  11. EXECUTE TOOL           |                      |
    |                             |--------------------------------------------------->|
    |                             |  Run actual Python logic    |                      |
    |                             |<---------------------------------------------------|
    |                             |                             |                      |
    |  <--------------------------|                             |                      |
    |  Returns Result             |                             |                      |
    |                             |                             |                      |

The Magic: Decoupling Clients from Providers

  1. Client Believes it’s Standard: The MCP Client (Cursor) sees a standard OAuth2 server. It registers itself, gets a client_id, and performs a code exchange.
  2. Proxy Handles the Reality: Behind the scenes, the Proxy maps that client_id to a specific Entra ID Enterprise App. It performs the real handshake with Microsoft, using the pre-registered Redirect URI that the admin whitelisted.
  3. Token Injection: When the tool actually runs, the Proxy “user” is authenticated. The Proxy injects the raw Entra ID token into the tool’s context so the Python code can call the Microsoft Graph API or other secure backends.

Why this is better

  • Zero Admin Friction: You register ONE generic “MCP Gateway” app in Entra ID. You whitelist the Proxy’s URL. That’s it.
  • Support Any Client: Whether it’s Copilot CLI running on a random port or a secure M365 Agent, they all talk to the Proxy. The Proxy handles the strict Entra requirements.
  • Multi-Tenant Ready: A single Proxy deployment can serve different customers. One client might map to “Tenant A HR App” and another to “Tenant B Marketing”.

Architecture Deep Dive

For the architects, here is how we structure this internally.

1. Multi-Tenant Isolation

We treat the local client_id (generated by the proxy) as a Routing Key.

When a request comes in for /mcp/cursor_session_1, the Proxy:

  1. Looks up the session in its internal database.
  2. Decrypts the associated Entra ID tokens (stored at rest using Fernet encryption).
  3. Hydrates a sandboxed Python instance for that specific user context.

This ensures that if you are serving multiple organizations, their tokens and data never cross paths in memory.

2. Scope Injection

Enterprises often require specific scopes like openid, profile, and offline_access. Clients often forget to ask for them. The Proxy forces these scopes on the upstream request, ensuring your tools always have the tokens they need to function long-term (via Refresh Tokens).

3. Error Normalization

Entra ID errors can be cryptic (AADSTS50011). The Proxy catches these and returns standard RFC 6749 error responses to the MCP client, preventing the client from crashing or hanging on unknown error formats.

The “Copilot Ready” Advantage

🚀

This proxy approach is the hidden key to unlocking the Microsoft Copilot Ecosystem.

By making your MCP server behave like a standard, compliant OAuth entity, you can wrap it as an M365 Agent. When a user in Microsoft Teams asks a question contextually linked to your data, the auth flow is handled by your proxy.

This means you can deploy one server that serves:

  1. Your developers in VS Code (using local DCR).
  2. Your business users in Teams (using M365 Auth).
  3. Your CI/CD pipelines (using Copilot CLI).

Conclusion

Setting up MCP in an enterprise isn’t just about writing tool definitions; it’s about navigating the rigid security structures of modern identity providers. By using an OAuth2 Proxy architecture, you stop fighting the “Redirect URI” battle and start deploying AI agents at scale.

The result is a system that satisfies the CISO’s need for control and the Developer’s need for speed.


Related Articles

More articles coming soon...

Discussion

Loading...