Decide whether the MCP origin serves a copy of MAS's RFC 8414 document, a redirect, or nothing #148

Open
opened 2026-09-02 12:04:07 +00:00 by jlxq0 · 0 comments
Owner

#94 adds two unauthenticated routes that serve an RFC 8414 authorization-server metadata document from this server's own origin, hand-built from cfg.authorization_server. It is mechanically green. The decision it waits on is whether this origin should serve that document at all, and that decision is this issue rather than a line in a PR comment.

What is measured

MAS's own document, fetched from https://matrixauthservice.kampong.social/.well-known/oauth-authorization-server, against what #94 emits with the deployment's live env:

field                              MAS                                        #94
issuer                             https://matrixauthservice.kampong.social/  https://matrix-mcp.kampong.social
authorization_endpoint             …/authorize                                match
token_endpoint                     …/oauth2/token                             match
registration_endpoint              …/oauth2/registration                      match
code_challenge_methods_supported   plain, S256                                S256
grant_types_supported              authorization_code, refresh_token,         authorization_code,
                                   client_credentials, device_code            refresh_token

And what the origin does today, with #94 not deployed:

/.well-known/oauth-protected-resource/mcp   200
/.well-known/oauth-authorization-server     401
  WWW-Authenticate: Bearer resource_metadata="…/.well-known/oauth-protected-resource/mcp", scope="…"

Three findings, in the order they bite

issuer names this server rather than MAS. RFC 8414 §2 defines it as the authorization server's identifier. A client that records it and then checks the RFC 9207 iss on the authorization response compares matrixauthservice.kampong.social/ against matrix-mcp.kampong.social and must reject the code, after the user has authenticated at MAS. The trailing slash is part of the value: MATRIX_MCP_AUTHORIZATION_SERVER is set with one, and authorization_server_keeps_its_trailing_slash already exists to preserve it, so the correct value is cfg.authorization_server verbatim rather than the authorization_server_base() form the endpoints use.

Naming MAS instead does not make it compliant. RFC 8414 §3.3 has the client check that the issuer matches the one it derived the retrieval URL from, and that URL is this origin. So both candidate values fail a strict client, one at retrieval and one after authentication. The route is a compatibility shim for MCP clients predating the 2025-06-18 move to RFC 9728, and it cannot be made compliant, only less wrong. The current code comment says the opposite of RFC 8414 §2 and would teach the next reader the inverted rule.

A hand-built copy of another service's document has no mechanism keeping it in step, and this one already disagrees on two fields. Both disagreements are narrowings and safe in themselves, which is what makes them the wrong thing to focus on. The three endpoint paths match exactly today; that is a measurement of one afternoon rather than a property, and registration_endpoint is where a future mismatch costs most, because asserting it means a client doing dynamic registration POSTs there and connection setup fails outright.

The options

  • 302 to MAS's own document. Cannot drift, gives a lenient client MAS's real issuer, fails a strict client in exactly the same place the copy does, and is fewer lines than the struct it replaces.
  • Keep the copy for a client that will not follow a redirect, with issuer set to cfg.authorization_server verbatim and a test that fetches MAS's document and compares the fields, so drift is a red build rather than a client's problem.
  • Neither, and close #94. The compliant path already answers, and the 401 already carries the pointer to it.

Whichever is chosen, the /mcp variant needs dropping or justifying: its retrieval URL implies issuer …/mcp, which the document does not return under either candidate value.

Where this came from

Cross-engine review found the issuer fault and the §3.3 consequence, asked as can this new code do the wrong thing in the case it was written for rather than as a general review. The drift measurement and the comparison against the live MAS document are mine. Codex also called registration_endpoint unsafe if MAS lacks RFC 7591, which does not hold here: MAS advertises it at exactly the constructed path.

`#94` adds two unauthenticated routes that serve an RFC 8414 authorization-server metadata document from this server's own origin, hand-built from `cfg.authorization_server`. It is mechanically green. **The decision it waits on is whether this origin should serve that document at all**, and that decision is this issue rather than a line in a PR comment. ## What is measured MAS's own document, fetched from `https://matrixauthservice.kampong.social/.well-known/oauth-authorization-server`, against what `#94` emits with the deployment's live env: field MAS #94 issuer https://matrixauthservice.kampong.social/ https://matrix-mcp.kampong.social authorization_endpoint …/authorize match token_endpoint …/oauth2/token match registration_endpoint …/oauth2/registration match code_challenge_methods_supported plain, S256 S256 grant_types_supported authorization_code, refresh_token, authorization_code, client_credentials, device_code refresh_token And what the origin does today, with `#94` not deployed: /.well-known/oauth-protected-resource/mcp 200 /.well-known/oauth-authorization-server 401 WWW-Authenticate: Bearer resource_metadata="…/.well-known/oauth-protected-resource/mcp", scope="…" ## Three findings, in the order they bite **`issuer` names this server rather than MAS.** RFC 8414 §2 defines it as the authorization server's identifier. A client that records it and then checks the RFC 9207 `iss` on the authorization response compares `matrixauthservice.kampong.social/` against `matrix-mcp.kampong.social` and must reject the code, **after the user has authenticated at MAS**. The trailing slash is part of the value: `MATRIX_MCP_AUTHORIZATION_SERVER` is set with one, and `authorization_server_keeps_its_trailing_slash` already exists to preserve it, so the correct value is `cfg.authorization_server` verbatim rather than the `authorization_server_base()` form the endpoints use. **Naming MAS instead does not make it compliant.** RFC 8414 §3.3 has the client check that the issuer matches the one it derived the retrieval URL from, and that URL is this origin. So both candidate values fail a strict client, one at retrieval and one after authentication. **The route is a compatibility shim for MCP clients predating the 2025-06-18 move to RFC 9728, and it cannot be made compliant, only less wrong.** The current code comment says the opposite of RFC 8414 §2 and would teach the next reader the inverted rule. **A hand-built copy of another service's document has no mechanism keeping it in step, and this one already disagrees on two fields.** Both disagreements are narrowings and safe in themselves, which is what makes them the wrong thing to focus on. The three endpoint paths match exactly today; that is a measurement of one afternoon rather than a property, and `registration_endpoint` is where a future mismatch costs most, because asserting it means a client doing dynamic registration POSTs there and connection setup fails outright. ## The options - **302 to MAS's own document.** Cannot drift, gives a lenient client MAS's real issuer, fails a strict client in exactly the same place the copy does, and is fewer lines than the struct it replaces. - **Keep the copy** for a client that will not follow a redirect, with `issuer` set to `cfg.authorization_server` verbatim and a test that fetches MAS's document and compares the fields, so drift is a red build rather than a client's problem. - **Neither**, and close `#94`. The compliant path already answers, and the 401 already carries the pointer to it. Whichever is chosen, the `/mcp` variant needs dropping or justifying: its retrieval URL implies issuer `…/mcp`, which the document does not return under either candidate value. ## Where this came from Cross-engine review found the `issuer` fault and the §3.3 consequence, asked as *can this new code do the wrong thing in the case it was written for* rather than as a general review. The drift measurement and the comparison against the live MAS document are mine. Codex also called `registration_endpoint` unsafe if MAS lacks RFC 7591, which **does not hold here**: MAS advertises it at exactly the constructed path.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
jlxq0/matrix-mcp#148
No description provided.