Authenticate MCP clients with OAuth 2.1
Use OAuth when each MCP client or user needs a distinct identity in your governance evidence. The DVARA MCP Gateway checks who issued the token, which Gateway it targets, which workspace it belongs to, and which MCP operations it allows before a tool request runs.
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, which lets you migrate clients without interrupting them.
Configure an authorization server
This example binds every accepted token from one issuer to the
claims-production workspace:
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. Both values
must use HTTPS, except for a localhost development address.
When you do not set a key-set URI, DVARA first uses OAuth authorization-server
discovery and then OpenID Connect discovery. With require-pkce: true, the
default, discovered authorization metadata must advertise the S256 PKCE
method. Restart the Gateway after changing these process settings.
| Setting | Default | What it controls |
|---|---|---|
enabled | false | Whether the MCP Gateway accepts OAuth access tokens and publishes protected-resource metadata. |
enforce-scopes | true | Whether each MCP operation requires its corresponding OAuth scope. |
api-keys-enabled | true | Whether workspace API keys remain valid after OAuth is enabled. |
require-pkce | true | Whether discovered authorization-server metadata must advertise PKCE with S256. |
clients.allowed | Empty | Exact client IDs or HTTPS metadata-document prefixes that are allowed. An empty list does not restrict client IDs. |
clients.require-metadata-document | false | Whether a client must be allow-listed or use an HTTPS Client ID Metadata Document. |
clients.dcr-sunset | Not set | The date after which clients treated as dynamically registered are refused. |
Bind each token to a workspace
Use the workspace setting on an issuer when that issuer serves one DVARA
workspace. If the token also carries a workspace claim, it must match the
configured workspace.
For an issuer shared by several workspaces, omit workspace and put the
workspace ID in the token's dvara_workspace claim. Set workspace-claim when
your issuer uses a different claim name. DVARA refuses a token when neither an
issuer binding nor the configured claim identifies a workspace.
With scope enforcement enabled, clients need:
| Scope | Operations |
|---|---|
mcp:read | Initialize an MCP session and list tools. It also covers reads on the server-scoped REST surface. |
mcp:call | Call a tool. |
An OAuth token with no scopes is not unrestricted. That differs from a DVARA API key created without scopes.
Check what DVARA accepts
DVARA accepts an asymmetric signed JWT only. The token must have a valid
signature, sub, exp, matching iss, matching aud, and a usable workspace
binding. It refuses unsigned and HMAC-signed tokens, expired or not-yet-valid
tokens, and tokens sent in an access_token query parameter.
Fetch the protected-resource metadata without a credential:
curl --silent \
https://gateway.internal.example.com/.well-known/oauth-protected-resource/mcp
{
"resource": "https://gateway.internal.example.com/mcp",
"authorization_servers": ["https://login.internal.example.com"],
"scopes_supported": ["mcp:read", "mcp:call"],
"bearer_methods_supported": ["header"]
}
The metadata endpoint returns 404 while OAuth is disabled. When it is
enabled, clients can use the document to find the authorization server and ask
for the correct audience and scopes.
Now list tools 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": {}
}'
For a workspace with no visible tools, the JSON-RPC result is:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"tools": []
}
}
Open Governance → Audit and inspect the resulting MCP event. OAuth calls record the authentication method, issuer, subject, and available client identity. DVARA never records the access token.
Control which clients can connect
The token's client_id identifies the MCP client. DVARA also understands the
common azp, cid, and appid claims when client_id is absent.
Set clients.allowed when you have a fixed client inventory. Each entry is an
exact client ID or an HTTPS prefix ending in / for Client ID Metadata
Documents. Once the list is nonempty, DVARA refuses every client that does not
match it.
Without an allow-list, an HTTPS metadata-document client is accepted. Other client IDs are treated as dynamically registered. You can move away from that open posture in either of these ways:
- Set
clients.require-metadata-document: trueto refuse non-metadata clients immediately. - Set
clients.dcr-sunsetto a planned cutoff date. DVARA accepts those clients before the date and refuses them after it.
Move from API keys without downtime
Enable OAuth while leaving api-keys-enabled: true, which is the default.
Move one MCP client at a time, then verify its workspace, subject, client ID,
and scope in the audit trail. After every client uses OAuth, set
api-keys-enabled: false and restart the Gateway.
This setting applies only to the MCP Gateway. It does not disable workspace API keys for the LLM or A2A Gateway.
Resolve authentication failures
| Result | Meaning | What to check |
|---|---|---|
400 | The credential was placed in the query string. | Send Authorization: Bearer <token> instead. |
401 | The token is missing, invalid, expired, or does not match the issuer, audience, or workspace. | Follow the WWW-Authenticate link, then compare token claims with the resource metadata and issuer configuration. |
403 with insufficient_scope | The token is valid but lacks the operation's scope. | Request mcp:read or mcp:call as appropriate. |
| Client refused | The client fails the allow-list, metadata-document, or dynamic-registration cutoff rule. | Check the resolved client ID and your client controls. |
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.