openai / openai/codex

MCP OAuth: expected issuer taken from the resource URL instead of `authorization_servers`, rejecting conformant servers

Open
#40,885 5 comments 10 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

auth bug CLI mcp
Dominant language
Rust
Stars
125k
Forks
19.4k
PR merge metrics
PR metrics pending

Description

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

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at the codex mcp login OAuth flow and reproduce the failure with the GitLab MCP URL and the protected-resource metadata shown in the issue. Trace how the expected issuer is selected, then verify it against the authorization server's published issuer. Done means conformant GitLab and self-managed GitLab servers authenticate without an issuer mismatch, while genuinely disagreeing metadata is still rejected.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
authentication, cli, security
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.