Summary
altinity-mcp should replace Dynamic Client Registration (DCR) with OAuth Client ID Metadata Documents (CIMD) for MCP OAuth client metadata.
DCR is not widely used in current altinity-mcp deployments. It has mainly been exercised on a small set of stage/dev servers for interoperability testing with ChatGPT and Claude. Current testing shows both ChatGPT and Claude support CIMD. The corporate Auth0 tenant also has DCR disabled, so relying on DCR does not match the production authorization environment.
The proposed direction is:
- support CIMD as the only dynamic MCP OAuth client metadata mechanism;
- advertise CIMD support in authorization server metadata;
- remove DCR support immediately;
- do not advertise
registration_endpoint;
- reject calls to any remaining DCR registration route if the route still exists during cleanup;
- define strict URL, metadata, cache, trust, redirect, logging, and SSRF requirements for CIMD;
- make the authorization-code replay model explicit for HA deployments without shared storage.
Background
MCP clients and MCP authorization servers need a way to exchange OAuth client metadata, especially client_id, client_name, redirect_uris, and token endpoint authentication method, when the client and server have no prior relationship.
Historically, altinity-mcp supported this through DCR. With DCR, the client calls a registration endpoint, submits metadata, and receives a server-issued client identifier. This requires the MCP server to expose and maintain a registration surface.
CIMD uses a different model. The OAuth client_id itself is an HTTPS URL. That URL points to a JSON metadata document owned by the client. The authorization server fetches the document, validates it, and uses it as the client registration metadata for the OAuth flow.
The MCP authorization specification lists CIMD as the preferred dynamic mechanism for clients and authorization servers. DCR is optional and included for backwards compatibility with earlier versions of the MCP authorization spec.
DCR vs CIMD
| Area |
DCR |
CIMD |
| Client identity |
Server issues a client identifier after registration. |
Client identifier is a stable HTTPS URL controlled by the client. |
| Metadata delivery |
Client POSTs metadata to a registration endpoint. |
Authorization server fetches metadata from the client_id URL. |
| Server write surface |
Requires a registration endpoint. |
No registration endpoint required. |
| Client lifecycle |
Server may need to manage registered clients, expiration, cleanup, and abuse controls. |
Metadata is resolved on demand and cached. |
| HA / stateless operation |
More stateful unless registration data is encoded or shared. |
Fits stateless and multi-replica deployments, but authorization-code replay still needs an explicit model. |
| Production fit for Altinity |
Corporate Auth0 tenant has DCR disabled. |
Matches current MCP direction and tested ChatGPT/Claude behavior. |
| MCP direction |
Optional backwards-compatibility path. |
Preferred path for clients with no prior relationship. |
| Main risk |
Registration endpoint abuse and lifecycle complexity. |
SSRF risk from fetching attacker-controlled URLs. |
Proposal
Use CIMD as the default and only dynamic mechanism for inbound MCP OAuth clients.
DCR should be deprecated and removed immediately. There should be no new compatibility flag for DCR unless a concrete production client requires it later.
This issue is intentionally written as requirements rather than implementation detail. The implementation can choose the internal resolver, cache, and code organization as long as it satisfies the requirements below.
Requirements
Authorization server metadata
The authorization server metadata should advertise CIMD support and the token endpoint authentication methods supported by altinity-mcp:
{
"issuer": "https://mcp.example.com",
"authorization_endpoint": "https://mcp.example.com/oauth/authorize",
"token_endpoint": "https://mcp.example.com/oauth/token",
"client_id_metadata_document_supported": true,
"response_types_supported": ["code"],
"grant_types_supported": ["authorization_code"],
"token_endpoint_auth_methods_supported": ["none"],
"code_challenge_methods_supported": ["S256"]
}
Requirements:
none is required for public CIMD clients using PKCE.
- Shared-secret token endpoint authentication methods must not be advertised for CIMD.
registration_endpoint must not be advertised.
client_id_metadata_document_supported: true must be advertised.
code_challenge_methods_supported must include S256.
- Any remaining DCR registration route must return a clear unsupported/removed error until the route is deleted.
DCR removal
DCR should be removed in the same change or explicitly disabled with no supported configuration path.
Requirements:
- Do not advertise DCR in OAuth authorization server metadata.
- Do not mount
/oauth/register as a working endpoint.
- Do not issue new server-minted DCR
client_id values.
- Remove DCR documentation, except for a migration note saying CIMD replaced DCR.
- Remove DCR tests, except negative tests that verify DCR is not exposed.
- Remove DCR metrics after the route is deleted.
- Remove or deprecate DCR-specific configuration such as custom registration-path settings.
CIMD client identifier URL validation
altinity-mcp should accept a client_id as a CIMD metadata URL only when all of the following are true:
- the
client_id parses as an absolute URL;
- the scheme is exactly
https;
- the URL has a hostname;
- the URL has a non-empty path component;
- the URL has no fragment;
- the URL has no username or password component;
- the URL has no query string in v1;
- the URL length is below a configured maximum, with a default maximum of 2048 characters;
- the port is allowed by policy;
- the hostname is allowed by policy;
- the path contains no single-dot or double-dot path segments;
- the path contains no encoded single-dot or double-dot path segments;
- the path contains no normalization edge cases that would cause two different strings to identify the same effective path.
Dot-segment and normalization rules
The implementation must reject dot-segment variants before fetching the metadata document.
Reject examples include:
/./client.json
/../client.json
/a/./client.json
/a/../client.json
/%2e/client.json
/%2E/client.json
/%2e%2e/client.json
/%2E%2E/client.json
/%2e./client.json
/.%2e/client.json
Validation requirements:
- Parse the URL using a standards-compliant URL parser.
- Do not normalize the path and then accept the normalized result.
- Split the raw path into path segments.
- Strictly percent-decode each segment for validation.
- Reject malformed percent encodings.
- Reject any raw or decoded segment equal to
. or ...
- Reject encoded slash and encoded backslash in path segments, such as
%2f, %2F, %5c, and %5C, to avoid segment-boundary ambiguity.
- Reject paths where applying standard dot-segment removal would change the path.
- Use the original exact
client_id string for metadata client_id comparison and cache keying.
Query string policy
CIMD says client identifier URLs should not include a query string. For v1, altinity-mcp should reject query strings completely.
Requirements:
- Reject any CIMD
client_id URL with a non-empty query component.
- Do not support query-string based metadata routing in v1.
- Do not log full request URLs that could contain sensitive query strings.
- OAuth
redirect_uri values may contain query strings only when they are part of the registered redirect URI and are matched according to the redirect URI policy below.
Trust policy
CIMD allows unknown clients, but altinity-mcp still needs an explicit production trust policy.
Production default
Production default should be open internet for CIMD metadata hostnames after strict SSRF validation.
This preserves the main CIMD value: MCP clients can connect without prior registration. SSRF controls, metadata validation, token endpoint authentication, and redirect policies provide the baseline safety checks.
Optional hostname allowlist
Operators may configure an allowlist to restrict accepted CIMD client_id hostnames.
Suggested configuration shape:
ALTINITY_MCP_CIMD_ALLOWED_HOSTS=
ALTINITY_MCP_CIMD_ALLOWED_PORTS=443
ALTINITY_MCP_CIMD_TRUSTED_LOOPBACK_REDIRECT_HOSTS=
ALTINITY_MCP_CIMD_DEV_ALLOW_SPECIAL_USE_IPS=false
Behavior:
- Empty
ALTINITY_MCP_CIMD_ALLOWED_HOSTS means public internet hostnames are allowed after SSRF validation.
- Non-empty
ALTINITY_MCP_CIMD_ALLOWED_HOSTS means only matching hostnames are allowed.
- Hostnames are normalized to lower-case ASCII using IDNA before comparison.
- Exact host matches are allowed.
- Wildcards are allowed only as the entire left-most label, for example
*.example.com.
*.example.com matches a.example.com but not example.com and not a.b.example.com.
- Wildcards must not be allowed for public suffixes such as
*.com.
- Partial wildcards such as
*example.com, api.*.example.com, and example.* are rejected.
- CIDR ranges are not accepted in hostname allowlists; IP filtering belongs to SSRF validation.
Allowed ports
Production default should allow only port 443 for CIMD metadata URLs.
Behavior:
- URL with no explicit port is treated as port
443.
- URL with explicit port
443 is allowed.
- Any other port is rejected unless explicitly listed in
ALTINITY_MCP_CIMD_ALLOWED_PORTS.
- Stage/dev may add non-standard HTTPS ports explicitly.
- HTTP is never allowed for CIMD metadata URLs.
Dev/stage overrides
Dev/stage may enable controlled exceptions, but they must be explicit.
Requirements:
- Special-use IP ranges remain rejected by default.
- Loopback/private/link-local CIMD metadata URLs are allowed only if
ALTINITY_MCP_CIMD_DEV_ALLOW_SPECIAL_USE_IPS=true.
- When dev special-use IPs are enabled, restrict them to local development deployments and log a startup warning.
- The override must not be enabled by default in production images, Helm charts, or example production configs.
Failure mode
- If hostname, port, trust, or SSRF validation fails, abort the authorization request.
- Return an OAuth-compatible error to the client.
- Log a redacted structured reason for operators.
- Do not fall back to DCR.
Metadata fetch requirements
The metadata document is fetched only after URL, trust, and SSRF validation succeeds.
Requirements:
- Use HTTP
GET only.
- Require HTTP
200 OK.
- Do not automatically follow redirects.
- Require JSON content.
- Enforce a small body size limit, default 5 KiB.
- Enforce a decompressed-size limit if compression is accepted.
- Use a short timeout.
- Send no cookies.
- Send no authorization headers.
- Send no client credentials.
- Send no ambient headers from the inbound user request.
- Do not use environment-configured HTTP proxies.
Metadata schema validation
The fetched metadata document must be treated as untrusted input.
Minimum required fields:
client_id
client_name
redirect_uris
token_endpoint_auth_method
Validation requirements:
- The document must be a JSON object.
- Duplicate JSON object keys should be rejected where the JSON parser supports detection.
- Unknown fields may be ignored after total document size and field size limits are enforced.
client_id must be a string.
client_id must exactly equal the original requested client_id string using simple string comparison.
client_name must be a non-empty string.
client_name must have a bounded length, suggested maximum 128 characters.
redirect_uris must be a non-empty array of strings.
redirect_uris must have a bounded length, suggested maximum 20 entries.
- Each redirect URI string must have a bounded length, suggested maximum 2048 characters.
- Duplicate redirect URIs should be rejected.
grant_types, if present, must include authorization_code and must not require unsupported grant types.
response_types, if present, must include code and must not require unsupported response types.
token_endpoint_auth_method must be explicitly present.
token_endpoint_auth_method must be none.
- Omitted
token_endpoint_auth_method is rejected because RFC 7591 defines the omitted default as client_secret_basic.
- Shared-secret methods are rejected for CIMD, including
client_secret_basic, client_secret_post, and client_secret_jwt.
client_secret and client_secret_expires_at are rejected in CIMD metadata.
- Client assertions and
private_key_jwt are not supported in v1 and must be rejected if requested.
- Unsupported fields must not influence security decisions.
Redirect URI policy
Redirect URI validation must be strict.
Requirements:
- Non-loopback redirect URIs must use
https.
- Non-loopback redirect URI matching must be exact string matching against the validated metadata document.
http redirect URIs are allowed only for loopback redirect hosts.
- Loopback redirect hosts are limited to
localhost, 127.0.0.1, and [::1].
- Loopback redirects are accepted in production only when the CIMD
client_id hostname is trusted for loopback redirects.
- Trusted loopback redirect hostnames are configured separately from the general CIMD hostname allowlist.
- If loopback redirects are accepted, the consent or approval surface must clearly display the redirect URI hostname.
- If all registered redirect URIs are loopback redirects, show an additional warning where a user-facing consent surface exists.
- If there is no user-facing consent or approval surface, reject loopback redirects unless the client ID hostname is explicitly trusted.
- Query strings in redirect URIs are allowed only when registered and matched according to the exact redirect policy.
Loopback redirect port handling
For compatibility with native clients, loopback redirect port handling may be port-agnostic only under a narrow policy.
Requirements:
-
For non-loopback redirect URIs, the port must match exactly.
-
For loopback redirect URIs, the port may vary only if:
- the registered redirect URI is loopback;
- the scheme, host, path, and query match policy;
- the client ID hostname is trusted for loopback redirects;
- the implementation documents the port matching behavior.
-
Do not support wildcard redirect paths.
-
Do not support wildcard redirect hostnames.
Client trust and consent display
Open-internet CIMD means the authorization server may see previously unknown clients. The user-facing approval or consent surface must therefore avoid presenting dynamic clients as implicitly trusted.
Requirements:
- Display the CIMD
client_id hostname or full origin to the user where an approval surface exists.
- Display the selected
redirect_uri hostname where an approval surface exists.
- Do not fetch or inline remote
logo_uri, client_uri, tos_uri, or similar optional display assets in v1.
- If there is no user-facing approval surface, loopback redirects must require explicit trust configuration.
- If there is no user-facing approval surface and open-internet CIMD is enabled, document the risk and provide an operator allowlist option.
Public CIMD clients
When token_endpoint_auth_method is none:
- the client is treated as a public client;
- the authorization-code flow must use PKCE;
- PKCE method must be
S256 in v1;
- the token request must not include a client secret;
- the token request must not include a client assertion.
Resource indicator policy
MCP clients are expected to use OAuth resource indicators. altinity-mcp should bind the requested resource into the authorization decision.
Requirements:
/authorize should require a resource parameter identifying this MCP protected resource, unless a temporary compatibility mode is explicitly enabled.
/token must reject a resource parameter that conflicts with the resource bound at /authorize.
- The exact resource decision must be bound into the authorization code.
- Resource comparison may tolerate configured trailing-slash normalization, but the bound value must be deterministic and documented.
- Token audience binding remains a separate issue, but this change must not weaken existing resource checks.
Authorization-code binding
CIMD metadata can change between /authorize and /token. The authorization decision must therefore be bound to the authorization code.
At /authorize, after resolving and validating CIMD metadata, bind the following to the pending authorization state and to the issued authorization code:
- exact
client_id string;
- validated metadata decision record, not only raw JSON;
- metadata hash, if useful for logging/debugging;
- metadata fetch time and cache decision;
- selected
token_endpoint_auth_method;
redirect_uri used in the authorization request;
- PKCE
code_challenge;
- PKCE
code_challenge_method;
- requested
resource;
- requested scopes;
- loopback redirect trust decision, if applicable;
- consent or approval decision, if applicable.
At /token, validate that:
- the code was issued to the same exact
client_id;
- the token request uses the same
redirect_uri;
- the PKCE verifier matches the bound challenge;
- the token endpoint authentication method matches the bound metadata decision;
- no client secret or client assertion is supplied for public CIMD clients;
- the code has not expired;
- the code replay policy below is satisfied;
- the requested
resource, if supplied at /token, matches the authorization decision.
The token endpoint must not make a less restrictive decision by re-fetching changed metadata. If metadata is re-fetched at /token, the new metadata must not broaden the authorization-code decision.
HA authorization-code replay model
altinity-mcp should not claim strict local single-use authorization-code enforcement while also requiring no shared storage across replicas. Without shared storage, a stateless JWE authorization code can be redeemed more than once within its TTL if the redeemer has the code and PKCE verifier.
For HA deployments without shared storage, use upstream authorization-code redemption as the replay boundary.
Required model for the brokered upstream OAuth flow:
-
Do not exchange the upstream authorization code in /oauth/callback.
-
In /oauth/callback, issue a downstream authorization code as a short-lived JWE.
-
The downstream authorization-code JWE must contain:
- upstream authorization code;
- upstream PKCE verifier;
- exact CIMD
client_id;
- validated CIMD metadata decision record;
- downstream
redirect_uri;
- downstream PKCE challenge and method;
- requested resource;
- requested scopes;
- issue time and expiration time.
-
In /oauth/token, after validating the downstream client, redirect URI, PKCE verifier, resource, and metadata decision, exchange the upstream authorization code with the upstream IdP.
-
Treat upstream invalid_grant on repeated redemption as the replay protection result.
-
Do not return the same upstream bearer again from a replayed downstream authorization code.
-
Keep the downstream authorization-code TTL short, suggested maximum 60 seconds.
Tradeoff:
- If
/oauth/token successfully exchanges the upstream code but the response is lost before the client receives it, a retry may fail with invalid_grant. This is acceptable and more OAuth-compliant than replaying the same authorization code successfully.
If altinity-mcp later supports a fully self-issued authorization-server mode without an upstream stateful authorization server, strict single-use authorization-code enforcement requires one of:
- shared storage or consensus for used-code tracking;
- sticky sessions plus local replay cache, documented as weaker and not globally strict;
- stateless short-TTL mitigation, documented as not strict OAuth single-use.
Only shared storage or an upstream stateful authorization server provides strict cross-replica single-use semantics.
Refresh-token policy
Refresh-token behavior for public CIMD clients must be explicit.
Requirements:
- Do not advertise
refresh_token in grant_types_supported for CIMD v1 unless refresh-token replay/reuse behavior is designed and tested.
- If refresh tokens are issued to public CIMD clients, they must either be sender-constrained or rotated with replay/reuse detection by a stateful authority.
- If altinity-mcp only wraps upstream refresh tokens, rely on upstream refresh-token rotation/reuse detection where available and document that dependency.
- If upstream refresh-token rotation/reuse detection is not available, prefer not issuing refresh tokens to public CIMD clients in v1.
- Refresh-token JWE claims must bind the exact
client_id, selected resource/audience decision, scope, issuer, and expiration.
Caching
Valid CIMD metadata documents may be cached.
Requirements:
- Cache key is the exact original
client_id string.
- Cache values must include the validated metadata and validation decision, not just raw JSON.
- Respect HTTP cache headers where safe.
- Honor
Cache-Control: no-store by not storing the metadata beyond the current request.
- Treat
Cache-Control: no-cache as requiring refetch before reuse.
- If no usable cache headers are present, use a short default TTL, suggested 5 minutes.
- Cap positive cache TTL, suggested maximum 1 hour.
- Do not use stale metadata for new authorization decisions after TTL expiry.
- Do not cache error responses as valid metadata.
- Do not cache invalid or malformed metadata as valid metadata.
- Failed fetches may use short negative caching only for abuse control, suggested maximum 30 seconds.
- Negative cache entries must not override an existing unexpired valid metadata cache entry.
- Cache entries must be bounded by count and total memory size.
- Cache behavior must not require cross-pod coordination.
SSRF risk
CIMD requires the authorization server to fetch a URL supplied by the OAuth client as client_id. Without strict controls, this can turn altinity-mcp into an SSRF probe.
Examples of unsafe targets an attacker could try to reach:
localhost services;
- Kubernetes API endpoints;
- cloud instance metadata services;
- private RFC1918 addresses;
- link-local addresses such as
169.254.169.254;
- IPv6 loopback, link-local, and unique-local addresses;
- internal DNS names;
- services reachable only from the altinity-mcp network.
The metadata fetcher must therefore be treated as a security boundary, not as a generic HTTP client.
SSRF-safe fetcher and dialer
The implementation should use a dedicated CIMD/JWKS fetcher, not a generic default HTTP client.
Requirements:
-
Disable environment proxy use.
-
Resolve DNS explicitly.
-
Validate all resolved A and AAAA records before connecting.
-
Reject special-use IP ranges, including:
- loopback;
- private RFC1918 IPv4 ranges;
- link-local;
- multicast;
- unspecified addresses;
- IPv6 loopback;
- IPv6 link-local;
- IPv6 unique-local;
- carrier-grade NAT and other non-public ranges where applicable.
-
Pin the connection to a validated IP address through the outbound dial.
-
Keep TLS SNI as the original hostname.
-
Keep the HTTP Host header as the original hostname.
-
Re-check the connected remote address before sending the HTTP request.
-
Do not allow internal DNS names to bypass IP validation.
-
Reject redirects.
-
Enforce body and decompressed-size limits.
-
Bound concurrent outbound metadata/JWKS fetches.
-
Rate-limit repeated failed fetches by hostname and/or source client where practical.
-
Avoid retry storms.
Optional metadata URLs
Optional metadata URLs are untrusted.
Requirements:
- Do not fetch
logo_uri, client_uri, tos_uri, or similar optional URLs in v1.
- If optional URLs are displayed, display them as external untrusted URLs.
- If optional URL fetching is added later, it must use the same SSRF-safe fetcher and must be separately threat-modeled.
Error handling and logging
Invalid CIMD metadata should fail closed.
Errors should be clear enough for operators and client developers to diagnose:
- unsupported scheme;
- query string not allowed;
- dot segment not allowed;
- unsupported port;
- hostname not allowed;
- blocked address;
- fetch timeout;
- redirect response;
- oversized response;
- non-JSON response;
- missing required field;
- invalid field type;
client_id mismatch;
- redirect URI mismatch;
- unsupported token endpoint authentication method;
- unsupported client assertion or client secret for public CIMD;
- invalid or replayed authorization code;
- upstream
invalid_grant during delegated authorization-code redemption.
Logging requirements:
- Use structured logs.
- Redact authorization codes, access tokens, refresh tokens, client assertions, client secrets, and full query strings.
- Log the exact reason category.
- Log the normalized hostname and policy decision.
- Do not log full JWT assertions.
- Do not log full upstream token responses.
Acceptance criteria
- CIMD public clients can complete the MCP OAuth authorization-code flow with PKCE using
token_endpoint_auth_method: "none".
- Authorization server metadata advertises
client_id_metadata_document_supported: true.
- Authorization server metadata advertises
token_endpoint_auth_methods_supported: ["none"].
- Authorization server metadata advertises
code_challenge_methods_supported including S256.
- Authorization server metadata does not advertise
registration_endpoint.
- Authorization server metadata does not advertise shared-secret client authentication methods for CIMD.
- DCR registration is removed or returns a clear unsupported/removed error during cleanup.
- CIMD
client_id URLs with query strings are rejected.
- CIMD
client_id URLs with dot segments or encoded dot segments are rejected.
- Invalid CIMD metadata fails closed.
- Metadata schema validation covers required fields, field types, field sizes, duplicate redirect URIs, and unsupported auth methods.
- Client secrets, client assertions, and
private_key_jwt are rejected for CIMD v1.
- Authorization codes are bound to the validated CIMD metadata decision record.
- HA authorization-code replay behavior is implemented by deferring upstream authorization-code exchange to
/oauth/token, or another explicitly documented design with equivalent cross-replica single-use semantics.
- Replaying a downstream authorization code after successful upstream redemption does not return the same bearer again.
- Redirect URI validation implements the explicit non-loopback and loopback policies above.
- Resource indicator validation binds the selected resource into the authorization decision.
- Refresh-token behavior for public CIMD clients is either disabled in v1 or explicitly backed by upstream rotation/reuse detection or another stateful replay-control mechanism.
- SSRF tests cover localhost, private IPs, link-local IPs, IPv6 local ranges, internal DNS names, redirects, oversized responses, non-JSON responses, encoded path edge cases, blocked ports, proxy bypass, and DNS rebinding-style cases where feasible.
- Cache tests cover
max-age, no-store, no-cache, missing cache headers, TTL cap, negative caching, and exact client_id cache keys.
- Logs and metrics distinguish CIMD success, CIMD validation failure, CIMD fetch failure, SSRF rejection, unsupported client authentication, authorization-code replay/upstream
invalid_grant, and DCR removed/unsupported route usage.
- Documentation explains that CIMD replaced DCR.
Non-goals
- Do not redesign the full OAuth authorization flow beyond the CIMD and replay requirements in this issue.
- Do not add DPoP support in this issue.
- Do not add shared-secret CIMD client authentication.
- Do not add
private_key_jwt support in this issue.
- Do not fetch optional display assets such as
logo_uri in v1.
- Do not fully solve token audience binding here; that should remain a separate OAuth/resource-indicator issue.
- Do not introduce mandatory shared storage for metadata caching.
References
Summary
altinity-mcp should replace Dynamic Client Registration (DCR) with OAuth Client ID Metadata Documents (CIMD) for MCP OAuth client metadata.
DCR is not widely used in current altinity-mcp deployments. It has mainly been exercised on a small set of stage/dev servers for interoperability testing with ChatGPT and Claude. Current testing shows both ChatGPT and Claude support CIMD. The corporate Auth0 tenant also has DCR disabled, so relying on DCR does not match the production authorization environment.
The proposed direction is:
registration_endpoint;Background
MCP clients and MCP authorization servers need a way to exchange OAuth client metadata, especially
client_id,client_name,redirect_uris, and token endpoint authentication method, when the client and server have no prior relationship.Historically, altinity-mcp supported this through DCR. With DCR, the client calls a registration endpoint, submits metadata, and receives a server-issued client identifier. This requires the MCP server to expose and maintain a registration surface.
CIMD uses a different model. The OAuth
client_iditself is an HTTPS URL. That URL points to a JSON metadata document owned by the client. The authorization server fetches the document, validates it, and uses it as the client registration metadata for the OAuth flow.The MCP authorization specification lists CIMD as the preferred dynamic mechanism for clients and authorization servers. DCR is optional and included for backwards compatibility with earlier versions of the MCP authorization spec.
DCR vs CIMD
client_idURL.Proposal
Use CIMD as the default and only dynamic mechanism for inbound MCP OAuth clients.
DCR should be deprecated and removed immediately. There should be no new compatibility flag for DCR unless a concrete production client requires it later.
This issue is intentionally written as requirements rather than implementation detail. The implementation can choose the internal resolver, cache, and code organization as long as it satisfies the requirements below.
Requirements
Authorization server metadata
The authorization server metadata should advertise CIMD support and the token endpoint authentication methods supported by altinity-mcp:
{ "issuer": "https://mcp.example.com", "authorization_endpoint": "https://mcp.example.com/oauth/authorize", "token_endpoint": "https://mcp.example.com/oauth/token", "client_id_metadata_document_supported": true, "response_types_supported": ["code"], "grant_types_supported": ["authorization_code"], "token_endpoint_auth_methods_supported": ["none"], "code_challenge_methods_supported": ["S256"] }Requirements:
noneis required for public CIMD clients using PKCE.registration_endpointmust not be advertised.client_id_metadata_document_supported: truemust be advertised.code_challenge_methods_supportedmust includeS256.DCR removal
DCR should be removed in the same change or explicitly disabled with no supported configuration path.
Requirements:
/oauth/registeras a working endpoint.client_idvalues.CIMD client identifier URL validation
altinity-mcp should accept a
client_idas a CIMD metadata URL only when all of the following are true:client_idparses as an absolute URL;https;Dot-segment and normalization rules
The implementation must reject dot-segment variants before fetching the metadata document.
Reject examples include:
/./client.json/../client.json/a/./client.json/a/../client.json/%2e/client.json/%2E/client.json/%2e%2e/client.json/%2E%2E/client.json/%2e./client.json/.%2e/client.jsonValidation requirements:
.or...%2f,%2F,%5c, and%5C, to avoid segment-boundary ambiguity.client_idstring for metadataclient_idcomparison and cache keying.Query string policy
CIMD says client identifier URLs should not include a query string. For v1, altinity-mcp should reject query strings completely.
Requirements:
client_idURL with a non-empty query component.redirect_urivalues may contain query strings only when they are part of the registered redirect URI and are matched according to the redirect URI policy below.Trust policy
CIMD allows unknown clients, but altinity-mcp still needs an explicit production trust policy.
Production default
Production default should be open internet for CIMD metadata hostnames after strict SSRF validation.
This preserves the main CIMD value: MCP clients can connect without prior registration. SSRF controls, metadata validation, token endpoint authentication, and redirect policies provide the baseline safety checks.
Optional hostname allowlist
Operators may configure an allowlist to restrict accepted CIMD
client_idhostnames.Suggested configuration shape:
Behavior:
ALTINITY_MCP_CIMD_ALLOWED_HOSTSmeans public internet hostnames are allowed after SSRF validation.ALTINITY_MCP_CIMD_ALLOWED_HOSTSmeans only matching hostnames are allowed.*.example.com.*.example.commatchesa.example.combut notexample.comand nota.b.example.com.*.com.*example.com,api.*.example.com, andexample.*are rejected.Allowed ports
Production default should allow only port
443for CIMD metadata URLs.Behavior:
443.443is allowed.ALTINITY_MCP_CIMD_ALLOWED_PORTS.Dev/stage overrides
Dev/stage may enable controlled exceptions, but they must be explicit.
Requirements:
ALTINITY_MCP_CIMD_DEV_ALLOW_SPECIAL_USE_IPS=true.Failure mode
Metadata fetch requirements
The metadata document is fetched only after URL, trust, and SSRF validation succeeds.
Requirements:
GETonly.200 OK.Metadata schema validation
The fetched metadata document must be treated as untrusted input.
Minimum required fields:
client_idclient_nameredirect_uristoken_endpoint_auth_methodValidation requirements:
client_idmust be a string.client_idmust exactly equal the original requestedclient_idstring using simple string comparison.client_namemust be a non-empty string.client_namemust have a bounded length, suggested maximum 128 characters.redirect_urismust be a non-empty array of strings.redirect_urismust have a bounded length, suggested maximum 20 entries.grant_types, if present, must includeauthorization_codeand must not require unsupported grant types.response_types, if present, must includecodeand must not require unsupported response types.token_endpoint_auth_methodmust be explicitly present.token_endpoint_auth_methodmust benone.token_endpoint_auth_methodis rejected because RFC 7591 defines the omitted default asclient_secret_basic.client_secret_basic,client_secret_post, andclient_secret_jwt.client_secretandclient_secret_expires_atare rejected in CIMD metadata.private_key_jwtare not supported in v1 and must be rejected if requested.Redirect URI policy
Redirect URI validation must be strict.
Requirements:
https.httpredirect URIs are allowed only for loopback redirect hosts.localhost,127.0.0.1, and[::1].client_idhostname is trusted for loopback redirects.Loopback redirect port handling
For compatibility with native clients, loopback redirect port handling may be port-agnostic only under a narrow policy.
Requirements:
For non-loopback redirect URIs, the port must match exactly.
For loopback redirect URIs, the port may vary only if:
Do not support wildcard redirect paths.
Do not support wildcard redirect hostnames.
Client trust and consent display
Open-internet CIMD means the authorization server may see previously unknown clients. The user-facing approval or consent surface must therefore avoid presenting dynamic clients as implicitly trusted.
Requirements:
client_idhostname or full origin to the user where an approval surface exists.redirect_urihostname where an approval surface exists.logo_uri,client_uri,tos_uri, or similar optional display assets in v1.Public CIMD clients
When
token_endpoint_auth_methodisnone:S256in v1;Resource indicator policy
MCP clients are expected to use OAuth resource indicators. altinity-mcp should bind the requested resource into the authorization decision.
Requirements:
/authorizeshould require aresourceparameter identifying this MCP protected resource, unless a temporary compatibility mode is explicitly enabled./tokenmust reject aresourceparameter that conflicts with the resource bound at/authorize.Authorization-code binding
CIMD metadata can change between
/authorizeand/token. The authorization decision must therefore be bound to the authorization code.At
/authorize, after resolving and validating CIMD metadata, bind the following to the pending authorization state and to the issued authorization code:client_idstring;token_endpoint_auth_method;redirect_uriused in the authorization request;code_challenge;code_challenge_method;resource;At
/token, validate that:client_id;redirect_uri;resource, if supplied at/token, matches the authorization decision.The token endpoint must not make a less restrictive decision by re-fetching changed metadata. If metadata is re-fetched at
/token, the new metadata must not broaden the authorization-code decision.HA authorization-code replay model
altinity-mcp should not claim strict local single-use authorization-code enforcement while also requiring no shared storage across replicas. Without shared storage, a stateless JWE authorization code can be redeemed more than once within its TTL if the redeemer has the code and PKCE verifier.
For HA deployments without shared storage, use upstream authorization-code redemption as the replay boundary.
Required model for the brokered upstream OAuth flow:
Do not exchange the upstream authorization code in
/oauth/callback.In
/oauth/callback, issue a downstream authorization code as a short-lived JWE.The downstream authorization-code JWE must contain:
client_id;redirect_uri;In
/oauth/token, after validating the downstream client, redirect URI, PKCE verifier, resource, and metadata decision, exchange the upstream authorization code with the upstream IdP.Treat upstream
invalid_granton repeated redemption as the replay protection result.Do not return the same upstream bearer again from a replayed downstream authorization code.
Keep the downstream authorization-code TTL short, suggested maximum 60 seconds.
Tradeoff:
/oauth/tokensuccessfully exchanges the upstream code but the response is lost before the client receives it, a retry may fail withinvalid_grant. This is acceptable and more OAuth-compliant than replaying the same authorization code successfully.If altinity-mcp later supports a fully self-issued authorization-server mode without an upstream stateful authorization server, strict single-use authorization-code enforcement requires one of:
Only shared storage or an upstream stateful authorization server provides strict cross-replica single-use semantics.
Refresh-token policy
Refresh-token behavior for public CIMD clients must be explicit.
Requirements:
refresh_tokeningrant_types_supportedfor CIMD v1 unless refresh-token replay/reuse behavior is designed and tested.client_id, selected resource/audience decision, scope, issuer, and expiration.Caching
Valid CIMD metadata documents may be cached.
Requirements:
client_idstring.Cache-Control: no-storeby not storing the metadata beyond the current request.Cache-Control: no-cacheas requiring refetch before reuse.SSRF risk
CIMD requires the authorization server to fetch a URL supplied by the OAuth client as
client_id. Without strict controls, this can turn altinity-mcp into an SSRF probe.Examples of unsafe targets an attacker could try to reach:
localhostservices;169.254.169.254;The metadata fetcher must therefore be treated as a security boundary, not as a generic HTTP client.
SSRF-safe fetcher and dialer
The implementation should use a dedicated CIMD/JWKS fetcher, not a generic default HTTP client.
Requirements:
Disable environment proxy use.
Resolve DNS explicitly.
Validate all resolved A and AAAA records before connecting.
Reject special-use IP ranges, including:
Pin the connection to a validated IP address through the outbound dial.
Keep TLS SNI as the original hostname.
Keep the HTTP
Hostheader as the original hostname.Re-check the connected remote address before sending the HTTP request.
Do not allow internal DNS names to bypass IP validation.
Reject redirects.
Enforce body and decompressed-size limits.
Bound concurrent outbound metadata/JWKS fetches.
Rate-limit repeated failed fetches by hostname and/or source client where practical.
Avoid retry storms.
Optional metadata URLs
Optional metadata URLs are untrusted.
Requirements:
logo_uri,client_uri,tos_uri, or similar optional URLs in v1.Error handling and logging
Invalid CIMD metadata should fail closed.
Errors should be clear enough for operators and client developers to diagnose:
client_idmismatch;invalid_grantduring delegated authorization-code redemption.Logging requirements:
Acceptance criteria
token_endpoint_auth_method: "none".client_id_metadata_document_supported: true.token_endpoint_auth_methods_supported: ["none"].code_challenge_methods_supportedincludingS256.registration_endpoint.client_idURLs with query strings are rejected.client_idURLs with dot segments or encoded dot segments are rejected.private_key_jwtare rejected for CIMD v1./oauth/token, or another explicitly documented design with equivalent cross-replica single-use semantics.max-age,no-store,no-cache, missing cache headers, TTL cap, negative caching, and exactclient_idcache keys.invalid_grant, and DCR removed/unsupported route usage.Non-goals
private_key_jwtsupport in this issue.logo_uriin v1.References