MCP OAuth with Microsoft Entra fails when protected-resource metadata has a no-trailing-slash resource
Summary
codex mcp login cannot authenticate against the Microsoft Fabric Core MCP server because the emitted Microsoft Entra authorization URL sends a resource parameter with a trailing slash, while the requested Fabric scope identifies the same resource without one. Microsoft Entra rejects the request before the user can grant consent:
AADSTS9010010: The resource parameter provided in the request doesn't match with the requested scopes.
This is distinct from #17265, #13891, and #33403. Those reports concern missing/omitted resource or scope propagation; this report concerns an extra trailing slash in an emitted resource value.
Environment
- Codex CLI:
0.145.0 - OS: Windows
- MCP server:
https://api.fabric.microsoft.com/v1/mcp/core - OAuth client: pre-registered Microsoft Entra public client with PKCE
- Codex callback: configured local
localhostcallback (registered successfully) - No bearer token, client secret, certificate, service principal, or proxy
Fabric OAuth protected-resource metadata
{
"issuer": "https://login.microsoftonline.com/organizations/v2.0",
"authorization_servers": [
"https://login.microsoftonline.com/organizations/v2.0"
],
"resource": "https://api.fabric.microsoft.com",
"scopes_supported": [
"https://api.fabric.microsoft.com/.default"
]
}
The resource is deliberately advertised without a trailing slash.
Codex configuration
mcp_oauth_callback_port = 19877
mcp_oauth_callback_url = "http://localhost:19877/codex-mcp"
[mcp_servers.fabric-core]
url = "https://api.fabric.microsoft.com/v1/mcp/core"
scopes = ["https://api.fabric.microsoft.com/.default"]
enabled = true
[mcp_servers.fabric-core.oauth]
client_id = "<redacted-public-client-id>"
There is no oauth_resource configuration.
Reproduction
- Run
codex mcp login fabric-core. - Inspect the authorization URL printed by Codex.
- It contains the following effective values (state, PKCE challenge, client ID, and redirect URI redacted):
scope=https://api.fabric.microsoft.com/.default
resource=https://api.fabric.microsoft.com/
- Microsoft Entra immediately returns
AADSTS9010010.
Expected behavior
Codex must preserve the canonical protected-resource value from metadata and emit exactly one resource parameter:
scope=https://api.fabric.microsoft.com/.default
resource=https://api.fabric.microsoft.com
No trailing slash should be introduced.
The MCP authorization specification recommends a no-trailing-slash canonical resource form when the slash is not semantically significant:
https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization
Microsoft Fabric documents the same .default scope:
https://learn.microsoft.com/en-us/rest/api/fabric/articles/mcp-servers/core-remote/get-started-core
Suspected implementation area
In rust-v0.145.0, perform_oauth_login.rs delegates authorization URL construction to rmcp::AuthorizationManager; the subsequent Codex helper only appends an explicit oauth_resource value rather than replacing an existing query value:
https://github.com/openai/codex/blob/rust-v0.145.0/codex-rs/rmcp-client/src/perform_oauth_login.rs
This suggests the protected-resource URI is parsed/serialized as a URL and normalized to https://api.fabric.microsoft.com/, while the advertised scope remains https://api.fabric.microsoft.com/.default.
Requested fix
- Preserve the raw
resourcevalue returned by protected-resource metadata, including its trailing-slash semantics. - Ensure the authorization URL contains exactly one
resourceparameter. - If
oauth_resourceis configured, replace any existingresourceparameter instead of appending another one. - Forward the same exact resource value in authorization, token exchange, and refresh-token requests.
- Add a regression test with:
- metadata resource:
https://api.fabric.microsoft.com - scope:
https://api.fabric.microsoft.com/.default - assertion: exactly one
resource, no trailing slash.
This is blocking direct Fabric Core MCP authentication in both Codex CLI and Codex Desktop, which share the Codex MCP OAuth path.