MCP OAuth: expected issuer taken from the resource URL instead of `authorization_servers`, rejecting conformant servers
What happened?
codex mcp login rejects an MCP server whose OAuth discovery metadata is RFC 9728 conformant, because the expected issuer is taken from the protected resource's own URL instead of the authorization_servers value that document advertises.
Error: Authorization server issuer mismatch: expected https://gitlab.com/api/v4/mcp, received https://gitlab.com
https://gitlab.com is the correct issuer. Codex expected https://gitlab.com/api/v4/mcp, which is the resource identifier, not an authorization-server issuer.
Evidence that the server metadata is conformant
Both documents are public, no auth required:
$ curl -s https://gitlab.com/.well-known/oauth-protected-resource/api/v4/mcp
{"resource":"https://gitlab.com/api/v4/mcp",
"authorization_servers":["https://gitlab.com"],
"scopes_supported":["mcp"]}
$ curl -s https://gitlab.com/.well-known/oauth-authorization-server | jq .issuer
"https://gitlab.com"
So authorization_servers[0] == the authorization server's issuer == https://gitlab.com. RFC 9728 §3.1 defines authorization_servers as a list of authorization-server issuer identifiers, and RFC 8414 §3.3 requires the issuer in the metadata to match the one used for discovery. Both hold here. There is nothing for Codex to reject.
How this differs from #38944
#38944 is about Meta's endpoints, where authorization_servers[0] is https://mcp.facebook.com/ads while the authorization server publishes issuer: https://www.facebook.com. Those genuinely disagree, so Codex is right to reject Meta, and a per-server issuer override is a reasonable escape hatch for a non-conformant server.
GitLab is the opposite case: the metadata agrees with itself, and Codex rejects it anyway. That points at Codex reading the expected issuer from the wrong field rather than at the server. A trusted-issuer override would paper over it, but the underlying comparison is what needs fixing — otherwise every conformant server whose issuer is not byte-identical to its resource URL stays broken, which is the normal shape for any MCP endpoint mounted on a path.
A comment on #38944 reports the same GitLab error, but under that issue's framing it reads as another non-conformant server. Filing separately so the distinction is not lost.
Steps to reproduce
codex mcp add gitlab --url https://gitlab.com/api/v4/mcp
codex mcp login gitlab
Reproduced identically on a self-managed GitLab 19.2.2 instance, whose metadata is the same shape.
Expected behaviour
Resolve the expected issuer from authorization_servers[] in the protected-resource metadata, then validate the authorization server's issuer against that value. Only report a mismatch when those two disagree.
Why there is no workaround
The GitLab MCP endpoint advertises scopes_supported: ["mcp"], and mcp is not among the personal-access-token scopes (api, read_api, read_repository, write_repository, read_registry, write_registry, self_rotate, read_virtual_registry, write_virtual_registry, create_runner, manage_runner, ai_features, k8s_proxy, admin_mode, read_service_ping, sudo, read_user). A PAT with full api scope authenticates against /api/v4/user (200) and is refused by /api/v4/mcp (403). So --bearer-token-env-var is not an alternative path, and OAuth is the only one — which this bug closes.
For contrast, other MCP clients that resolve the issuer from authorization_servers connect to the same endpoint without trouble.
Codex version
0.149.1
What platform is your computer?
macOS 26.6 / arm64
1 Comment
Same issue here. Hoping for a workaround.