MCP Authentication In drio
How MCP auth works in drio, which requests stay public, how the OAuth bridge works, and what to verify when protected tools fail.
This page explains the auth model for MCP apps hosted by drio.
Use it when you need to understand:
- which MCP requests are public
- when drio challenges an MCP client for sign-in
- how drio's OAuth bridge works
- what a published app must satisfy before MCP auth will work reliably
This is the MCP auth model for drio-hosted apps. It is separate from the management API auth flow described in Authentication.
The Model In One View
drio uses one server-wide OAuth configuration per published MCP app.
That means:
initializeis publictools/listis public- public tools can run without auth
- protected tools challenge on
tools/call - the OAuth metadata and OAuth endpoints are shared for the whole MCP server
What drio does not do anymore:
- it does not expose a different OAuth registration flow per tool
- it does not expect MCP clients to invent custom
adapter_idcalls - it does not require auth before a client can discover tools
So the distinction is:
- authorization is per tool
- authentication configuration is per server
Public Versus Protected Behavior
For a drio MCP server, the request behavior is:
| Request | Auth required? | Notes |
|---|---|---|
initialize | No | Always available so clients can connect and inspect the server |
tools/list | No | Clients can discover public and protected tools before sign-in |
tools/call for a public tool | No | The tool executes immediately |
tools/call for a protected tool | Yes | drio returns an MCP auth challenge when no bearer token is present |
This gives clients a usable discovery experience while still protecting business actions.
Host And Route Layout
Each published app has an MCP host. That can be:
https://{mcpId}.mcp.getdrio.com/mcp
https://customer.example.com/mcpThe same tenant host serves the auth discovery and bridge routes:
GET /.well-known/oauth-protected-resourceGET /.well-known/oauth-authorization-serverGET /.well-known/openid-configurationPOST /mcp/oauth/registerGET /mcp/oauth/authorizePOST /mcp/oauth/token
The upstream provider callback is brokered through drio's control-plane host:
https://www.getdrio.com/api/mcp/oauth/callback
What drio Publishes At Runtime
When an app is published, drio compiles the authoring model into runtime MCP config.
For auth, the important runtime rule is:
- a published MCP app can expose one effective delegated OAuth strategy
drio derives that from the published tools. If the app would require multiple non-equivalent delegated OAuth strategies, publish fails. That is intentional: the MCP server should present one consistent OAuth configuration to clients.
This applies to:
- tools that inherit delegated OAuth through their integration
- direct API tools configured with delegated OAuth
How The Challenge Works
If a client calls a protected tool without a bearer token, drio returns a 401
with standard MCP auth metadata.
The important part is the WWW-Authenticate value:
Bearer resource_metadata="https://{host}/.well-known/oauth-protected-resource"The same value is also included in the MCP error _meta.
Example response shape:
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32001,
"message": "Authentication required",
"data": {
"_meta": {
"mcp/www_authenticate": "Bearer resource_metadata=\"https://example.mcp.getdrio.com/.well-known/oauth-protected-resource\""
}
}
}
}The key point is that this challenge is server-wide. It is not tool-specific metadata.
The Discovery Flow
Once challenged, the MCP client should follow the advertised endpoints in the usual order:
- fetch
/.well-known/oauth-protected-resource - read the
authorization_serversentry - fetch
/.well-known/oauth-authorization-server - use the returned
registration_endpoint - use the returned
authorization_endpoint - exchange the returned code at the returned
token_endpoint
The client should use those endpoints as-is. It should not construct drio- specific variants.
The OAuth Bridge Flow
drio acts as an OAuth bridge between the MCP client and the configured upstream OAuth provider.
High-level flow:
sequenceDiagram autonumber participant Client as MCP client participant Drio as drio MCP host participant OAuth as Upstream OAuth provider participant API as Protected upstream API Client->>Drio: tools/call protected tool without token Drio-->>Client: 401 + resource_metadata Client->>Drio: GET /.well-known/oauth-protected-resource Client->>Drio: GET /.well-known/oauth-authorization-server Client->>Drio: POST /mcp/oauth/register Client->>Drio: GET /mcp/oauth/authorize Drio->>OAuth: Redirect user to authorize OAuth-->>Drio: GET https://www.getdrio.com/api/mcp/oauth/callback Drio-->>Client: Redirect back with authorization code Client->>Drio: POST /mcp/oauth/token Client->>Drio: Retry protected tools/call with Bearer token Drio->>API: Forward authenticated request API-->>Drio: Protected response Drio-->>Client: Tool result
What Each Route Does
GET /.well-known/oauth-protected-resource
This identifies the MCP server as the protected resource and tells the client which authorization server to use.
Typical response:
{
"resource": "https://example.mcp.getdrio.com/mcp",
"authorization_servers": ["https://example.mcp.getdrio.com"],
"bearer_methods_supported": ["header"],
"scopes_supported": ["openid", "profile", "email"]
}GET /.well-known/oauth-authorization-server
This advertises the OAuth endpoints for the MCP host.
Typical response:
{
"issuer": "https://example.mcp.getdrio.com",
"authorization_endpoint": "https://example.mcp.getdrio.com/mcp/oauth/authorize",
"token_endpoint": "https://example.mcp.getdrio.com/mcp/oauth/token",
"registration_endpoint": "https://example.mcp.getdrio.com/mcp/oauth/register",
"response_types_supported": ["code"],
"grant_types_supported": ["authorization_code", "refresh_token"],
"code_challenge_methods_supported": ["S256"],
"token_endpoint_auth_methods_supported": ["none"],
"scopes_supported": ["openid", "profile", "email"]
}/.well-known/openid-configuration is also exposed as a compatibility alias.
POST /mcp/oauth/register
This creates a local client registration for the MCP client and, when needed, registers an upstream public client with the delegated OAuth provider.
drio currently expects a public OAuth client flow:
authorization_coderefresh_tokentoken_endpoint_auth_method: none- PKCE
Example:
curl -X POST https://example.mcp.getdrio.com/mcp/oauth/register \
-H "Content-Type: application/json" \
-d '{
"client_name": "My MCP Client",
"redirect_uris": ["http://127.0.0.1:3333/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"token_endpoint_auth_method": "none",
"scope": "openid profile email"
}'GET /mcp/oauth/authorize
This starts the OAuth authorization flow for the already-registered MCP client.
drio validates the local client registration, then redirects the user to the configured upstream provider authorization endpoint.
POST /mcp/oauth/token
This exchanges the authorization code or refresh token against the upstream provider and proxies the token response back to the MCP client.
Supported grant types:
authorization_coderefresh_token
How Tokens Are Used
After token exchange, the MCP client retries the protected tools/call
request with:
Authorization: Bearer ACCESS_TOKENdrio then forwards that bearer token to the protected downstream path for the tool.
For the drio management API tools, that means the token is forwarded into the drio platform stack. For custom API tools, it is forwarded to the configured upstream API call.
What This Means For App Authors
If your app has both public and protected tools:
- the public tools still work before sign-in
- the protected tools all rely on the same server OAuth configuration
If your app needs multiple different delegated OAuth providers for different protected tools, that is not a good fit for one published MCP server in drio. You should simplify to one delegated OAuth strategy or split the surface into separate apps.
Verify The Flow With curl
1. Confirm discovery routes
curl -i https://{mcpId}.mcp.getdrio.com/.well-known/oauth-protected-resource
curl -i https://{mcpId}.mcp.getdrio.com/.well-known/oauth-authorization-serverExpected:
- both return
200 - the metadata points back to the same MCP host
2. Confirm public MCP discovery
curl -i https://{mcpId}.mcp.getdrio.com/mcp \
-H "Accept: application/json, text/event-stream" \
-H "Content-Type: application/json" \
--data '{
"jsonrpc":"2.0",
"id":1,
"method":"tools/list"
}'Expected:
200- no auth challenge
3. Confirm protected tool challenge
curl -i https://{mcpId}.mcp.getdrio.com/mcp \
-H "Accept: application/json, text/event-stream" \
-H "Content-Type: application/json" \
--data '{
"jsonrpc":"2.0",
"id":1,
"method":"tools/call",
"params":{
"name":"List apps",
"arguments":{"limit":5}
}
}'Expected:
401WWW-Authenticate: Bearer resource_metadata=".../.well-known/oauth-protected-resource"
Common Failure Modes
initialize or tools/list returns 401
That means the MCP server is still behaving like whole-server auth. In drio's intended model, those requests stay public.
Register succeeds, authorize fails immediately
That usually means the local client registration is invalid, expired, or no longer matches the published server auth config.
Tool challenge appears, but the protected tool still fails after sign-in
That usually means the bearer token is reaching the protected path, but a downstream service does not trust it. In drio-hosted tools, that often means a platform auth mismatch between the token issuer and the service validating the token.
Metadata works, but /mcp returns 406
The client is missing the required accept header. drio's MCP transport expects:
Accept: application/json, text/event-streamDesign Constraints To Keep In Mind
- one published MCP app exposes one effective delegated OAuth configuration
- public discovery stays available even when some tools are protected
- protected tool auth is triggered by
tools/call, not byinitialize - MCP clients should follow the advertised metadata endpoints directly
- drio's OAuth bridge is stateless from the MCP client's point of view