Skip to main content
Version: 1.8.0

Authenticate MCP clients with OAuth 2.1

Use OAuth when an MCP client should act as an identified user instead of sharing a workspace API key. The DVARA MCP Gateway validates the access token, fixes the workspace boundary, enforces MCP scopes, and records the caller in the governance trail.

Enterprise only

Not included in DVARA Open Source.

OAuth is off by default. When it is off, MCP calls require a DVARA workspace API key. Turning it on accepts OAuth access tokens alongside API keys by default; set api-keys-enabled: false only after every MCP client has moved.

Configure one authorization server​

This example binds every accepted token from the issuer to one workspace. Use the claim-based form in the next section when one issuer serves several workspaces.

dvara:
mcp-gateway:
oauth:
enabled: true
resource: https://gateway.internal.example.com/mcp
authorization-servers:
- issuer: https://login.internal.example.com
workspace: claims-production
scopes-supported:
- mcp:read
- mcp:call
enforce-scopes: true
api-keys-enabled: true
require-pkce: true

resource is the MCP Gateway's canonical URI and must appear in the token's aud claim. issuer must match the token's iss claim exactly. When no explicit key-set URI is configured, DVARA discovers the issuer metadata and requires advertised PKCE support with S256 by default.

Restart the Gateway after changing these process settings.

Map tokens to workspaces and operations​

For a shared issuer, omit workspace and put the workspace ID in the token's dvara_workspace claim. Change workspace-claim if your issuer uses a different claim name. A token with neither an issuer binding nor the configured workspace claim is refused.

With enforce-scopes: true, which is the default, clients need:

ScopeOperations
mcp:readInitialize the session and list tools
mcp:callCall a tool

An OAuth token with no scopes is not unrestricted. This differs from an unscoped DVARA API key.

Verify discovery and a token​

After restart, fetch the unauthenticated protected-resource metadata:

curl --silent \
https://gateway.internal.example.com/.well-known/oauth-protected-resource/mcp

The response names the resource, authorization server, and supported scopes:

{
"resource": "https://gateway.internal.example.com/mcp",
"authorization_servers": ["https://login.internal.example.com"],
"scopes_supported": ["mcp:read", "mcp:call"],
"bearer_methods_supported": ["header"]
}

Then call tools/list with an access token:

curl --silent https://gateway.internal.example.com/mcp \
--header 'Authorization: Bearer <oauth-access-token>' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json, text/event-stream' \
--data '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list",
"params": {}
}'

Open Governance → Audit and inspect the resulting MCP event. OAuth calls record auth_method: oauth, the issuer, and the token subject; DVARA never records the token itself.

Diagnose rejected clients​

An invalid or expired token returns 401 with a WWW-Authenticate challenge that points to the protected-resource metadata. A valid token without the operation's scope returns 403 with error="insufficient_scope" and the required scope.

Use clients.allowed to restrict callers to known client IDs. An empty list accepts any client for which the configured issuer issued a valid token. For a gradual move away from Dynamic Client Registration, set clients.require-metadata-document: true to refuse clients that are neither allow-listed nor represented by a Client ID Metadata Document. The default is false.

Never pass the caller's access token through to an upstream MCP server. If an upstream uses delegated authorization, send a separate upstream token as described in Govern tool calls with the MCP Gateway.