agentic-community / agentic-community/mcp-gateway-registry
oauth_user egress requires client_secret, so a public PKCE client cannot be configured — intentional?
- Langage dominant
- Python
- Étoiles
- 911
- Forks
- 234
- Merge moyen
- 1 j 11 h
- PR mergées (30 j)
- 62
Description
## Question
Configuring `oauth_user` egress requires a `client_secret` unconditionally, which makes a **public PKCE client** impossible to configure. Is that deliberate?
`registry/api/egress_auth_routes.py:654-655`:
```python
if not eo["client_secret_encrypted"]:
raise HTTPException(status.HTTP_400_BAD_REQUEST, detail="client_secret required")
```
I am filing this as a question rather than a bug because "we only support confidential clients" is a coherent posture for a server-side component that can hold a secret safely, and PKCE + a secret is a legitimate belt-and-braces pattern. But the code carries no comment either way, which is notable in a file that documents its other security decisions carefully (the SSRF rationale in `_post_token` a few lines away, for instance).
## What points toward it being unintended
- **PKCE exists so public clients need no secret** (RFC 7636), and the registry implements it fully: `generate_pkce_verifier`, `pkce_challenge_s256`, and `OAuthProviderConfig.use_pkce` defaulting to `True`.
- **The engine does not require one.** `_build_token_request` just interpolates whatever it is handed (`data["client_secret"] = client_secret`); nothing downstream validates it. Only the config-write path does.
- **The generated authorize URL already includes `code_challenge_method=S256`.** So the registry does PKCE *and* demands the secret PKCE is designed to replace.
## Concrete case
Salesforce Hosted MCP (Headless 360). Salesforce's own setup docs describe "Require Secret for Web Server Flow" as **discouraged** for clients that cannot store a secret securely, so a public External Client App is a normal configuration there.
I verified such a client genuinely has no secret: a `refresh_token` grant against `https://login.salesforce.com/services/oauth2/token` with `client_id` only returned **HTTP 200** with `scope: refresh_token mcp_api`, and the resulting token worked against the MCP endpoint (`initialize` + `tools/list` both 200).
Attempting to configure it:
```
POST /api/servers/salesforce-headless-360/egress-auth
{"egress_auth_mode":"oauth_user","egress_provider":"custom","client_id":"",
"scopes":["mcp_api","refresh_token"],
"custom_authorize_url":"...","custom_token_url":"..."}
-> HTTP 400 {"detail":"client_secret required"}
```
The identical request with a `client_secret` added returns **200**, and the flow then initiates correctly (`/api/egress-auth/initiate` → 200, authorize URL with `code_challenge_method=S256` and the registry's own callback). So the secret is the only blocker.
## If intentional
Worth a comment at that line plus a clearer error — `"client_secret required"` does not hint that public clients are unsupported *by design*, so the natural reading is that something is misconfigured. A line in the egress docs would help too.
## If not
Gating the check on the resolved provider's `use_pkce` would cover it: required when PKCE is off, optional when on.
## What I have not verified
I have **not** proven a full end-to-end flow succeeds without a secret — only the refresh leg, tested directly against Salesforce rather than through the gateway. The authorize leg needs the gateway callback allowlisted in the provider's app, which I have not completed. So I cannot claim that removing the check is sufficient, only that the current check is what stops the configuration being stored.
Happy to send a patch if the gating approach is the preferred direction.
Guide de contribution
Ouvrir le guide de contribution
Évaluation
Cette issue n'a pas encore été évaluée.