Note: CIMD is currently an IETF Internet-Draft. Specifications may change before final adoption.

Back to Home

CIMD for Servers

Learn how authorization servers can securely consume Client ID Metadata Documents.

With CIMD, your authorization server can fetch client metadata just-in-time from the client_id URL. In most cases, you don't even need a client registry — just fetch the metadata in real time.

CIMD Integration Flow

1

Receive OAuth Request

Client sends authorization request with client_id as HTTPS URL

GET /authorize?client_id=https://client.dev/oauth/metadata.json&...
2

Fetch CIMD Document

Make HTTPS GET request to client_id URL to retrieve metadata

GET /oauth/metadata.json HTTP/1.1
Host: client.dev
Accept: application/json
3

Validate Schema & Content

Parse JSON, validate required fields, check redirect URIs

4

Enforce Policies

Apply security rules, rate limits, and organizational policies

Proceed with OAuth Flow

Continue with standard OAuth authorization flow using fetched metadata

Implementation Notes

Fetching & Caching

HTTPS Only

Never fetch metadata over HTTP - reject such client_ids immediately

Reasonable TTL

Cache metadata for 5-15 minutes to balance freshness with performance

Respect Cache-Control

Honor HTTP caching headers if provided by the client

Backoff on Failures

Implement exponential backoff when metadata fetches fail

Validation Requirements

Strict JSON Parsing

Reject malformed JSON immediately

Required Fields

Ensure client_id matches the fetched URL and redirect_uris is present

URI Validation

Validate all URIs are well-formed and use appropriate schemes

Redirect URI Security

Enforce HTTPS for redirect URIs and require exact matches (no wildcards)

Error Handling

Fetch Failures

If metadata fetch fails → return clear OAuth error response

{
  "error": "invalid_client",
  "error_description": "Unable to fetch client metadata from specified URL"
}

Validation Failures

If JSON is invalid or missing required fields → return descriptive error

{
  "error": "invalid_client_metadata",
  "error_description": "Client metadata missing required 'redirect_uris' field"
}

Retry Strategy

Consider implementing a retry window before permanent failure

Security Considerations

Critical: SSRF Protection

Fetching arbitrary URLs creates Server-Side Request Forgery (SSRF) risks. Implement these protections:

Network Restrictions

Allowlist egress to public IPv4/IPv6 only - block private ranges (10.0.0.0/8, 192.168.0.0/16, 127.0.0.0/8)

DNS Security

Resolve DNS once and pin IP for the request to prevent DNS rebinding attacks

Request Limits

Set tight timeouts (5-10s), limit content length (5KB), restrict redirects (max 3)

Dedicated Proxy

Use a separate egress proxy for metadata fetches, isolated from internal networks

Network Security

• Block link-local addresses (169.254.0.0/16)

• Reject file:// and other non-HTTP schemes

• Validate TLS certificates

• Use modern TLS versions only

Rate Limiting

• Limit metadata fetches per client_id

• Implement per-IP rate limiting

• Use exponential backoff on failures

• Monitor for abuse patterns

Operational Guidelines

Monitoring & Logging

Log All Fetch Outcomes

Success, failures, validation errors with correlation IDs

Track Performance Metrics

Response times, cache hit rates, error rates by client_id

Alert on Anomalies

High failure rates, slow responses, suspicious patterns

Management Tools

Manual Refresh

Admin tool to force re-fetch when clients update metadata

Cache Inspection

View cached metadata and expiration times

Debugging Support

Trace metadata fetch process for troubleshooting

Consent Screen Enhancement

Display client_uri on consent screens. Show trust warnings for localhost redirects.

Implementation Pseudocode

CIMD Processing Flow
async def process_cimd_client(client_id_url: str) -> ClientMetadata:
    # 1. Validate URL format
    if not client_id_url.startswith('https://'):
        raise InvalidClientError("client_id must be HTTPS URL")

    # 2. Check cache first
    cached_metadata = cache.get(client_id_url)
    if cached_metadata and not cached_metadata.is_expired():
        return cached_metadata

    # 3. Fetch with security protections
    try:
        # Apply SSRF protections
        validated_url = ssrf_validator.validate(client_id_url)

        # Fetch with timeouts and limits
        response = await http_client.get(
            validated_url,
            timeout=10,
            max_size=5120,  # 5KB limit
            follow_redirects=3
        )

        if response.content_type != 'application/json':
            raise InvalidClientMetadataError("Invalid Content-Type")

    except (TimeoutError, NetworkError) as e:
        # Log failure and return error
        logger.error(f"Failed to fetch CIMD",
                    client_id=client_id_url, error=str(e))
        raise InvalidClientError("Unable to fetch client metadata")

    # 4. Parse and validate JSON
    try:
        metadata = json.loads(response.body)
        validate_cimd_schema(metadata, client_id_url)
    except (JSONDecodeError, ValidationError) as e:
        logger.error(f"Invalid CIMD document",
                    client_id=client_id_url, error=str(e))
        raise InvalidClientMetadataError("Invalid metadata format")

    # 5. Cache and return
    cache.set(client_id_url, metadata, ttl=600)  # 10 minutes
    return ClientMetadata(metadata)

def validate_cimd_schema(metadata: dict, expected_client_id: str):
    # Ensure required fields
    if metadata.get('client_id') != expected_client_id:
        raise ValidationError("client_id mismatch")

    if not metadata.get('redirect_uris'):
        raise ValidationError("Missing redirect_uris")

    # Validate redirect URIs
    for uri in metadata['redirect_uris']:
        if not uri.startswith('https://'):
            raise ValidationError("Redirect URIs must use HTTPS")