Contact Us 1-800-596-4880

OAuth 2.0 Protected Resource Metadata Policy

Policy Name

OAuth 2.0 Protected Resource Metadata

Summary

Serves OAuth 2.0 Protected Resource Metadata at the well-known endpoint

Category

Security

First Omni Gateway version available

v1.13.0

Release Notes

OAuth 2.0 Protected Resource Metadata

Returned Status Codes

404 - Request path does not match the well-known endpoint

405 - HTTP method is not GET

400 - Registration request matches no configured client (Static Dynamic Client Registration only)

Summary

The OAuth Protected Resource Metadata policy serves a JSON document describing the protected resource’s OAuth 2.0 metadata at /.well-known/oauth-protected-resource as defined by RFC 9728.

This policy enables authorization servers and client applications to discover important OAuth 2.0 configuration details about your protected resource, such as:

  • Which authorization server tokens are accepted

  • What scopes are available for authorization

  • How to send bearer tokens (header, body, or query)

  • Whether DPoP or mTLS token binding is required

  • What URLs are available for resource documentation, policy, and terms of service

For identity providers (IdP) that don’t support support Dynamic Client Registration (DCR), the policy provides Static DCR configuration. Use it when your MCP clients require a registration_endpoint, but your IdP doesn’t support DCR. If your IdP supports DCR, or clients only need to discover your authorization server and scopes, static DCR configuration isn’t required.

This policy rejects all requests that aren’t made to the /.well-known/oauth-protected-resource route with a 404 Not Found response.

Configuring Policy Parameters

Omni Gateway Local Mode

The OAuth 2.0 Protected Resource Metadata policy doesn’t support Omni Gateway running in Local Mode.

Managed Omni Gateway and Omni Gateway Connected Mode

When you apply the policy from the UI, the following parameters are displayed:

Parameter Required or Optional Default Value Description

Resource Identifier

Required

The protected resource identifier (RFC 8707). Must be an https URL without a fragment. This value is returned as the resource field in the metadata response and is used by clients to validate the response matches the resource they intended to query.
Example: "https://api.example.com/v1"

Authorization Servers

Required

Array of OAuth 2.0 authorization server issuer identifier URLs (per RFC 8414) whose tokens are accepted by this protected resource. Enter multiple values separated by commas or on separate lines.
If you enable Static Dynamic Client Registration, these are the upstream servers whose metadata the policy fetches, and the response advertises the Consumer URL instead.
Example: ["https://auth.example.com"]

JWK Set URI

Optional

(omitted from response)

URL of the resource’s JWK Set document containing its public keys for signing resource responses. Must use https scheme.
Example: "https://api.example.com/.well-known/jwks.json"

Scopes Supported

Optional (recommended by RFC 9728)

(omitted from response)

Array of default OAuth 2.0 scope values that can be used to request access to this resource. This value is returned when the well-known path has no suffix or when the suffix doesn’t match any entry in Resource Scopes Overrides. Enter multiple values separated by commas or on separate lines.
Example: ["read", "write", "admin"]

Resource Scopes Overrides

Optional

[] (no overrides)

Array of objects mapping per-resource-path overrides for scopes_supported. Each entry maps a path suffix to a specific set of scopes. When a client requests /.well-known/oauth-protected-resource/<suffix>, the policy looks for a matching entry; if found, the entry’s scopes replace the default Scopes Supported in the response.
Each object contains:

* path (string, required) - The path suffix to match. Matched case-sensitively against the portion of the request path after /.well-known/oauth-protected-resource/.
* scopesSupported (array of strings, required) - The scopes to return for this resource path.

Bearer Methods Supported

Optional

(omitted from response)

Array of methods supported for sending bearer access tokens. Defined values are "header", "body", and "query" (per RFC 6750 Sections 2.1-2.3). An empty array means no bearer token methods are supported.
Example: ["header"]

Resource Signing Algorithm Values

Optional

(omitted from response)

Array of JWS alg values the resource uses to sign responses.
Example: ["RS256", "ES256"]

Resource Name

Optional (recommended by RFC 9728)

(omitted from response)

Human-readable name of the protected resource, suitable for display to end users.
Example: "Example API"

Resource Documentation URL

Optional

(omitted from response)

URL of a page containing human-readable developer documentation for the resource.
Example: "https://docs.example.com/api"

Resource Policy URI

Optional

(omitted from response)

URL of a page describing the resource’s data-use policy for clients.
Example: "https://example.com/privacy"

Resource Terms of Service URI

Optional

(omitted from response)

URL of a page with the resource’s terms of service.
Example: "https://example.com/tos"

TLS Client Certificate Bound Access Tokens

Optional

false (omitted from response when false)

Boolean indicating whether the resource requires mutual-TLS certificate-bound access tokens (RFC 8705).

Authorization Details Types Supported

Optional

(omitted from response)

Array of authorization details type values the resource supports (RFC 9396).
Example: ["payment_initiation"]

DPoP Signing Algorithm Values

Optional

(omitted from response)

Array of JWS alg values the resource accepts for DPoP proof JWTs (RFC 9449).
Example: ["ES256"]

DPoP Bound Access Tokens Required

Optional

false (omitted from response when false)

Boolean indicating whether DPoP-bound access tokens are always required by this resource.

Static Dynamic Client Registration

Optional

(disabled)

Object that makes an identity provider without Dynamic Client Registration (DCR) support appear DCR-capable to MCP clients. For the fields and behavior, see Static Dynamic Client Registration.

Resource Configuration Examples

Minimal Configuration

This example shows the minimum required configuration with basic scope and authorization server information:

- policyRef:
    name: oauth-protected-resource-metadata-policy-flex
  config:
    resource: "https://api.example.com"
    scopesSupported:
      - "read"
      - "write"
    authorizationServers:
      - "https://auth.example.com"

Full Configuration

This example demonstrates all available configuration parameters:

- policyRef:
    name: oauth-protected-resource-metadata-policy-flex
  config:
    resource: "https://api.example.com/v1"
    authorizationServers:
      - "https://auth.example.com"
      - "https://backup-auth.example.com"
    jwksUri: "https://api.example.com/.well-known/jwks.json"
    scopesSupported:
      - "read"
      - "write"
      - "admin"
    bearerMethodsSupported:
      - "header"
    resourceSigningAlgValuesSupported:
      - "RS256"
      - "ES256"
    resourceName: "Example API"
    resourceDocumentation: "https://docs.example.com/api"
    resourcePolicyUri: "https://example.com/privacy"
    resourceTosUri: "https://example.com/tos"
    tlsClientCertificateBoundAccessTokens: true
    authorizationDetailsTypesSupported:
      - "payment_initiation"
    dpopSigningAlgValuesSupported:
      - "ES256"
    dpopBoundAccessTokensRequired: true

Per-Resource Scope Overrides

This example shows how to configure different scopes for different resource paths:

- policyRef:
    name: oauth-protected-resource-metadata-policy-flex
  config:
    resource: "https://api.example.com"
    authorizationServers:
      - "https://auth.example.com"
    scopesSupported:
      - "read"
      - "write"
    resourceScopesOverrides:
      - path: "users"
        scopesSupported:
          - "users:read"
          - "users:write"
          - "users:delete"
      - path: "orders"
        scopesSupported:
          - "orders:read"
          - "orders:create"

Static Dynamic Client Registration

MCP clients register with an authorization server through its registration_endpoint (RFC 7591). If your IdP doesn’t support DCR, there is no registration_endpoint, and MCP clients can’t complete the OAuth flow.

When you enable Static Dynamic Client Registration, the policy:

  • Advertises the gateway’s public URL (the Consumer URL) as the authorization server.

  • Serves the IdP’s authorization server metadata with a registration_endpoint that points at the gateway.

  • Serves that endpoint at /.well-known/idp/register. It returns the credentials of a client you already created in the IdP, chosen by matching the request’s client_name.

The IdP still authorizes users and issues tokens.

All MCP clients that match the same entry receive the same client_id and client_secret. Use public clients without a secret, or trusted MCP clients, and limit what the client can access in the IdP.

Parameters

Configure these parameters under Static Dynamic Client Registration (staticDcr). You must also set Authorization Servers.

Parameter Required or Optional Default Value Description

Enable (enabled)

Optional

false

Turns the feature on.

Authorization Server Metadata Path (authorizationServerMetadataPath)

Optional

/.well-known/oauth-authorization-server

Path of the authorization server metadata. For OpenID Connect IdPs, use /.well-known/openid-configuration.

Consumer URL (consumerUrl)

Optional

Taken from API Manager

Public URL clients use to reach this API.

Static DCR Clients (clients)

Required

List of preconfigured clients. The policy uses the first entry whose name matches the request’s client_name. If none match, the request is rejected. Each entry contains:

* name (required) - Case-insensitive match on client_name. Use as a wildcard at the start or end, such as claude. Use * alone to match any name.
* clientId (required) - The client ID to return.
* clientSecret (optional) - The client secret to return. Omit it for public clients.

Static DCR Configuration Example

- policyRef:
    name: oauth-protected-resource-metadata-policy-flex
  config:
    resource: "https://api.example.com"
    authorizationServers:
      - "https://idp.example.com"
    staticDcr:
      enabled: true
      consumerUrl: "https://api.example.com"
      clients:
        - name: "claude*"
          clientId: "claude-mcp-client"
          clientSecret: "<CLAUDE_CLIENT_SECRET>"
        - name: "*"
          clientId: "shared-mcp-client"

Static DCR MCP Client Flow

First, create the OAuth clients in your IdP and apply the policy as shown in the example. Then an MCP client does the following:

  1. Requests /.well-known/oauth-protected-resource. The policy returns the gateway as the authorization server:

    {
      "resource": "https://api.example.com",
      "authorization_servers": ["https://api.example.com/"]
    }
  2. Requests /.well-known/oauth-authorization-server. The policy returns the IdP’s metadata with the gateway’s registration_endpoint:

    {
      "issuer": "https://idp.example.com",
      "authorization_endpoint": "https://idp.example.com/authorize",
      "token_endpoint": "https://idp.example.com/token",
      "registration_endpoint": "https://api.example.com/.well-known/idp/register"
    }
  3. Sends a registration request to /.well-known/idp/register:

    {
      "client_name": "Claude Desktop",
      "redirect_uris": ["https://client.example.com/callback"]
    }

    The name matches claude*, so the policy returns 201 Created with that client’s credentials:

    {
      "client_id": "claude-mcp-client",
      "client_secret": "<CLAUDE_CLIENT_SECRET>",
      "client_secret_expires_at": 0,
      "client_id_issued_at": 1719360000,
      "client_name": "Claude Desktop",
      "redirect_uris": ["https://client.example.com/callback"]
    }
  4. Signs the user in directly with the IdP using the returned client_id, then calls the API with the access token.

If no entry matches the client_name, the policy returns 400 Bad Request with the error invalid_client_metadata.

The policy fetches the IdP’s metadata on every authorization server metadata request and doesn’t cache it. If the policy can’t reach any IdP, the request returns 502 Bad Gateway. The registration endpoint doesn’t depend on the IdP.

Request Processing

  1. Path Matching: The policy checks whether the request path ends with /.well-known/oauth-protected-resource or contains /.well-known/oauth-protected-resource/ followed by a suffix. If the path doesn’t match, the policy returns 404 Not Found.

  2. Method Validation: If the path matches but the HTTP method is not GET, the policy returns 405 Method Not Allowed with an Allow: GET header.

  3. Scope Resolution: If the request path has a suffix after /.well-known/oauth-protected-resource/, the policy searches resourceScopesOverrides for an entry whose path matches the suffix exactly. If found, the entry’s scopesSupported replaces the top-level default in the response. If no override matches, the top-level scopesSupported is used.

  4. Metadata Response: For matching GET requests, the policy constructs a JSON object from the configured properties. For response examples, see Example Responses.

Property Mapping

The policy maps configuration parameters to JSON response fields using snake_case naming:

Configuration Parameter JSON Response Field

Resource Identifier

resource

Authorization Servers

authorization_servers

JWK Set URI

jwks_uri

Scopes Supported

scopes_supported

Bearer Methods Supported

bearer_methods_supported

Resource Signing Algorithm Values

resource_signing_alg_values_supported

Resource Name

resource_name

Resource Documentation URL

resource_documentation

Resource Policy URI

resource_policy_uri

Resource Terms of Service URI

resource_tos_uri

TLS Client Certificate Bound Access Tokens

tls_client_certificate_bound_access_tokens

Authorization Details Types Supported

authorization_details_types_supported

DPoP Signing Algorithm Values

dpop_signing_alg_values_supported

DPoP Bound Access Tokens Required

dpop_bound_access_tokens_required

Example Responses

These examples show the JSON metadata document the policy returns for a GET request to the well-known endpoint.

Minimal Configuration Response

When only resource, authorizationServers, and scopesSupported are configured, a GET request to /.well-known/oauth-protected-resource returns:

{
  "resource": "https://api.example.com",
  "authorization_servers": ["https://auth.example.com"],
  "scopes_supported": ["read", "write"]
}

Full Configuration Response

When all parameters are configured, a GET request to /.well-known/oauth-protected-resource returns all configured fields:

{
  "resource": "https://api.example.com/v1",
  "authorization_servers": ["https://auth.example.com", "https://backup-auth.example.com"],
  "jwks_uri": "https://api.example.com/.well-known/jwks.json",
  "scopes_supported": ["read", "write", "admin"],
  "bearer_methods_supported": ["header"],
  "resource_signing_alg_values_supported": ["RS256", "ES256"],
  "resource_name": "Example API",
  "resource_documentation": "https://docs.example.com/api",
  "resource_policy_uri": "https://example.com/privacy",
  "resource_tos_uri": "https://example.com/tos",
  "tls_client_certificate_bound_access_tokens": true,
  "authorization_details_types_supported": ["payment_initiation"],
  "dpop_signing_alg_values_supported": ["ES256"],
  "dpop_bound_access_tokens_required": true
}

Per-Resource Scope Overrides Responses

When resourceScopesOverrides maps the users path suffix to ["users:read", "users:write", "users:delete"] over a top-level default of ["read", "write"]:

  • A GET request to the default endpoint, /.well-known/oauth-protected-resource, returns the top-level scopes:

    {
      "resource": "https://api.example.com",
      "authorization_servers": ["https://auth.example.com"],
      "scopes_supported": ["read", "write"]
    }
  • A GET request to /.well-known/oauth-protected-resource/users matches the users override and returns its scopes:

    {
      "resource": "https://api.example.com",
      "authorization_servers": ["https://auth.example.com"],
      "scopes_supported": ["users:read", "users:write", "users:delete"]
    }
  • A GET request to an unconfigured suffix, such as /.well-known/oauth-protected-resource/billing, falls back to the top-level scopes:

    {
      "resource": "https://api.example.com",
      "authorization_servers": ["https://auth.example.com"],
      "scopes_supported": ["read", "write"]
    }

Edge Cases

  • Non-matching paths: Any request whose path doesn’t contain /.well-known/oauth-protected-resource receives 404 Not Found. The policy is intended to be route-scoped to the well-known path.

  • Trailing slash: /.well-known/oauth-protected-resource/ (with trailing slash) is treated as a match with an empty suffix. Since an empty string won’t match any resourceScopesOverrides entries, the top-level scopesSupported is returned.

  • Case sensitivity: Path matching is case-sensitive per RFC 9728 and RFC 8615.

  • Query parameters: Query strings on the well-known URL are ignored (the path is matched without query or fragment).

  • Empty bearerMethodsSupported: An explicitly configured empty array [] is serialized in the response (it signals "no bearer methods supported"), distinct from omitting the field entirely.