TL;DR
- CIMD means Client ID Metadata Document. It lets an OAuth client identify itself with an HTTPS URL that points to a JSON metadata document.
- For MCP, CIMD helps solve a real registration problem: MCP clients and MCP servers often meet for the first time at runtime, so manual pre-registration does not scale well.
- Auth0 now supports manual CIMD registration for third-party applications. A tenant admin imports the CIMD URL, Auth0 fetches and validates the document, and the CIMD URL becomes the external client identity.
- In Auth0’s Auth for MCP model, your MCP server is an OAuth protected resource, Auth0 is the authorization server, and the MCP client or agent must obtain a scoped access token before calling tools.
- For modern apps, CIMD is not a replacement for login, JWT validation, scopes, authorization checks, audit logs, or fine-grained access control. It is a cleaner way to register and recognize clients.
- For workflow agents and orchestrators, the serious pattern is: authenticate the human, identify the agent/client, bind tokens to the MCP server, exchange tokens on behalf of the user when calling downstream APIs, and check resource-level permissions before every meaningful action.
Agentic software makes a very old identity question feel new again:
Who is calling this system,
on behalf of whom,
through which client,
for which resource,
with what permission,
and can I prove it later?
That question used to belong mostly to enterprise identity teams. Now it shows up inside normal product work: MCP tools, copilots, support agents, workflow builders, internal automation, RAG pipelines, and agents that call other agents.
Auth0’s new CIMD support matters because it gives teams a practical path for one piece of that puzzle: how an OAuth authorization server recognizes a client in a dynamic MCP world.
What You Will Learn Here
- What CIMD is and why it exists
- How Auth0 uses CIMD with Auth for MCP
- How the MCP authorization flow fits together with OAuth 2.1, protected resource metadata, authorization server metadata, PKCE, and scopes
- How to set up a CIMD-backed client from zero
- How to protect a modern web app, MCP server, workflow agent, or agent orchestrator with Auth0
- Where CIMD ends and where you still need authorization, token exchange, audit, and approval controls
The One-Sentence Version
CIMD lets a client say, “my client ID is this HTTPS metadata URL,” and lets the authorization server fetch that URL to understand the client’s name, redirect URIs, logo, grant types, and authentication method.
Example:
client_id = https://agent.example.com/oauth/client.json
That URL is not just a random string. It is also where the client publishes metadata about itself.
{
"client_id": "https://agent.example.com/oauth/client.json",
"client_name": "Acme Workflow Agent",
"client_uri": "https://agent.example.com",
"logo_uri": "https://agent.example.com/logo.png",
"redirect_uris": ["https://agent.example.com/oauth/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"token_endpoint_auth_method": "none"
}
For a public client, token_endpoint_auth_method: "none" means the client does not authenticate with a secret at the token endpoint. It must use PKCE in the authorization code flow.
For a confidential client, CIMD can use private_key_jwt, where the client publishes public keys through jwks_uri and signs token endpoint authentication with its private key.
Why CIMD Exists
Classic OAuth assumes the client is registered before the authorization flow begins. That works nicely when your application has a known relationship with one identity provider.
It gets awkward in MCP.
MCP clients and servers are intentionally composable. A user may connect Claude Desktop, Cursor, VS Code, a custom agent, or a workflow orchestrator to an MCP server that the client has never seen before.
Without CIMD, you usually have three options:
| Approach | Works well when | Problem |
|---|---|---|
| Pre-registration | Client and server already have a relationship | Poor fit for open MCP ecosystems |
| Dynamic Client Registration | Unknown clients need automated registration | Can create operational cleanup, abuse, and database growth concerns |
| Manual copy/paste client IDs | Small private deployments | Bad user experience and easy to misconfigure |
CIMD changes the shape:
Client owns metadata URL
Authorization server fetches metadata
Client ID is stable across deployments
Admin can inspect the URL and metadata
OAuth flow continues with normal authorization code + PKCE
It is especially useful for MCP because the client can carry its identity in a standards-aligned way without needing a new client registration for every server/user/installation pair.
How Auth0 Fits In
Auth0’s Auth for MCP positions Auth0 as the authorization server for MCP clients and servers.
The high-level model:
MCP Client / Agent
|
| needs access token
v
Auth0 Authorization Server
|
| issues scoped token
v
MCP Server / Tool Gateway
|
| optional token exchange
v
Downstream APIs, workflows, data stores
Auth0 handles:
- user authentication through your configured identity providers
- OAuth/OIDC token issuance
- standards-based discovery metadata
- client registration with CIMD
- consent and scoped permissions
- on-behalf-of token exchange for downstream APIs
Your application still handles:
- validating tokens on API and MCP requests
- enforcing scopes and resource permissions
- deciding which tool calls are allowed
- logging and auditing actions
- adding human approval where risk is high
The MCP Authorization Stack
MCP authorization is not a brand-new auth protocol. It combines several OAuth building blocks.
| Layer | What it does |
|---|---|
| OAuth 2.1 | Authorization code flow, PKCE, bearer token usage, refresh token expectations |
| RFC 9728 Protected Resource Metadata | Lets the MCP server tell clients where authorization happens |
| RFC 8414 Authorization Server Metadata | Lets clients discover Auth0 authorization and token endpoints |
| CIMD | Lets clients identify themselves with a metadata URL |
| Resource Indicators | Bind tokens to the specific MCP server/resource |
| Scopes | Express coarse permissions such as tool:greet or files:read |
| OBO Token Exchange | Lets the MCP server call another API while preserving user context |
That stack sounds heavy, but the runtime flow is understandable.
User MCP Client MCP Server Auth0 API
| | | | |
| asks agent | | | |
|----------------->| | | |
| | unauth request | | |
| |------------------->| | |
| | 401 + metadata | | |
| |<-------------------| | |
| | discover Auth0 | | |
| |----------------------------------------->| |
| | auth request | | |
| | client_id=CIMD URL | | |
| |----------------------------------------->| |
| login + consent | | | |
|<---------------------------------------------------------->| |
| | access token | | |
| |<-----------------------------------------| |
| | call MCP tool | | |
| | Bearer token | | |
| |------------------->| validate token | |
| | | maybe exchange OBO | |
| | |------------------->| |
| | | API-scoped token | |
| | |<-------------------| |
| | | call API | |
| | |------------------------------------->|
| | | result | |
| |<-------------------|<-------------------------------------|
| response | | | |
|<-----------------| | | |
From Zero: Set Up Auth0 CIMD
The practical setup has two sides: the client metadata you host, and the Auth0 tenant configuration that imports it.
1. Host a CIMD document
Put the document on a stable HTTPS URL with a real path:
https://agent.example.com/oauth/client.json
Auth0’s CIMD URL validation rejects local loopback hosts like localhost, 127.0.0.1, and ::1. The URL must use HTTPS, include a path beyond /, and avoid fragments, query strings, credentials, dot segments, and whitespace.
For a public MCP client:
{
"client_id": "https://agent.example.com/oauth/client.json",
"client_name": "Acme Agent Console",
"description": "MCP client for Acme workflow automation",
"client_uri": "https://agent.example.com",
"logo_uri": "https://agent.example.com/logo.png",
"application_type": "web",
"grant_types": ["authorization_code", "refresh_token"],
"redirect_uris": ["https://agent.example.com/oauth/callback"],
"response_types": ["code"],
"token_endpoint_auth_method": "none"
}
For a confidential client:
{
"client_id": "https://agent.example.com/oauth/client.json",
"client_name": "Acme Agent Backend",
"application_type": "web",
"grant_types": ["authorization_code", "refresh_token"],
"redirect_uris": ["https://agent.example.com/oauth/callback"],
"response_types": ["code"],
"token_endpoint_auth_method": "private_key_jwt",
"jwks_uri": "https://agent.example.com/oauth/jwks.json"
}
The jwks_uri must share the same origin as the CIMD URL in Auth0’s model.
2. Enable CIMD in Auth0
In the Auth0 Dashboard:
Settings
-> Advanced
-> Client ID Metadata Document Registration
For MCP clients, Auth0 also recommends enabling the Resource Parameter Compatibility Profile so authorization requests can use the resource parameter when audience is not provided.
3. Preview the CIMD with the Management API
Before registering the client, preview it:
curl --request POST \
--url "https://YOUR_AUTH0_DOMAIN/api/v2/clients/cimd/preview" \
--header "Authorization: Bearer YOUR_MANAGEMENT_API_TOKEN" \
--header "Content-Type: application/json" \
--data '{
"external_client_id": "https://agent.example.com/oauth/client.json"
}'
Use preview as a CI check for your client metadata. It catches validation problems before the tenant admin registers the client.
4. Register the CIMD client
After preview passes:
curl --request POST \
--url "https://YOUR_AUTH0_DOMAIN/api/v2/clients/cimd/register" \
--header "Authorization: Bearer YOUR_MANAGEMENT_API_TOKEN" \
--header "Content-Type: application/json" \
--data '{
"external_client_id": "https://agent.example.com/oauth/client.json"
}'
Auth0 stores a registered application record, but the hosted CIMD remains the source of truth. Metadata updates are synchronized through a manual refresh.
5. Configure API access
For an MCP server, create an Auth0 API that represents the MCP resource.
Example scopes:
tool:greet
tool:search
tool:write
workflow:run
workflow:approve
documents:read
Use boring names. They are easier to audit than clever names.
Protecting a Modern Web App
In a normal web app, CIMD is usually relevant when your app or integration acts as an OAuth client to an Auth0-protected resource. Your application still follows the same core pattern:
Browser
-> Web app frontend
-> Backend route / server action / API route
-> Validate Auth0 token
-> Check scopes and resource permissions
-> Perform action
A useful Next.js-style server helper might look like this:
import { createRemoteJWKSet, jwtVerify } from "jose";
const issuer = `https://${process.env.AUTH0_DOMAIN}/`;
const audience = process.env.AUTH0_AUDIENCE!;
const jwks = createRemoteJWKSet(
new URL(`https://${process.env.AUTH0_DOMAIN}/.well-known/jwks.json`)
);
export async function verifyAccessToken(token: string) {
const { payload } = await jwtVerify(token, jwks, {
issuer,
audience,
});
return {
subject: payload.sub,
clientId: payload.client_id,
scopes: String(payload.scope ?? "").split(" ").filter(Boolean),
claims: payload,
};
}
export function requireScope(scopes: string[], required: string) {
if (!scopes.includes(required)) {
throw new Response("Insufficient scope", { status: 403 });
}
}
That is the minimum. Real production apps usually add:
- org/workspace membership checks
- row-level or document-level authorization
- audit events
- rate limits per user, client, tenant, and tool
- explicit approvals for irreversible actions
Protecting an MCP Server
An MCP server is a tool surface. Treat it as a resource server.
It should publish protected resource metadata:
{
"resource": "https://mcp.example.com",
"authorization_servers": ["https://YOUR_AUTH0_DOMAIN"],
"scopes_supported": [
"tool:greet",
"tool:search",
"workflow:run"
],
"resource_name": "Acme MCP Server",
"bearer_methods_supported": ["header"]
}
It should challenge unauthenticated callers with enough information for the client to discover auth:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource",
scope="tool:search"
And it should validate every request:
type ToolAuth = {
subject: string;
clientId?: string;
scopes: string[];
};
export async function authorizeToolCall(
token: string,
requiredScope: string
): Promise<ToolAuth> {
const auth = await verifyAccessToken(token);
requireScope(auth.scopes, requiredScope);
return {
subject: auth.subject!,
clientId: typeof auth.clientId === "string" ? auth.clientId : undefined,
scopes: auth.scopes,
};
}
The important rule: never treat “the agent called the tool” as enough. The token must be valid for this MCP server, scoped for this action, and tied to the user or client context you expect.
Calling APIs on the User’s Behalf
MCP servers often need to call a downstream API.
Example:
User asks: "Summarize my invoices and draft a refund workflow."
MCP server needs:
- invoice API token
- customer support API token
- workflow API token
Do not pass the MCP access token through to every downstream API.
Use on-behalf-of token exchange:
MCP access token
audience = https://mcp.example.com
scope = tool:refund
exchange with Auth0
API access token
audience = https://api.example.com
scope = refunds:write
user = same original user context
Pseudo-code:
export async function exchangeOnBehalfOfUser(accessToken: string) {
const response = await fetch(`https://${process.env.AUTH0_DOMAIN}/oauth/token`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
client_id: process.env.MCP_SERVER_CLIENT_ID,
client_secret: process.env.MCP_SERVER_CLIENT_SECRET,
subject_token_type: "urn:ietf:params:oauth:token-type:access_token",
subject_token: accessToken,
requested_token_type: "urn:ietf:params:oauth:token-type:access_token",
audience: "https://api.example.com",
scope: "refunds:write"
})
});
if (!response.ok) {
throw new Error("Token exchange failed");
}
return response.json() as Promise<{ access_token: string; scope?: string }>;
}
Auth0’s SDKs wrap this for supported environments, but the mental model is the same: exchange the token at the boundary where the audience changes.
Workflow Agents and Orchestrators
The most interesting use case is not “agent logs in.” It is “agent does constrained work for a human inside a larger workflow.”
A good production model separates four identities:
| Identity | Example | Why it matters |
|---|---|---|
| Human | user:ana | Consent, ownership, audit, business accountability |
| Client | https://agent.example.com/oauth/client.json | Which app initiated the OAuth flow |
| Agent | agent:refund-drafter | Which autonomous worker performed the step |
| Task | task:refund-9821 | What bounded job was approved |
That gives you flows like this:
Human approves task
|
v
Orchestrator creates task-scoped run
|
v
Agent invokes MCP tool
|
v
MCP server validates token + scope
|
v
Authorization layer checks resource permission
|
v
Tool executes or requests human approval
For PMs, the product question is:
What should the agent be allowed to do without asking again?
For engineers, the implementation question is:
Where do we encode that boundary so every tool path enforces it?
Scopes are useful, but they are not enough for rich product permissions.
Use scopes for broad capability:
workflow:run
documents:read
refunds:write
Use your authorization system for resource-level decisions:
Can user:ana view document:roadmap?
Can agent:refund-drafter draft refund for customer:123?
Can task:refund-9821 write to case:456 before manager approval?
If you already use Auth0 FGA or OpenFGA, this is where it belongs.
When to Use CIMD, Pre-Registration, or DCR
Use CIMD when:
- your client and authorization server may not have a prior relationship
- you are building an MCP client, agent platform, or third-party integration
- you want a stable, URL-based client identity
- you want admins to inspect a human-readable registration source
- you can host metadata on a stable HTTPS domain
Use pre-registration when:
- the integration is private or first-party
- you control both sides
- client IDs and callback URLs are known ahead of time
- operational simplicity matters more than dynamic discovery
Use Dynamic Client Registration when:
- your ecosystem already depends on it
- the authorization server has strong abuse controls
- clients cannot reasonably host metadata
- you need backwards compatibility with older MCP clients
Expert Checklist
Before shipping this to production, check the boring things. Boring is where security lives.
- The CIMD URL is stable, HTTPS, short enough for humans to inspect, and hosted under a domain you control.
- The
client_idinside the JSON exactly matches the document URL. - Redirect URIs are exact, minimal, and production-only unless you have a deliberate development policy.
- Public clients use authorization code with PKCE.
- Confidential clients use
private_key_jwt, not shared secrets in metadata. - The authorization server advertises
client_id_metadata_document_supportedwhen CIMD is supported. - MCP protected resource metadata is available through the well-known endpoint or the
WWW-Authenticatechallenge. - Tokens are audience-bound to the MCP server.
- MCP servers reject tokens minted for other APIs.
- Downstream APIs get their own access tokens through token exchange.
- Tool scopes are minimal and are challenged incrementally when possible.
- Fine-grained permissions are checked server-side.
- Logs include user, client, agent, task, resource, scopes, and decision.
- Sensitive tool calls require confirmation, approval, or a policy gate.
- Metadata changes trigger review when redirect URIs, keys, grant types, scopes, name, or logo change.
Common Mistakes
Mistake 1: Treating CIMD as authentication
CIMD identifies the client. It does not authenticate the user. You still need the normal OAuth/OIDC login flow.
Mistake 2: Trusting the client name too much
Client metadata can contain display fields such as name and logo. Those help users understand consent, but they are not a complete trust signal. Show domains clearly and maintain admin approval policies.
Mistake 3: Passing one token everywhere
The MCP server token should be for the MCP server. If the server calls a downstream API, exchange it for an API-scoped token.
Mistake 4: Using scopes as your entire permission model
Scopes answer broad capability questions. They usually cannot answer “can this user access this exact project, ticket, invoice, or document?”
Mistake 5: Forgetting non-human identity
In agent systems, audit only by human user is too thin. You also need the client, agent, model/workflow run, and task context.
A Practical Architecture
For a serious web app with MCP and workflow agents, I would start here:
+----------------------+
| Auth0 |
| - Login |
| - CIMD registration |
| - Token issuance |
| - OBO exchange |
+----------+-----------+
|
v
+-------------+ +--------+---------+ +----------------+
| Web / Agent | ----> | MCP Gateway | ----> | Internal APIs |
| Client | | - JWT validation | | - Business ops |
| CIMD URL | | - Scope checks | | - Data access |
+-------------+ | - Tool policy | +--------+-------+
+--------+---------+ |
| |
v v
+--------+---------+ +----------------+
| Authorization | | Audit / SIEM |
| - FGA/RBAC/ABAC | | - user |
| - task policy | | - client |
| - approval gates | | - agent/task |
+------------------+ +----------------+
The MCP gateway becomes the secure choke point. It does not need to know every business rule itself, but it must enforce the rule that every tool call has an identity, token, scope, resource, and decision.
Product Questions Worth Asking
For PMs and engineering leads, the implementation is only half the design.
Ask:
- Which actions can an agent perform silently?
- Which actions require user consent once?
- Which actions require approval every time?
- Which downstream APIs need user-bound tokens?
- Which actions are reversible?
- Which actions need audit exports for enterprise customers?
- Which customers need tenant-level allowlists for approved MCP clients?
- What should happen when a client metadata document changes?
These questions shape the user experience more than the OAuth details do.
Gaps and Open Edges
There are still places where teams need to design carefully.
Auth0’s CIMD support is currently manual registration: a tenant admin imports and approves the CIMD URL. That is good for control, but it is not the same as accepting any unknown client automatically.
Auth0’s documentation also notes current limitations and future work around third-party application Organizations support and rate limits for CIMD clients.
CIMD itself does not fully solve desktop or localhost client impersonation. The MCP spec calls out localhost redirect risks, and the broader ecosystem is still exploring stronger software statements and platform attestation.
Agent identity is still partly an application architecture problem. OAuth can identify the user and client. Your orchestrator still needs a clean model for agent identity, task identity, approvals, and audit trails.
Final Mental Model
Think of Auth0 CIMD as the front door label for an OAuth client in a dynamic agent ecosystem.
It says:
This is the client.
Here is where its metadata lives.
Here are its redirect URIs and auth method.
Here is what users and admins should see before trusting it.
Auth0 Auth for MCP then gives you the identity plumbing around that client:
discover -> authorize -> issue token -> call MCP -> exchange token -> call API
Your app provides the product safety:
permissions -> approvals -> audit -> least privilege -> human accountability
That combination is where agentic applications start to feel less like demos and more like real systems.
Source List
- Auth0: Auth0 Auth for MCP Is Now Generally Available
- Auth0 Docs: Register Applications with CIMD
- Auth0 AI Docs: Auth for MCP
- Auth0 AI Docs: Why use Auth for MCP
- Auth0 AI Docs: Call Your API on a User’s Behalf
- Auth0 Docs: On-Behalf-Of Token Exchange
- Auth0 Docs: Getting Started with Auth0 Model Context Protocol Server
- Model Context Protocol Specification: Authorization, version 2025-11-25
- Model Context Protocol Blog: Evolving OAuth Client Registration in MCP
- IETF Datatracker: OAuth Client ID Metadata Document draft
- RFC 9728: OAuth 2.0 Protected Resource Metadata
- RFC 8414: OAuth 2.0 Authorization Server Metadata