Skip to main content
Client ID Metadata Documents are an experimental feature — see Experimental Features for important caveats.
Traditional OAuth requires every client to be registered ahead of time — an operator creates a clientID, issues credentials, and records redirect URIs before the client can authenticate. This works for a fixed set of applications, but breaks down for the growing population of AI agents and MCP clients that appear dynamically and cannot be pre-provisioned. Client ID Metadata Documents (CIMD) solve this by letting a client identify itself with an https URL instead of a pre-registered ID. The client’s client_id is a URL that serves a JSON metadata document describing the client. When the Orchestrator receives an authorization request from an unknown URL client_id, it fetches that document, validates it, caches it, and builds a client from it on the fly — no manual registration required. This implements draft-ietf-oauth-client-id-metadata-document.
CIMD is the mechanism modern AI agents use to authenticate. For example, Claude Code presents its published metadata URL as its client_id and requests no scope — the Orchestrator resolves it to a public client and issues tokens without any per-client setup. See the AI Identity Gateway and MCP Proxy guides for related patterns.

How it works

An operator enables CIMD by adding one or more oidcCimd apps to an Orchestrator that already runs an OIDC Provider (the top-level oidcProvider configuration). Each app is a domain-scoped policy: it lists the domains whose metadata URLs it will resolve, and the authentication, authorization, and token settings to apply to clients from those domains. Once resolved, a CIMD client behaves like any other public or confidential OIDC client: it completes the authorization code flow (with PKCE) and receives tokens signed by the OIDC Provider. Resolved metadata is cached so subsequent requests from the same client skip the fetch.

The metadata document

The document served at the client_id URL is a JSON object using the client metadata fields defined by OAuth Dynamic Client Registration (RFC 7591) and profiled by draft-ietf-oauth-client-id-metadata-document. Refer to those specifications for the full field registry and semantics. The Orchestrator reads a subset of those fields — client_id, redirect_uris, grant_types, response_types, token_endpoint_auth_method, jwks_uri, scope, and client_name — and applies the constraints described in Client requirements below. A minimal metadata document for a public client looks like:
{
  "client_id": "https://app.example.com/oauth-client",
  "client_name": "Example Agent",
  "redirect_uris": ["https://app.example.com/callback"],
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "none"
}

Client requirements

The Orchestrator enforces the following rules. A metadata document that violates any of them makes the client unresolvable, and the authorization request is rejected as an invalid client. client_id URL — must use the https scheme, include a path, and must not contain dot-segments (. / ..), a fragment, userinfo, or a query string. The host must match one of the configured allowedDomains. redirect_uris — at least one is required. Each must use https, except that http is permitted for loopback hosts (localhost, 127.0.0.1, ::1) per RFC 8252. Loopback redirects may use any port. grant_types — only authorization_code and refresh_token are supported, and the field defaults to authorization_code when omitted. Declaring refresh_token requires authorization_code as well. Implicit, hybrid, client credentials, and other grants are rejected. response_types — only code (authorization code flow) is supported, and the field defaults to code when omitted. token_endpoint_auth_method — defaults to none when omitted:
  • none — the client is public and MUST use PKCE.
  • private_key_jwt — the client is confidential; its jwks_uri must be an https URL sharing the same origin (scheme + host + port) as the client_id. The Orchestrator fetches the JWKS to verify the client’s signed authentication assertions.
scope — optional. When present, requestable scopes are restricted to this set; when omitted, scopes are unrestricted (like a static client).
When a client declares the refresh_token grant, the Orchestrator automatically grants the offline_access scope so a refresh token is issued — even if the client requests no scope. This is required for agents such as Claude Code that send an empty scope but still need to refresh tokens without re-running the full authorization flow.

Configuration

CIMD is configured as one or more apps of type oidcCimd alongside the top-level oidcProvider configuration. Each oidcCimd app registers a single domain-scoped policy.
FieldTypeRequiredDescription
allowedDomainsstring[]yesDomains whose metadata URLs this policy resolves. Each entry is an exact hostname (app.example.com) or a wildcard prefix (*.example.com). A wildcard is only valid as the leftmost label.
authenticationobjectyesHow users are authenticated — an idps list, or isAuthenticatedSE + authenticateSE service extensions. Same shape as an oidc app.
authorizationobjectnoAuthorization policy applied to resolved clients. Defaults to allow-all.
allowedAudiencesstring[]noAudiences CIMD clients may request in access tokens.
claimsMappingmapnoMaps user attributes to token claims, namespaced by connector (e.g. email: azure.email).
accessTokenobjectnoAccess token type (jwt / opaque), length, and lifetimeSeconds.
refreshTokenobjectnoRefresh token length and lifetimeSeconds.
attrProvidersobject[]noConnectors used to load attributes for resolved clients.
loadAttrsSEservice extensionnoCustomizes attribute loading.
buildIDTokenClaimsSEservice extensionnoAdds custom claims to the ID token.
buildAccessTokenClaimsSEservice extensionnoAdds custom claims to the access token.

Example

An OIDC Provider with a single CIMD policy that resolves any client under *.example.com and authenticates users against an Azure connector:
maverics.yaml
# The OIDC Provider is configured under the top-level `oidcProvider` key.
oidcProvider:
  discovery:
    issuer: https://sso.company.com
    endpoints:
      wellKnown: /.well-known/openid-configuration
      auth: /oauth2/authorize
      token: /oauth2/token
      jwks: /oauth2/jwks
      userinfo: /oauth2/userinfo
  jwks:
    - algorithm: RSA256
      privateKey: <oidc-signing-key>

# CIMD policies are registered as apps of type `oidcCimd`.
apps:
  - name: partner-agents
    type: oidcCimd
    # Resolve CIMD client_id URLs under these domains.
    allowedDomains:
      - "*.example.com"
    authentication:
      idps:
        - azure
    allowedAudiences:
      - "https://api.company.com"
    accessToken:
      type: jwt
      lifetimeSeconds: 3600
    refreshToken:
      lifetimeSeconds: 2592000

Multiple policies

You can register several oidcCimd apps to apply different templates to different domains. When more than one policy could match a host, the most specific domain pattern wins: an exact hostname outranks any wildcard, and a wildcard with more labels outranks one with fewer. Exact-duplicate domain strings across policies are rejected at startup.
maverics.yaml
apps:
  # Broad policy for all partner subdomains.
  - name: partner-agents
    type: oidcCimd
    allowedDomains:
      - "*.example.com"
    authentication:
      idps:
        - azure

  # More specific policy that wins for this exact host, with stricter settings.
  - name: privileged-agent
    type: oidcCimd
    allowedDomains:
      - "agent.example.com"
    authentication:
      idps:
        - azure
    # Stricter authorization: only members of the privileged group may use this client.
    authorization:
      rules:
        - and:
            - contains: ["{{ azure.groups }}", "privileged-agents"]
    accessToken:
      lifetimeSeconds: 300

Security and caching

Because the Orchestrator fetches a client-supplied URL, CIMD metadata retrieval is hardened against server-side request forgery (SSRF):
  • Special-use IP blocking — resolved IPs are checked against RFC 6890 special-use ranges (loopback, private, link-local, etc.) and rejected. The connection is made to the validated IP to prevent DNS rebinding.
  • No redirects — the metadata fetch does not follow HTTP redirects.
  • Bounded requests — a 5-second timeout and a ~5 KB response size cap, and the response must be application/json.
  • Domain allowlist — only hosts matching a policy’s allowedDomains are fetched at all.
Resolved metadata is cached to avoid re-fetching on every request:
  • The cache TTL is derived from the response’s Cache-Control: max-age, clamped to a minimum of 5 minutes and a maximum of 24 hours.
  • Up to 1000 metadata documents are cached per policy.
  • When a cached document is refreshed and a security-relevant field changes (token_endpoint_auth_method, jwks_uri, or redirect_uris), the change is logged.

Discovery

When at least one oidcCimd policy is registered, the OIDC Provider advertises CIMD support in its discovery (well-known) document:
  • client_id_metadata_document_supported is set to true.
  • token_endpoint_auth_methods_supported gains none and private_key_jwt (the methods CIMD honors) alongside the static client_secret_post.
With no CIMD policies configured, the discovery document is unchanged and client_id_metadata_document_supported is false.

Examples

The following oidcCimd app configurations allow common AI clients to authenticate using CIMD without pre-registering a client ID. In each case, the client presents an https URL as its client_id; the Orchestrator matches the host against allowedDomains, fetches the metadata document, and completes the authorization code flow automatically.
Both Claude Code and Claude Desktop present a metadata URL on claude.ai as the client_id. A single oidcCimd policy covers both.
maverics.yaml
apps:
  - name: claude
    type: oidcCimd
    allowedDomains:
      - claude.ai
    authentication:
      idps:
        - upstream-idp
    accessToken:
      type: jwt
      lifetimeSeconds: 3600
    refreshToken:
      lifetimeSeconds: 86400
    claimsMapping:
      email: upstream-idp.email
      name: upstream-idp.name
      sub: upstream-idp.sub
When Claude Desktop connects via mcp-remote as a local stdio proxy, CIMD does not apply — the client ID is a plain string, not a URL, and must be registered as a standard OIDC app. See Connect Claude to the Gateway for mcp-remote configuration.

Experimental Features

Overview of all experimental features and important caveats

OIDC Provider

Configure the Orchestrator as an OIDC authorization server

AI Identity Gateway

Secure AI agent-to-tool communication via MCP

Connect Claude

Connect Claude Code and other Anthropic clients