Getting started Community Training Tutorials Documentation APIs, AI & Tools
Resource Identifier
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 |
|
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) |
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.
|
The OAuth 2.0 Protected Resource Metadata policy doesn’t support Omni Gateway running in Local 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 |
|
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. |
|
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 |
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. |
Resource Scopes Overrides |
Optional |
|
Array of objects mapping per-resource-path overrides for |
Bearer Methods Supported |
Optional |
(omitted from response) |
Array of methods supported for sending bearer access tokens. Defined values are |
Resource Signing Algorithm Values |
Optional |
(omitted from response) |
Array of JWS |
Resource Name |
Optional (recommended by RFC 9728) |
(omitted from response) |
Human-readable name of the protected resource, suitable for display to end users. |
Resource Documentation URL |
Optional |
(omitted from response) |
URL of a page containing human-readable developer documentation for the resource. |
Resource Policy URI |
Optional |
(omitted from response) |
URL of a page describing the resource’s data-use policy for clients. |
Resource Terms of Service URI |
Optional |
(omitted from response) |
URL of a page with the resource’s terms of service. |
TLS Client Certificate Bound Access Tokens |
Optional |
|
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 |
DPoP Signing Algorithm Values |
Optional |
(omitted from response) |
Array of JWS |
DPoP Bound Access Tokens Required |
Optional |
|
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. |
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"
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
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"
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.
|
Configure these parameters under Static Dynamic Client Registration (staticDcr). You must also set Authorization Servers.
| Parameter | Required or Optional | Default Value | Description |
|---|---|---|---|
Enable ( |
Optional |
|
Turns the feature on. |
Authorization Server Metadata Path ( |
Optional |
|
Path of the authorization server metadata. For OpenID Connect IdPs, use |
Consumer URL ( |
Optional |
Taken from API Manager |
Public URL clients use to reach this API. |
Static DCR Clients ( |
Required |
List of preconfigured clients. The policy uses the first entry whose |
- 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"
First, create the OAuth clients in your IdP and apply the policy as shown in the example. Then an MCP client does the following:
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/"]
}
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"
}
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"]
}
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.
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.
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.
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.
Metadata Response: For matching GET requests, the policy constructs a JSON object from the configured properties. For response examples, see Example Responses.
The policy maps configuration parameters to JSON response fields using snake_case naming:
| Configuration Parameter | JSON Response Field |
|---|---|
Resource Identifier |
|
Authorization Servers |
|
JWK Set URI |
|
Scopes Supported |
|
Bearer Methods Supported |
|
Resource Signing Algorithm Values |
|
Resource Name |
|
Resource Documentation URL |
|
Resource Policy URI |
|
Resource Terms of Service URI |
|
TLS Client Certificate Bound Access Tokens |
|
Authorization Details Types Supported |
|
DPoP Signing Algorithm Values |
|
DPoP Bound Access Tokens Required |
|
These examples show the JSON metadata document the policy returns for a GET request to the well-known endpoint.
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"]
}
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
}
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"]
}
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.