Skip to main content
Every MCP server deployed on ezForge is protected by OAuth 2.1 with mandatory PKCE (Proof Key for Code Exchange). This ensures that only authorized clients — AI assistants, agents, or applications — can call your server’s tools. For background on how ezForge’s auth approach fits into the broader landscape of agentic auth patterns and how it compares to alternative implementation stacks, see Auth for AI Agents and MCP Servers: a landscape guide.

Client registration: CIMD vs DCR

ezForge supports two registration paths, matching the MCP Authorization Spec (2025-11 revision):
PathSpec levelHow it works
CIMD (Client ID Metadata Document)should — preferredClient sets client_id to its own metadata URL; the AS fetches and registers it automatically
DCR (RFC 7591 Dynamic Client Registration)may — fallbackClient POSTs metadata to /api/v1/servers/:id/clients; explicit call required

CIMD registration (preferred)

CIMD is the preferred path for clients that can host a metadata document at a stable HTTPS URL (Claude Desktop, Gemini ADK, Cursor, etc.). The client hosts a JSON document at a URL it controls, e.g.:
GET https://my-agent.example.com/.well-known/oauth-client-metadata
Content-Type: application/json
{
  "client_id": "https://my-agent.example.com/.well-known/oauth-client-metadata",
  "client_name": "My MCP Agent",
  "redirect_uris": ["https://my-agent.example.com/callback"],
  "grant_types": ["authorization_code"],
  "response_types": ["code"],
  "scope": "mcp:read mcp:write",
  "token_endpoint_auth_method": "none"
}
To begin the PKCE flow, the client uses its metadata URL as client_id:
GET /api/v1/oauth/authorize
  ?client_id=https://my-agent.example.com/.well-known/oauth-client-metadata
  &code_challenge=<S256>
  &code_challenge_method=S256
  &redirect_uri=https://my-agent.example.com/callback
  &scope=mcp:read mcp:write
  &resource=https://my-server.mcp.ezforge.ai
  &state=<random>
ezForge detects the URL-shaped client_id, fetches the metadata document, validates it, and registers the client automatically. No separate registration step is required. The metadata cache is refreshed every 24 hours using ETag-based conditional requests (RFC 7232). Metadata fetch failures degrade safely — the authorization request is rejected with a clear error and no token is issued. You can also pre-register a CIMD client explicitly (e.g. to validate the document ahead of the auth flow):
curl -X POST https://auth.ezforge.ai/api/v1/servers/{serverId}/clients \
  -H "Authorization: Bearer <session-token>" \
  -H "Content-Type: application/json" \
  -d '{ "clientMetadataUrl": "https://my-agent.example.com/.well-known/oauth-client-metadata" }'

DCR registration (fallback)

DCR (RFC 7591) remains fully supported for clients that cannot host a metadata URL. See Dynamic Client Registration below.

Why OAuth 2.1?

The Model Context Protocol specification recommends OAuth 2.1 as the standard auth mechanism for HTTP-based MCP servers. It provides:
  • Short-lived access tokens that limit the impact of token leakage
  • Refresh tokens for long-lived sessions without re-authentication
  • Fine-grained scopes to control what clients can do
  • PKCE to prevent authorization code interception attacks (mandatory — plain method not accepted)

How it works

MCP Client (AI Assistant)

         │  1. Discover auth metadata

GET /.well-known/oauth-protected-resource

         │  2. Redirect to authorization server

auth.ezforge.ai/oauth/authorize
    ?client_id=...
    &code_challenge=<S256-hash>
    &code_challenge_method=S256
    &redirect_uri=...
    &scope=mcp:execute

         │  3. User approves (or auto-approved for trusted clients)
         │  4. Auth code returned to redirect_uri

POST auth.ezforge.ai/oauth/token
    grant_type=authorization_code
    &code=...
    &code_verifier=<original-random>
    &resource=https://my-server.mcp.ezforge.ai

         │  5. Access token issued (bound to server URI)

GET /sse
    Authorization: Bearer <access-token>

Resource Indicators (RFC 8707)

Access tokens are bound to a specific server URI using RFC 8707 Resource Indicators. A token issued for my-server.mcp.ezforge.ai cannot be used to call any other server. This prevents token replay attacks across servers.

MCP scopes

ScopeDescription
mcp:readList available tools on the server
mcp:writeCall tools that modify state
mcp:executeExecute any tool call (most permissive)
offline_accessRequest long-lived refresh tokens

ezforge_managed mode

When a server uses ezforge_managed auth (the default), ezForge acts as the OAuth authorization server:
  • ezForge registers the MCP client
  • Handles the authorization flow
  • Issues and validates tokens
  • No configuration required from you

BYOA mode (Bring Your Own Auth)

If you already run an OAuth 2.1 authorization server, you can configure your ezForge server to accept tokens from it:
ezforge servers update my-server \
  --auth-mode byoa \
  --byoa-issuer https://auth.example.com \
  --byoa-jwks-uri https://auth.example.com/.well-known/jwks.json \
  --byoa-authorization-endpoint https://auth.example.com/oauth/authorize \
  --byoa-token-endpoint https://auth.example.com/oauth/token
ezForge validates incoming tokens by:
  1. Fetching your JWKS endpoint to get public keys
  2. Verifying the JWT signature
  3. Checking the iss claim matches your configured issuer
  4. Checking the aud claim includes the server’s URI (RFC 8707)
  5. Verifying the token is not expired

Choosing between ezforge_managed and BYOA

Both modes deliver the same MCP-spec compliance (OAuth 2.1, PKCE, RFC 8707 resource binding, RFC 9728 protected resource metadata). The choice is about who owns identity issuance.
Choose ezforge_managed if…Choose BYOA if…
You want a working spec-compliant MCP server in minutes, with zero auth configurationYou already operate an OAuth 2.1 authorization server you want to keep
You don’t yet have an enterprise identity providerYou have existing SSO/SCIM contracts (WorkOS, Auth0, Stytch/Twilio, etc.) you need to honor
You want ezForge to handle user accounts, token issuance, and JWKS rotationYou want auth audit logs and account lifecycle inside your own IdP
You’re an indie developer, SMB customer, or running a single-tenant deploymentYou have residency, isolation, or compliance constraints that require keeping identity in your own infrastructure
Both modes use the same ezforge servers update --auth-mode {ezforge_managed,byoa} command — see the BYOA mode section above for the BYOA-specific flags.

Protected Resource Metadata

All ezForge-hosted servers expose the RFC 9728 standard endpoint:
GET https://{slug}.mcp.ezforge.ai/.well-known/oauth-protected-resource
{
  "resource": "https://my-server.mcp.ezforge.ai",
  "authorization_servers": ["https://auth.ezforge.ai"],
  "scopes_supported": ["mcp:read", "mcp:write", "mcp:execute", "offline_access"],
  "bearer_methods_supported": ["header"]
}
This allows MCP clients to automatically discover how to authenticate — no manual configuration needed.

Token lifetimes

TokenLifetimeNotes
Authorization code10 minutesSingle-use; consumed on token exchange
Access token15 minutesShort-lived; limits blast radius of leakage
Refresh token30 daysRotated on each use

Security recommendations

  • Request only the scopes your application needs
  • Use offline_access only when a long-lived session is necessary
  • Treat access tokens as secrets — don’t log them or include them in URLs
  • Rotate refresh tokens regularly; ezForge rotates them automatically on use