modelcontextprotocol / modelcontextprotocol/python-sdk
OAuth token refresh hits the wrong endpoint when the auth server lives under a path
Personne n'a encore pris cette issue.
- Langage dominant
- Python
- Étoiles
- 24.3k
- Forks
- 4k
- Merge moyen
- 1 j 1 h
- PR mergées (30 j)
- 31
Description
Ran into this with a hosted MCP server whose authorization server isn't at the origin root — token endpoint is https://host/oauth2/api/v1/token, not https://host/token.
If a client starts up with a cached-but-expired access token (+ refresh token) and hasn't done discovery yet, async_auth_flow refreshes at the very top — before any 401/metadata discovery. So oauth_metadata is None and _refresh_token uses the fallback urljoin(get_authorization_base_url(server_url), "/token"), i.e. just {scheme}://{netloc}/token. That 404s, _handle_refresh_response clears the tokens, and the flow drops to full interactive auth — which a headless/gateway client can't do. So the server silently disconnects every time the access token expires (mine are 5 min, so… constantly).
Same path-stripping fallback is in _get_token_endpoint, _perform_authorization_code_grant (/authorize) and DCR (/register) — refresh is just the one that bites silently.
Repro (roughly):
- MCP server whose AS metadata puts
token_endpointunder a path, not{origin}/token - log in normally so tokens get cached
- let the access token expire (or clear the expiry), reconnect with a fresh provider
- watch the refresh POST go to
https://host/token→ 404 → "Token refresh failed" → tokens cleared → it tries to open a browser
Fix looks like: discover metadata before the eager refresh (or stop dropping the issuer path in the fallback). Happy to PR — have a branch that pulls the PRM/ASM discovery out of the 401 branch and runs it before the refresh.
(used some AI help digging into this)
Guide de contribution
Ouvrir le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Piste de recherche
Commencez par suivre async_auth_flow et son chemin de refresh eager jusqu’à _refresh_token, puis comparez le comportement de fallback dans _get_token_endpoint, _perform_authorization_code_grant et DCR. Reproduisez le scénario d’un token expiré mis en cache face à un serveur d’autorisation dont les endpoints de métadonnées incluent un chemin. Le travail est terminé lorsque le refresh utilise l’endpoint de token découvert et n’efface pas les tokens valides ni ne revient à l’authentification interactive.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Évaluation
- Stack technique
- python
- Domaine
- api, authentication
- Type d'issue
- Bug
- Difficulté
- 4/5
- Temps estimé
- 3-5 jours
- Activité
- Calme
- Clarté
- Clairement spécifiée
- Accessibilité débutants
- 48/100