DEV Community

Cover image for Zero-Touch OAuth: How MCP Enterprise-Managed Authorization Is Solving the AI Agent Auth Crisis
Manoranjan Rajguru
Manoranjan Rajguru

Posted on

Zero-Touch OAuth: How MCP Enterprise-Managed Authorization Is Solving the AI Agent Auth Crisis

Zero-Touch OAuth: How MCP Enterprise-Managed Authorization Is Solving the AI Agent Auth Crisis

MCP Enterprise Auth Hero Banner


Table of Contents

  1. The Auth Tax on AI Agents
  2. Why Standard MCP Auth Breaks at Enterprise Scale
  3. Enter Enterprise-Managed Authorization (EMA)
  4. Deep Dive: The ID-JAG Token Exchange
  5. Implementing EMA as an MCP Client Developer
  6. Implementing EMA as an MCP Server Developer
  7. VS Code 1.123: EMA in Your IDE Right Now
  8. The Ecosystem as of June 2026
  9. How MCP Enterprise-Managed Authorization Changes Your Security Posture
  10. Conclusion: The New Baseline for Enterprise AI

The Auth Tax on AI Agents

Imagine you're an engineering manager at a 500-person company. You've decided to roll out AI-powered workflows using Claude or GitHub Copilot. Your developers will use MCP-connected tools — Figma for design context, Linear for issue tracking, Confluence for documentation, Supabase for database queries.

Do the math: 500 employees × 8 MCP servers = 4,000 individual OAuth consent flows that need to happen before a single agent can do useful work. That's before you account for new hires, role changes, offboarding, or token expiry. Every new MCP server your security team approves cascades into hundreds of manual re-authorizations. There's no audit trail. There's no central policy. Someone in finance accidentally links their personal Notion account to the corporate Linear MCP server. Your CISO is not pleased.

This is the auth tax — and until June 18, 2026, every enterprise deploying MCP paid it in full.

That changed when the Model Context Protocol published the stable Enterprise-Managed Authorization (EMA) extension, backed by Anthropic, Microsoft, and Okta. It's the first truly enterprise-grade solution to the MCP authentication problem — and if you're building or deploying AI agents in a corporate environment, you need to understand it deeply.


Why Standard MCP Auth Breaks at Enterprise Scale

Before we look at the solution, let's be precise about the failure modes of the current MCP authorization model.

The Per-User, Per-Server Consent Model

Standard MCP authorization is designed around user-scoped, interactive OAuth flows. When a developer connects their MCP client (say, Claude Desktop or VS Code with Copilot) to an MCP server (say, the Linear MCP), the following happens:

  1. The client performs Dynamic Client Registration against the MCP server's Authorization Server
  2. The user is redirected to a consent screen
  3. The user grants permission
  4. The client receives an access token scoped to that user + server pair

This works fine for individual developers experimenting with MCP on their personal laptops. It completely falls apart the moment you try to operationalize it across an org.

Four Enterprise-Scale Failure Modes

1. Onboarding scales linearly with server count. A new employee needs to manually authorize every MCP server your organization uses. In a mature deployment with a dozen MCP servers, this is a day-one obstacle that blocks AI productivity from the start.

2. Security teams can't enforce consistent policy. Since each user authorizes independently, there's no central record of who has access to what MCP server. Security audits require interrogating each individual MCP server's access logs separately. There's no equivalent of "list all users who can call the Supabase MCP server" — because that state doesn't exist in one place.

3. Personal and enterprise accounts blur together. Without corporate identity enforcement at the MCP layer, a user can connect a personal GitHub or Figma account to a work MCP tool. Files get saved to the wrong places. Proprietary data leaks into personal account history. Compliance frameworks like SOC 2 and ISO 27001 treat this as a material risk.

4. Offboarding is a manual, error-prone process. When an employee leaves, revoking their MCP access means logging into every MCP server's admin panel and removing their access individually. Tokens don't expire until their built-in TTL runs out — which, given that most implementations set generous TTLs for UX reasons, can mean ex-employees retain de facto access for hours or days.

These aren't edge cases. They are the default behavior of standard MCP auth in any organization with more than a handful of users.


Enter Enterprise-Managed Authorization (EMA)

The Enterprise-Managed Authorization extension (io.modelcontextprotocol/enterprise-managed-authorization) is a stable, open extension to the MCP specification that introduces the organization's Identity Provider (IdP) as the authoritative decision-maker for all MCP server access.

The core insight is elegant: enterprises already have a system of record for identity — Okta, Azure Entra ID, Auth0, Google Workspace. Every employee has a corporate identity. Every SaaS tool the company uses is already registered in that IdP. MCP servers should be no different.

EMA Architecture: IdP, MCP Client, and MCP Server token exchange flow

The Zero-Touch Promise

From an end-user perspective, EMA delivers what its name implies: zero-touch connector setup. The experience is:

  1. Developer opens VS Code, Claude, or any EMA-capable MCP client
  2. They log in with their corporate SSO credentials (the same thing they do every morning)
  3. All MCP servers that their admin has provisioned for their role are automatically connected — no consent screens, no redirect flows, no copy-pasting tokens

The connectors are there on first login. For the end user, it's invisible. For the security team, it's an enormous leap forward.

The Three Actors

EMA introduces a clear three-actor model that maps directly onto enterprise IAM patterns developers already understand:

Actor Role Example
Enterprise IdP Central authority — issues identity assertions, enforces access policy Okta, Azure Entra, Auth0
MCP Client Requests tokens on behalf of the authenticated user VS Code, Claude, Claude Code
MCP Server AS Validates identity assertions, issues scoped access tokens Figma MCP, Linear MCP, Supabase MCP

The IdP sits between the client and every MCP server. It's the trust anchor — and it's one your organization already trusts for everything else.


Deep Dive: The ID-JAG Token Exchange

The cryptographic heart of EMA is a new token type: the Identity Assertion JWT Authorization Grant, or ID-JAG (IETF draft draft-ietf-oauth-identity-assertion-authz-grant-04, authored by Okta's Aaron Parecki and colleagues).

ID-JAG is a JWT-based authorization grant that allows a client to exchange a user's existing identity assertion (an OpenID Connect ID token or SAML assertion obtained during SSO login) for a scoped access token at a third-party resource server — without any user interaction. The spec draws on two existing RFCs: Token Exchange (RFC 8693) and JWT Profile for OAuth 2.0 Authorization Grants (RFC 7523).

The Four-Step Flow

Here's the complete EMA authorization flow in detail:

┌─────────────┐          ┌───────────────────┐       ┌──────────────────────┐
│  MCP Client │          │  Enterprise IdP   │       │  MCP Server Auth AS  │
│ (VS Code)   │          │  (Okta / Entra)   │       │  (Figma / Linear)    │
└──────┬──────┘          └────────┬──────────┘       └──────────┬───────────┘
       │                          │                              │
       │  1. SSO Login (OIDC)     │                              │
       │─────────────────────────>│                              │
       │                          │                              │
       │  2. ID Token (JWT)       │                              │
       │<─────────────────────────│                              │
       │                          │                              │
       │  3. Token Exchange       │                              │
       │  (ID Token → ID-JAG)     │                              │
       │─────────────────────────>│                              │
       │                          │                              │
       │  4. ID-JAG JWT           │                              │
       │<─────────────────────────│                              │
       │                          │                              │
       │  5. Access Token Request (ID-JAG)                       │
       │────────────────────────────────────────────────────────>│
       │                          │                              │
       │  6. Scoped Access Token  │                              │
       │<────────────────────────────────────────────────────────│
       │                          │                              │
       │  7. MCP API Call with Bearer Token                      │
       │────────────────────────────────────────────────────────>│
Enter fullscreen mode Exit fullscreen mode

Step 1–2: SSO Login. The user authenticates with the enterprise IdP using standard OIDC. This is their normal morning login — nothing new for the user.

Step 3–4: Token Exchange (ID Token → ID-JAG). The MCP client sends the user's ID token to the IdP's token exchange endpoint, requesting an ID-JAG. The IdP evaluates whether the user (based on group membership, roles, and conditional access rules) is authorized to access the target MCP server. If so, it issues a short-lived ID-JAG JWT bound to that specific MCP server's audience.

Step 5–6: Access Token Request. The client presents the ID-JAG to the MCP server's Authorization Server. The AS validates the JWT — checking the signature against the IdP's JWKS endpoint, the audience claim, the issuer, and the expiration. If valid, it issues a scoped access token.

Step 7: MCP API Call. The client uses the access token as a standard Bearer token. From this point, the MCP server interaction is identical to any OAuth 2.0-protected API.

ID-JAG Claims

A well-formed ID-JAG JWT payload looks like this:

{
  "iss": "https://your-org.okta.com",
  "sub": "user-id-abc123",
  "aud": "https://mcp.linear.app/as",
  "iat": 1750300000,
  "exp": 1750300300,
  "scope": "issues:read issues:write projects:read",
  "resource": "https://mcp.linear.app",
  "act": {
    "sub": "mcp-client-id"
  },
  "email": "developer@yourorg.com",
  "groups": ["engineering", "platform-team"]
}
Enter fullscreen mode Exit fullscreen mode

Key claims to understand:

  • aud: The authorization server of the target MCP server — not the MCP server itself. Critically important for preventing token misuse across different servers.
  • resource: The resource server URI. Binds the token to a specific MCP server instance.
  • scope: The permissions the IdP is asserting for this user at this server, derived from the admin-configured policy.
  • act: The "actor" claim — identifies the MCP client performing the exchange, enabling client-level audit trails.
  • groups: Optional, but powerful — allows the MCP server to apply fine-grained, resource-level authorization based on org group membership.

Validating an ID-JAG in Python

Here's a complete Python snippet for validating an inbound ID-JAG JWT in your MCP server's auth middleware:

import jwt
import httpx
from jwt import PyJWKClient
from typing import Optional
from dataclasses import dataclass

EXPECTED_ISSUER = "https://your-org.okta.com"
EXPECTED_AUDIENCE = "https://mcp.yourserver.com/as"
JWKS_URI = f"{EXPECTED_ISSUER}/oauth2/v1/keys"

@dataclass
class IDJAGClaims:
    subject: str
    email: Optional[str]
    groups: list[str]
    scope: str
    resource: str
    actor_sub: Optional[str]

def validate_id_jag(token: str) -> IDJAGClaims:
    """
    Validate an inbound ID-JAG JWT from the enterprise IdP.
    Raises jwt.InvalidTokenError on any validation failure.
    """
    # Fetch the IdP's public keys from the JWKS endpoint
    # PyJWKClient caches keys automatically, so this is efficient at scale
    jwks_client = PyJWKClient(JWKS_URI)
    signing_key = jwks_client.get_signing_key_from_jwt(token)

    # Decode and validate all standard claims
    payload = jwt.decode(
        token,
        signing_key.key,
        algorithms=["RS256", "ES256"],
        audience=EXPECTED_AUDIENCE,
        issuer=EXPECTED_ISSUER,
        options={
            "require": ["exp", "iat", "sub", "aud", "iss", "resource", "scope"],
            "verify_exp": True,
            "verify_iat": True,
        }
    )

    # Additionally validate the resource claim matches this specific server
    if payload.get("resource") != "https://mcp.yourserver.com":
        raise jwt.InvalidClaimError("resource claim does not match this server")

    return IDJAGClaims(
        subject=payload["sub"],
        email=payload.get("email"),
        groups=payload.get("groups", []),
        scope=payload.get("scope", ""),
        resource=payload["resource"],
        actor_sub=payload.get("act", {}).get("sub"),
    )

def exchange_id_jag_for_access_token(id_jag: str) -> dict:
    """
    Exchange a validated ID-JAG for a scoped access token.
    This logic runs inside your MCP server's Authorization Server endpoint.
    """
    claims = validate_id_jag(id_jag)

    # Map IdP groups to internal permission scopes
    permissions = []
    if "engineering" in claims.groups:
        permissions.extend(["issues:read", "issues:write"])
    if "platform-team" in claims.groups:
        permissions.append("projects:read")

    # Issue a short-lived access token — safe to do because EMA handles
    # transparent re-issuance. 5 minutes is a reasonable enterprise default.
    return {
        "access_token": generate_access_token(claims.subject, permissions),
        "token_type": "Bearer",
        "expires_in": 300,  # 5-minute TTL — safe with EMA's silent re-exchange
        "scope": " ".join(permissions),
    }
Enter fullscreen mode Exit fullscreen mode

Implementing EMA as an MCP Client Developer

If you're building an MCP client (an IDE extension, an AI coding tool, an agent framework), here's what EMA support requires from you.

Step 1: Declare EMA Capability

During the MCP initialize handshake, your client must announce EMA support:

# MCP initialize request payload — declare EMA capability
initialize_request = {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {
        "protocolVersion": "2025-03-26",
        "capabilities": {
            "extensions": {
                # This extension identifier tells the server your client
                # supports the enterprise-managed authorization flow
                "io.modelcontextprotocol/enterprise-managed-authorization": {}
            },
            "roots": {"listChanged": True},
            "sampling": {}
        },
        "clientInfo": {
            "name": "my-mcp-client",
            "version": "1.0.0"
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

When the MCP server sees this capability, it knows it can require EMA instead of falling back to interactive per-server OAuth.

Step 2: Detect EMA Requirement and Perform the Token Exchange

import httpx
from urllib.parse import urlparse

async def get_server_auth_metadata(server_url: str) -> dict:
    """Fetch the MCP server's OAuth authorization server metadata."""
    base = f"{urlparse(server_url).scheme}://{urlparse(server_url).netloc}"
    async with httpx.AsyncClient() as client:
        resp = await client.get(
            f"{base}/.well-known/oauth-authorization-server"
        )
        resp.raise_for_status()
        return resp.json()

async def connect_to_mcp_server(server_url: str, idp_config: dict) -> str:
    """
    Connect to an MCP server, using EMA if the server requires it.
    Returns a valid Bearer access token.

    idp_config = {
        "token_endpoint": "https://your-org.okta.com/oauth2/v1/token",
        "id_token": "<current SSO session ID token>"
    }
    """
    metadata = await get_server_auth_metadata(server_url)

    ema_required = metadata.get(
        "io.modelcontextprotocol/enterprise-managed-authorization", {}
    ).get("required", False)

    if ema_required:
        return await perform_id_jag_exchange(server_url, metadata, idp_config)
    else:
        return await perform_standard_oauth(server_url, metadata)

async def perform_id_jag_exchange(
    server_url: str,
    server_metadata: dict,
    idp_config: dict
) -> str:
    """
    Exchange the user's SSO ID token for a server-scoped access token
    via the two-step ID-JAG flow defined in the EMA extension spec.
    """
    as_token_endpoint = server_metadata["token_endpoint"]

    async with httpx.AsyncClient() as client:
        # Step 1: Exchange the user's OIDC ID token for an ID-JAG at the IdP.
        # grant_type uses the IETF Token Exchange URN (RFC 8693).
        id_jag_response = await client.post(
            idp_config["token_endpoint"],
            data={
                # RFC 8693 Token Exchange grant type
                "grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
                "subject_token": idp_config["id_token"],
                # Tells the IdP what type of token we're submitting (OIDC ID token)
                "subject_token_type": "urn:ietf:params:oauth:token-type:id_token",
                # Requests an ID-JAG as the output token type
                "requested_token_type": "urn:ietf:params:oauth:token-type:id_jag",
                "resource": server_url,
                "audience": server_metadata["issuer"],
            }
        )
        id_jag_response.raise_for_status()
        id_jag = id_jag_response.json()["access_token"]

        # Step 2: Present the ID-JAG to the MCP server's Authorization Server.
        # grant_type here is the RFC 7523 JWT Bearer grant.
        token_response = await client.post(
            as_token_endpoint,
            data={
                # RFC 7523 JWT Profile for OAuth 2.0 Authorization Grants
                "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
                "assertion": id_jag,
            }
        )
        token_response.raise_for_status()
        return token_response.json()["access_token"]
Enter fullscreen mode Exit fullscreen mode

Step 3: Read IdP Config from Managed Policy

The IdP endpoints must be configurable at the organization level — not per-user. Read from your platform's managed config delivery mechanism, not from user preferences or synced settings. VS Code does this via the mcp.enterpriseManagedAuth.idp policy-managed setting.


Implementing EMA as an MCP Server Developer

Adding EMA support to your MCP server is primarily about accepting a new authorization grant type and validating ID-JAG JWTs rather than managing per-user OAuth client registrations.

Step 1: Advertise EMA Support in Your AS Metadata

Your /.well-known/oauth-authorization-server response must declare EMA support and list trusted IdP issuers:

{
  "issuer": "https://mcp.yourserver.com/as",
  "token_endpoint": "https://mcp.yourserver.com/as/token",
  "jwks_uri": "https://mcp.yourserver.com/as/.well-known/jwks",
  "grant_types_supported": [
    "authorization_code",
    "urn:ietf:params:oauth:grant-type:jwt-bearer"
  ],
  "io.modelcontextprotocol/enterprise-managed-authorization": {
    "required": true,
    "supported_idp_issuers": [
      "https://your-org.okta.com",
      "https://login.microsoftonline.com/{tenant-id}/v2.0"
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 2: EMA Validation Middleware (FastAPI)

from fastapi import FastAPI, Request, HTTPException, Depends
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
import jwt
from jwt import PyJWKClient
from functools import lru_cache

app = FastAPI(title="EMA-Protected MCP Server")
security = HTTPBearer()

# Cache JWKS clients per issuer to avoid re-fetching public keys on every request
@lru_cache(maxsize=10)
def get_jwks_client(issuer: str) -> PyJWKClient:
    # Okta's JWKS endpoint pattern; adjust for Entra/Auth0
    jwks_uri = f"{issuer}/oauth2/v1/keys"
    return PyJWKClient(jwks_uri, cache_keys=True)

TRUSTED_ISSUERS = {
    "https://your-org.okta.com",
    "https://login.microsoftonline.com/your-tenant/v2.0",
}

async def require_ema_auth(
    credentials: HTTPAuthorizationCredentials = Depends(security)
) -> dict:
    """
    FastAPI dependency that validates Bearer tokens issued via the EMA flow.
    Inject this into any route that requires enterprise-managed authorization.
    Returns the decoded JWT claims on success; raises HTTP 401 on failure.
    """
    token = credentials.credentials

    # Peek at the payload without verifying signature to find the issuer
    try:
        unverified_payload = jwt.decode(
            token, options={"verify_signature": False}
        )
    except jwt.DecodeError:
        raise HTTPException(status_code=401, detail="Malformed token")

    issuer = unverified_payload.get("iss")
    if issuer not in TRUSTED_ISSUERS:
        raise HTTPException(
            status_code=401,
            detail=f"Untrusted token issuer: {issuer}"
        )

    # Now fully verify using the issuer's JWKS
    try:
        jwks_client = get_jwks_client(issuer)
        signing_key = jwks_client.get_signing_key_from_jwt(token)
        claims = jwt.decode(
            token,
            signing_key.key,
            algorithms=["RS256", "ES256"],
            audience="https://mcp.yourserver.com/as",
            issuer=issuer,
        )
    except jwt.ExpiredSignatureError:
        raise HTTPException(status_code=401, detail="Token expired")
    except jwt.InvalidTokenError as e:
        raise HTTPException(status_code=401, detail=f"Invalid token: {e}")

    return claims

# Protected MCP route — user identity and groups injected automatically
@app.post("/mcp/tools/list-issues")
async def list_issues(
    request: Request,
    claims: dict = Depends(require_ema_auth)
):
    user_email = claims.get("email", claims["sub"])
    user_groups = claims.get("groups", [])

    # Apply fine-grained authorization using IdP group claims
    if "engineering" not in user_groups and "platform-team" not in user_groups:
        raise HTTPException(
            status_code=403,
            detail="Access restricted to engineering teams"
        )

    return {
        "issues": await fetch_issues_for_user(claims["sub"]),
        "authorized_as": user_email
    }
Enter fullscreen mode Exit fullscreen mode

Step 3: Publish Your Resource Descriptor

To appear in IdP admin consoles (so Okta or Entra admins can provision your server to their org's MCP policy), publish a resource descriptor at a well-known path:

{
  "resource": "https://mcp.yourserver.com",
  "resource_name": "YourServer MCP",
  "resource_documentation": "https://docs.yourserver.com/mcp",
  "scopes_supported": [
    "issues:read",
    "issues:write",
    "projects:read",
    "projects:write"
  ],
  "mcp_server_info": {
    "name": "YourServer MCP",
    "version": "1.0.0",
    "protocol_version": "2025-03-26"
  }
}
Enter fullscreen mode Exit fullscreen mode

This descriptor is what enables Okta's admin console to surface your MCP server alongside every other SaaS tool the org manages.


VS Code 1.123: EMA in Your IDE Right Now

Microsoft shipped EMA support in VS Code 1.123 (released June 3, 2026) as a preview feature under the name "Enterprise-managed MCP authentication." Here's how to configure it in practice.

The mcp.json Configuration

Add the "enterpriseManaged": true flag to any MCP server entry in your .vscode/mcp.json (or user-level mcp.json) to route it through the EMA/XAA flow:

{
  "servers": {
    "linear": {
      "url": "https://mcp.linear.app/mcp",
      "type": "http",
      "oauth": {
        // Route this server through the enterprise XAA flow
        // instead of per-server Dynamic Client Registration
        "enterpriseManaged": true
      }
    },
    "figma": {
      "url": "https://figma.com/api/mcp",
      "type": "http",
      "oauth": {
        "enterpriseManaged": true
      }
    },
    "supabase": {
      "url": "https://mcp.supabase.com/mcp",
      "type": "http",
      "oauth": {
        "enterpriseManaged": true,
        // Optional: specify a pre-registered OAuth client ID
        // (skips Dynamic Client Registration entirely)
        "clientId": "your-pre-registered-client-id"
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Delivering the IdP Policy Setting

The mcp.enterpriseManagedAuth.idp setting is policy-managed only — it can't be set by individual users and never syncs. This is a deliberate security decision. Delivery paths:

Windows (Group Policy / Registry):

// HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\VSCode
{
  "mcp.enterpriseManagedAuth.idp": {
    "issuer": "https://your-org.okta.com",
    "tokenEndpoint": "https://your-org.okta.com/oauth2/v1/token",
    "clientId": "vs-code-mcp-client"
  }
}
Enter fullscreen mode Exit fullscreen mode

macOS (MDM / Managed Preferences):

<!-- /Library/Managed Preferences/com.microsoft.VSCode.plist -->
<key>mcp.enterpriseManagedAuth.idp</key>
<dict>
  <key>issuer</key>
  <string>https://your-org.okta.com</string>
  <key>tokenEndpoint</key>
  <string>https://your-org.okta.com/oauth2/v1/token</string>
  <key>clientId</key>
  <string>vs-code-mcp-client</string>
</dict>
Enter fullscreen mode Exit fullscreen mode

Linux:

// /etc/vscode/policy.json
{
  "mcp.enterpriseManagedAuth.idp": {
    "issuer": "https://your-org.okta.com",
    "tokenEndpoint": "https://your-org.okta.com/oauth2/v1/token",
    "clientId": "vs-code-mcp-client"
  }
}
Enter fullscreen mode Exit fullscreen mode

Once the policy is deployed, any MCP server entry marked "enterpriseManaged": true is automatically routed through the XAA flow. Users sign in once via corporate SSO — VS Code handles all token exchange silently in the background.

Tip: To test the full EMA flow without a real enterprise IdP, use xaa.dev — a self-contained sample environment built by the MCP team with a sample IdP, resource authorization server, and MCP server you can stand up locally.


The Ecosystem as of June 2026

EMA launched with substantial, coordinated ecosystem support across clients, servers, and identity providers.

Supported Identity Providers

IdP Status Protocol
Okta ✅ Live (Cross-App Access / XAA) OIDC + ID-JAG
Azure Entra ID 🔜 Coming soon OIDC + ID-JAG
Auth0 🔜 Coming soon OIDC + ID-JAG

Supported MCP Servers

Server Status Sample Scopes
Asana ✅ Live tasks:read, projects:read/write
Atlassian (Rovo) ✅ Live jira:read/write, confluence:read
Canva ✅ Live designs:read/write
Figma ✅ Live files:read, comments:read/write
Granola ✅ Live meetings:read, notes:read
Linear ✅ Live issues:read/write, cycles:read
Supabase ✅ Live database:read/write
Slack 🔜 Actively adding

Supported MCP Clients

Client Status Notes
Claude (claude.com) ✅ Live Chat, Claude Code, Cowork — admin-provisioned
VS Code (Copilot) ✅ Preview v1.123+, requires policy-managed IdP config

Enterprise Customers Already Deployed

HubSpot, Ramp, and Webflow are among the first organizations rolling out EMA across their teams at scale, citing the elimination of per-user OAuth grants as a key operational win.


How MCP Enterprise-Managed Authorization Changes Your Security Posture

EMA isn't just a UX improvement — it fundamentally shifts what's possible in enterprise AI security.

1. Centralized Revocation is Now Instant

With standard MCP OAuth, revoking a departing employee's access requires a human to manually revoke tokens at every MCP server. In practice, tokens linger and access persists.

With MCP Enterprise-Managed Authorization, revocation happens at the IdP. Deprovision the user in Okta → every subsequent ID-JAG exchange fails immediately. The MCP server validates the ID-JAG against the IdP's JWKS on every access token issuance, so a revoked identity produces no new tokens. The exposure window is limited to the lifetime of already-issued access tokens — which leads to the next point.

2. Short-Lived Tokens Without UX Degradation

In standard MCP OAuth, generous token TTLs are a practical necessity — requiring users to re-authenticate is disruptive. EMA eliminates this tradeoff entirely. Since re-authentication is automated (the client exchanges a fresh ID-JAG silently in the background), short-lived tokens have zero UX cost. A 5-minute TTL on MCP access tokens is now operationally viable. The blast radius of a compromised token shrinks from "hours of unauthorized access" to "minutes."

3. Consolidated Audit Trail

All MCP server access events are now attributable to corporate identities and auditable from the IdP admin console. For SOC 2, ISO 27001, and HIPAA compliance, this means one fewer disparate system to pull audit logs from during an audit.

4. Enforcement of Corporate Identity

EMA makes it structurally impossible for employees to accidentally connect personal accounts to work MCP servers. The IdP enforces that the subject identity in the ID-JAG is a verified corporate identity. Anthropic's Claude Enterprise implementation includes an admin toggle to "require that a connector only ever connects through the IdP" — a hard block on the personal-account path.

5. Policy Lives at the IdP, Not in the Tool

Adding a new MCP server for the org, restricting it to a specific team, or revoking access during an incident all happen in the same admin console IT already uses for every other SaaS tool. No new admin surface, no new runbooks, no new access request workflows to build.


Conclusion: The New Baseline for Enterprise AI

The Model Context Protocol Enterprise-Managed Authorization extension is one of those rare infrastructure improvements that benefits everyone simultaneously: end users get a frictionless experience, security teams get stronger controls, and developers get a clean, standards-based model built on IETF RFCs that already maps to patterns they understand.

If you're building an MCP server in 2026, EMA support is no longer optional for enterprise sales — it's table stakes. Organizations evaluating AI tools will increasingly ask "does your MCP server support Enterprise-Managed Authorization?" in the same way they ask "do you support SSO?" today. The answer needs to be yes.

If you're deploying MCP in your organization, the path forward is clear: roll out VS Code 1.123 with the mcp.enterpriseManagedAuth.idp policy configured, or deploy Claude Enterprise with Okta provisioning. The per-user OAuth era for enterprise AI agents is over.

Just as SAML and OIDC became the non-negotiable SSO standard for enterprise SaaS, EMA is on track to become the non-negotiable identity standard for enterprise AI agents. The foundations — the IETF draft, the Okta XAA implementation, the Anthropic and Microsoft client support, and the ecosystem of MCP servers — are all in place. The standard will harden, more IdPs will adopt it, and in 18 months the question won't be "should we implement EMA?" but "why haven't you yet?"

Start today. Stand up the sample at xaa.dev, configure VS Code 1.123, and open a PR to add EMA support to your MCP server. Your CISO — and your users' mornings — will thank you.


Published June 19, 2026 | Focus keyword: MCP Enterprise-Managed Authorization | ~3,800 words | Estimated read time: 15 minutes

References:

Top comments (1)

Collapse
 
eleftheriabatsou profile image
Eleftheria Batsou

One thing worth adding for readers: enterprise-managed OAuth solves who the agent is beautifully, but it doesn't bound what the authenticated agent can reach once it's in.

Zero-touch auth and least-reach containment are complementary, not substitutes, you want the IdP deciding identity and the runtime deciding blast radius.

The auth crisis and the containment crisis get solved by different layers, and teams that fix one often think they've fixed both.