MCP spread through enterprises bottom-up, one connector at a time, with authorization left as an afterthought. The original model is per-user, per-server OAuth: every employee clicks through a consent screen for every server. That's the right default for a consumer wiring up a personal integration. Inside a company it's a mess, and a security gap nobody owns.
Enterprise-Managed Authorization (EMA) is the spec's answer, stable since June 18, 2026. It moves the authorization decision into the corporate identity provider, so employees reach approved servers on first sign-on and no one clicks consent screens anymore. This guide covers the real request shapes, how discovery works, and where EMA's authority stops: the line that decides how much of your security you still have to build yourself. It's based on the stable spec (io.modelcontextprotocol/enterprise-managed-authorization) and the IETF draft it profiles.
Key takeaways
- EMA makes the enterprise IdP (Okta today) the authority on which MCP clients can reach which MCP servers, so employees get approved servers on first SSO with no per-server consent.
- Under the hood it's three standard pieces: an OIDC/SAML login, an RFC 8693 token exchange for an ID-JAG, and an RFC 7523 JWT bearer grant for the access token.
- EMA decides the connection, and through scopes the coarse set of actions possible at issuance. What it can't do is make a runtime, context-aware decision about a specific call. The spec is explicit that the IdP never sees the MCP traffic.
- That runtime decision, per-action authorization, is the layer EMA leaves entirely to you, and it lives in the data path.
What Enterprise-Managed Authorization solves
The old model has three problems that get worse with scale. Security teams can't centralize anything: when every user authorizes independently, there's no single place to approve or deny a server. Onboarding and offboarding are manual, a new hire authorizing a dozen servers by hand and a leaver revoked server by server. And everyone drowns in consent prompts as tokens expire and re-issue.
EMA moves the decision into the identity provider. The IdP becomes the authority on which clients can reach which servers, for which users. The employee signs in once with corporate SSO, and behind that login the client silently acquires tokens for every approved server. Revoke at the IdP and it takes effect everywhere, immediately.
The boundary: what EMA governs, and what it doesn't
EMA governs the connection. What it can't govern is the runtime action: whether a specific call, in a specific context, should go through. The spec says so in its security considerations: the IdP's visibility is limited to the moment the access token is issued and does not extend to the MCP traffic between client and server. The IdP can decide that Alice, in engineering, may connect her assistant to the company knowledge base. It cannot decide whether a given request should be allowed to return a document marked confidential that sits outside her need-to-know.
There is a softer form of control worth stating precisely, because it's easy to oversell. If the MCP server defines granular OAuth scopes, the IdP policy can constrain which scopes a user may request at issuance time. So "engineering read-only, marketing read-write" is reachable, but only if the server split read and write into separate scopes, and only as a one-time decision baked into the token rather than a check that runs per call.
EMA answers can this user connect this client to this server, and with which scopes? It never answers should this specific action run, right now, on this resource?
Why the line falls there isn't arbitrary. It's a direct consequence of who is involved in the flow and how they hand off to each other, which is the next thing to understand.
The roles in the EMA flow
Four parties, and two of them sound alike and aren't:
- MCP Client: the AI app (Claude, VS Code, your own agent). The Client.
- MCP Server: the thing exposing tools. The Resource Server.
- Resource Authorization Server: issues access tokens for the MCP server. Found via the server's Protected Resource Metadata (RFC 9728).
- IdP Authorization Server: the enterprise identity provider doing SSO. It mints the intermediate grant.
The two authorization servers do different jobs. The IdP authorizes the user against org policy and hands back an intermediate grant. The Resource AS authorizes that grant and hands back the token the client actually calls the server with. The IdP touches the exchange and then drops out; it is never on the path of a tool call.
How the EMA flow works, end to end
Three standard pieces stacked: an OIDC or SAML login, a Token Exchange (RFC 8693), and a JWT authorization grant (RFC 7523).
Notice where each party leaves the diagram. The IdP's last appearance is issuing the ID-JAG. The Resource AS's last appearance is issuing the access token. Everything in the loop at the bottom, the actual work, happens with neither of them watching.
SSO, and the identity assertion
The client runs a normal OIDC or SAML login against the IdP. The user authenticates, MFA and conditional access fire if the org set them up, and the client ends up holding an Identity Assertion (an OIDC ID Token or a SAML assertion) and stores it.
SAML has an extra hop: you first exchange the assertion for a refresh token at the IdP, then use that refresh token in place of the ID Token.
Token Exchange for the ID-JAG
The ID-JAG comes from an RFC 8693 token exchange:
POST /oauth2/token HTTP/1.1
Host: acme.idp.example
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&requested_token_type=urn:ietf:params:oauth:token-type:id-jag
&audience=https://auth.chat.example/
&resource=https://mcp.chat.example/
&scope=chat.read+chat.history
&subject_token=eyJraWQiOiJzMTZ0cVNt...
&subject_token_type=urn:ietf:params:oauth:token-type:id_token
&client_id=2ec954a1d60620116d36d9ceb7
&client_secret=a26d84873504215a34a86d52ef5cd64f4b76Two parameters carry the weight. audience must be the issuer identifier of the Resource Authorization Server, which scopes the grant to one server's AS. resource is optional, but if set, it must be the MCP server's Resource Identifier per RFC 9728.
This is the one moment org policy runs. The IdP evaluates group, role, and conditional access, then decides whether this client may act for this user against this server at these scopes. A denial returns nothing, so unauthorized servers aren't merely blocked, they're invisible to the client.
The grant comes back like this:
{
"issued_token_type": "urn:ietf:params:oauth:token-type:id-jag",
"access_token": "eyJhbGciOiJIUzI1NiIsI...",
"token_type": "N_A",
"scope": "chat.read chat.history",
"expires_in": 300
}token_type is N_A because the ID-JAG isn't something you call anything with. It's a grant you redeem once, within about five minutes, then discard.
What's in the grant
{ "typ": "oauth-id-jag+jwt" }
.
{
"jti": "9e43f81b64a33f20116179",
"iss": "https://acme.idp.example",
"sub": "U019488227",
"email": "user@example.com",
"aud": "https://auth.chat.example/",
"resource": "https://mcp.chat.example/",
"client_id": "f53f191f9311af35",
"exp": 1311281970,
"iat": 1311280970,
"scope": "chat.read chat.history"
}The typ header is exactly oauth-id-jag+jwt; a Resource AS that accepts anything else here has a confused-deputy bug waiting to happen. sub is opaque and unique only within the issuer, so the real user key is iss+sub, never sub alone. email, when present, is for account linking, not identity, so don't authorize off it.
Redeeming it for an access token
The client presents the ID-JAG to the Resource AS as an RFC 7523 JWT bearer grant:
POST /oauth2/token HTTP/1.1
Host: auth.chat.example
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
&assertion=eyJhbGciOiJIUzI1NiIsI...
&client_id=https://client.example.com/client.jsonThe client authenticates with its registered credentials. A client that was never pre-registered can instead use a Client ID Metadata Document (a URL that is its client_id, as shown) and authenticate with private_key_jwt, which is useful for clients that can't pre-register everywhere.
The Resource AS validates the grant and returns an ordinary token response:
{
"token_type": "Bearer",
"access_token": "2YotnFZFEjr1zCsicMWpAA",
"expires_in": 86400,
"scope": "chat.read chat.history"
}The issued access token must be audience-restricted to the MCP server named in the ID-JAG's resource claim, so it's good for that one server and nothing else. From here it's plain MCP: a Bearer token on every call, silent refresh by re-running the exchange off the stored assertion, and a return to SSO only when the assertion itself expires.
How a client discovers EMA support
All of that assumes the client already knew the server speaks EMA. Finding out is two separate lookups.
First, which authorization server issues tokens for the server? That comes from the server's Protected Resource Metadata (RFC 9728), the same mechanism core MCP already uses. Second, does that Resource AS support EMA? The client checks for urn:ietf:params:oauth:grant-profile:id-jag in the authorization_grant_profiles_supported array of the AS metadata. There's no separate EMA endpoint and no custom well-known document; it's a grant profile on standard AS metadata.
The client declares its own support at the protocol level, in initialize:
{
"capabilities": {
"extensions": {
"io.modelcontextprotocol/enterprise-managed-authorization": {}
}
}
}Extensions are opt-in and off by default, so both ends have to announce themselves.
Implementing the Resource Authorization Server
Discovery and the flow define the protocol; building either side is mostly a matter of honoring it. The AS owns the validate-and-issue half, and the spec defers most processing rules to the IETF draft, so treat the draft as the authority on edge cases.
Accept the jwt-bearer grant alongside whatever you already support. Reject any assertion whose typ header isn't oauth-id-jag+jwt. Resolve iss against a trusted-IdP allowlist you keep out of band, per tenant, and verify the signature against that IdP's JWKS. Check that aud is your own issuer, resource is a server you front, and client_id matches the authenticated client, with the time claims holding inside a little skew. Replay-protect jti for the grant's short lifetime; it's cheap now and painful to retrofit.
Map the surviving claims to your permission model. iss+sub is the user, and scope is a ceiling, not a floor, so grant equal or less but never more. Issue a token audience-bound to resource, return standard OAuth errors when something doesn't line up, and advertise the id-jag grant profile so clients can find you.
The operationally annoying part is multi-tenant trust. You don't trust "Okta"; you trust this customer's Okta tenant, its specific issuer and JWKS, mapped to the specific clients that customer's admin approved. None of that lives in the protocol. It's admin-console plumbing you build, keyed off iss at validation time.
Implementing the MCP client
The client side discovers in order: PRM to find the AS, AS metadata to confirm the grant profile, its own initialize capability to announce it speaks EMA. Admins configure IdP endpoints at the org level, and treating that as a per-user setting is the classic early mistake.
Run the SSO, hold the assertion, do the two token steps, and never redirect the user to the Resource AS authorization endpoint. That redirect is the consent screen EMA exists to delete, so reaching for it means you've fallen back to the old flow. Treat a narrowed scope as a normal outcome and degrade features rather than erroring, and build silent refresh early, because re-running the exchange off the stored assertion is what makes "sign in once" hold across a long session.
What EMA doesn't govern: per-action authorization
Look again at the sequence diagram. After the access token is issued, the IdP and the Resource AS are gone, and every call in that loop runs with no policy in the path. That is the boundary, and it's structural, not an oversight.
Picture a release agent wired to the source-control server with write scope, because it legitimately needs to write sometimes. EMA got it a valid token. Nothing in EMA stops it, mid-run, from deleting a branch it shouldn't, or acting on a repo outside the one task it was asked to do. The token says this user may write here; it says nothing about this action, right now, for this purpose.
Closing that gap means putting something in the data path. If a layer sits between client and server, every call passes through it carrying an identity it can resolve and a context it can reason about, and it can make the decision EMA structurally can't: should this action, on this resource, by this actor, in this context, run, and if it does, what comes back?
In practice that's a policy decision point and a policy enforcement point, kept apart. The enforcement point is inline on the call path, because that's the only place the real request and response exist. The decision point is a separate engine (your own rules, or a policy engine like OPA, Cedar, or an OpenFGA/Zanzibar-style service) queried for an allow / deny / step-up verdict plus obligations: mask these fields, require approval for that action, log this one at elevated detail. Splitting them lets you change policy without touching the proxy, and turns every decision into a discrete, signable event, which is the difference between an audit trail and a pile of access logs.
A sketch of the rule layer, in Rego:
package policy.sourcecontrol
default allow = false
# engineering may read/write in its own repos
allow {
input.identity.groups[_] == "engineering"
input.action.method != "delete"
input.resource.repo_prefix == "team-eng/"
}
# no agent-initiated deletes without a human in the loop
deny[msg] {
input.action.method == "delete"
msg := "delete requires human approval"
}EMA makes this layer more clearly necessary, not less. It doesn't retire the connection-layer concerns (token theft, overprivileged scopes, a misconfigured IdP all still bite), but once the IdP owns "can connect," the question it leaves unanswered is the one that has no standard tooling yet: should this run.
EMA's limits: what it does not do
The action gap is the big limit. The smaller ones are worth knowing before you commit. Okta is the only IdP shipping EMA today, so plan a fallback for orgs that aren't on it. Each ID-JAG is scoped to one server, so there's no universal cross-server token; it's one exchange per target. The server has to implement the grant, which is a code change, not a config flip. And scope-level governance only works to the granularity the server defined its OAuth scopes, decided once at issuance rather than per call.
The one teams trip over hardest: the token this flow issues is good for the MCP server only. It is not a credential for the provider's general REST API, which sits behind its own authorization, audience, and grant. Getting an MCP token via EMA does not get you into the underlying REST surface; that's a parallel integration, not a freebie.
Implementation checklist
Resource Authorization Server
- Accept
urn:ietf:params:oauth:grant-type:jwt-beareron the token endpoint - Reject any assertion whose header
typisn'toauth-id-jag+jwt - Resolve
issagainst a per-tenant trusted-IdP allowlist before trusting anything - Verify the signature against that IdP's JWKS
- Check
aud= your issuer,resource= a server you front,client_id= the authenticated client, plusexp/iatwith skew - Replay-protect
jtifor the grant's lifetime - Key the user off
iss+sub; never grant scope beyond the ID-JAG'sscope - Issue an access token audience-restricted to
resource - Advertise
urn:ietf:params:oauth:grant-profile:id-jaginauthorization_grant_profiles_supported - Return RFC 6749 §5.2 errors on failure
MCP Client
- Find the Resource AS via the server's Protected Resource Metadata (RFC 9728)
- Confirm the id-jag grant profile in the AS metadata
- Declare the extension in your
initializecapabilities - Support org-level IdP configuration (not per-user)
- Do SSO via OIDC or SAML; if SAML, exchange the assertion for a refresh token first
- Token-exchange the assertion for an ID-JAG (
audience= Resource AS issuer;resourceoptional) - Redeem the ID-JAG via
jwt-bearer; support Client ID Metadata Document +private_key_jwtif not pre-registered - Never redirect the user to the Resource AS authorization endpoint
- Treat narrowed scope as a normal outcome, not an error
- Build silent refresh off the stored assertion
Policy layer (everything EMA leaves out)
- Intercept every tool call inline
- Resolve identity from the token's
subplus session group/role context - Split decision point from enforcement point; query for allow / deny / step-up + obligations
- Sign and store each decision for audit
- Inspect and redact responses, not just requests
- Keep a non-EMA fallback for orgs that aren't on a supporting IdP
Frequently asked questions about MCP enterprise-managed authorization
What is enterprise-managed authorization (EMA) in MCP? It's a stable MCP extension that lets an organization's identity provider centrally control which MCP clients can access which MCP servers. Instead of each employee authorizing each server through a consent screen, an admin sets policy once in the IdP, and users get their approved servers on first SSO login.
Does EMA replace MCP's normal OAuth flow?
No. It's additive. EMA introduces an extra grant path (the ID-JAG flow) for enterprise deployments; the standard per-user OAuth model still exists and is still the right default for consumer use. Servers opt in by advertising the id-jag grant profile, and clients opt in via an initialize capability.
Does EMA secure individual tool calls? No, and this is the most important thing to understand. EMA governs the connection: who may connect which client to which server, and at most which coarse scopes they get at token-issuance time. The spec states plainly that the IdP has no visibility into the MCP traffic itself. Whether a specific action should run on a specific resource is a separate, per-action authorization decision that EMA does not make.
Which identity providers support EMA? As of the June 2026 stable release, Okta is the IdP shipping support. EMA is built on the open ID-JAG / identity-chaining drafts, so other providers can implement it, but plan a non-EMA fallback for organizations that aren't on a supporting IdP yet.
Can I use an EMA access token to call the provider's REST API?
No. The token is audience-restricted to the MCP server named in the ID-JAG's resource claim. The provider's general REST API sits behind its own authorization server, audience, and grant. Reaching it is a separate integration, not something EMA hands you.
The bottom line
EMA solves a real problem. It puts the enterprise back in control of which agents can reach which systems, through identity it already runs, and that's worth adopting.
But it stops at the door. It decides who gets in and says nothing about what they do once inside. Reading "the IdP authorized this connection" as "this is safe" is a misreading of the spec. EMA closes the connection-governance gap; the per-action, runtime decision is the half it leaves open, and that half is still yours to build.
Comparing notes
I'm in the weeds on exactly this problem, the per-action authorization layer past EMA, with a small number of teams moving agent workflows from pilot to production. If you're standing up MCP inside an organization and hitting the gap between "the IdP let it connect" and "should this call run," I'm happy to compare notes on a quick call.
Sources
- EMA extension overview: https://modelcontextprotocol.io/extensions/auth/enterprise-managed-authorization
- Stable specification: https://github.com/modelcontextprotocol/ext-auth/blob/main/specification/stable/enterprise-managed-authorization.mdx
- ext-auth repository: https://github.com/modelcontextprotocol/ext-auth
- Launch announcement: https://blog.modelcontextprotocol.io/posts/enterprise-managed-auth/
- Identity Assertion JWT Authorization Grant (IETF draft): https://datatracker.ietf.org/doc/draft-ietf-oauth-identity-assertion-authz-grant/
- RFC 8693 (Token Exchange), RFC 7523 (JWT bearer grant), RFC 9728 (Protected Resource Metadata)
Subscribe for Deep Dives
Get exclusive in-depth technical articles and insights directly to your inbox.
