auth: refresh_token() requests offline_access that was never granted, so refreshes fail with invalid_scope
Mantenedores costumam responder em até 3 dias
Um pull request relacionado já foi integrado.
- #1331 de @jstar0 — integrado
Avaliação
- Dificuldade
- 2/5
- Tempo estimado
- 1-3 horas
- Facilidade para iniciantes
- 65/100
- Tipo de issue
- Bug
- Clareza
- Claramente especificada
- Status de atividade
- Ativa
- Stack de tecnologia
- rust
- Domínio
- authentication
Direção de pesquisa
Comece em crates/rmcp/src/transport/auth.rs, em refresh_token(), onde add_offline_access_if_supported é chamada sobre os granted_scopes armazenados antes de a requisição de refresh ser montada, e depois no próprio helper. Em seguida, leia o teste refresh_token_adds_offline_access_when_as_supports_it, que atualmente espera offline_access no refresh. Está pronto quando as requisições de refresh enviam apenas os granted_scopes (ou omitem scope), esse teste é reescrito para corresponder e o comportamento do caminho de autorização de #897 permanece inalterado.
Escrita pelo modelo de indexação a partir do texto da issue.
Descrição
Summary
Since #897, AuthorizationManager::refresh_token() adds offline_access to the refresh request whenever the authorization server lists it in scopes_supported, even if the stored grant doesn't include it. An authorization server that didn't grant offline_access refuses that refresh with invalid_scope. The client then has to re-authorize every time the access token expires.
Where
crates/rmcp/src/transport/auth.rs L2309-L2313:
let mut refresh_scopes = stored_credentials.granted_scopes.clone();
self.add_offline_access_if_supported(&mut refresh_scopes);
let requested_scopes = refresh_scopes.clone();
for scope in refresh_scopes {
refresh_request = refresh_request.add_scope(Scope::new(scope));
The test added in #897, refresh_token_adds_offline_access_when_as_supports_it, expects a refresh of a grant holding only read to send offline_access read.
Why this breaks
RFC 6749 §6: the refresh request's scope "MUST NOT include any scope not originally granted by the resource owner". The authorization server is right to answer invalid_scope.
Requesting offline_access at authorization time doesn't guarantee it's granted. OIDC Core §11 requires prompt=consent alongside offline_access "unless other conditions for processing the request permitting offline access … are in place", and rmcp adds the scope without the prompt. node-oidc-provider, for one, drops offline_access from the grant by default. An OP can still issue a refresh token without the scope (node-oidc-provider allows this through its issueRefreshToken hook), and SEP-2207's own "No guarantee" item allows for that. Every refresh rmcp then sends is rejected:
POST /token
grant_type=refresh_token&refresh_token=…&scope=openid+profile+email+offline_access
HTTP/1.1 400 Bad Request
{"error":"invalid_scope","error_description":"refresh token missing requested scope","scope":"offline_access"}
SEP-2207, which #676/#897 implement, only covers authorization requests. Client guidance item 2: the client "MAY add the offline_access scope to the list of scopes from the resource server before making authorization requests to the Authorization Server". It doesn't extend that to refresh requests.
Reproduction
- Run an authorization server that strips
offline_accesswithoutprompt=consent(node-oidc-provider 9.x does by default), still issues refresh tokens to the client (for node-oidc-provider, anissueRefreshTokenthat returnstruefor clients allowed therefresh_tokengrant), and listsoffline_accessinscopes_supported. - Authorize with rmcp. The token response's
scopedoesn't includeoffline_access, but a refresh token is issued. - Let the access token expire, or call
refresh_token(). The token endpoint returnsinvalid_scopeforoffline_access.
Seen in the wild with Codex CLI 0.155.0 and 0.161.0 (codex-mcp-client, rmcp 3.3.0). Restricting Codex's configured scopes doesn't help: the login requests only the configured scopes, but every refresh still gets offline_access added. Still present on main (08e021153ef0).
Suggested fix
Don't add offline_access on the refresh path. Send the stored granted_scopes, or omit scope entirely, which RFC 6749 §6 defines as "equal to the scope originally granted". If the grant does include offline_access, it's already in granted_scopes, so nothing is lost. #897's scope-upgrade change is a re-authorization, so SEP-2207 does cover it and it can stay.
- Linguagem predominante
- Rust
- Estrelas
- 4.1k
- Forks
- 694
- Merge médio
- 4d 9h
- PRs com merge (30d)
- 41
Preparar o ambiente
Inicia o contêiner de desenvolvimento do projeto no navegador, com a sua própria conta do GitHub.
- Sem Dockerfile nem arquivo Docker Compose
- Sem modelo de pull request
- Ler o guia de contribuição
Primeiros passos
- Leia a issue inteira e depois o guia de contribuição do projeto.
- Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
- Faça um fork do repositório e trabalhe em uma branch.
- Abra um pull request que referencie o número da issue.
Mais de modelcontextprotocol/rust-sdk
-
Support tool exposure modes (direct / deferred / codemode) and client-side aggregation.Talvez já em andamento @jamadeo assumiu há 1 dia. Abertaenhancement P3 T-model T-service
modelcontextprotocol/rust-sdk#1437 · 1 comentário · 1 responsável ·
Mantenedores costumam responder em até 3 dias
-
Return MergedList<T> from list_all_* and drop the _with_cache_hint variantsTalvez já em andamento @DaleSeo assumiu há 4 dias. Abertaenhancement P3 ready for work T-service v4
modelcontextprotocol/rust-sdk#1430 · 1 responsável ·
Mantenedores costumam responder em até 3 dias
-
Server logs a fabricated 'initialize' (with synthesized rmcp/3.8.0 peer identity) for no-initialize SEP-2567 requests, ignoring clientInfo from _metaTalvez já em andamento @jamadeo assumiu há 5 dias. Abertabug P2 ready for work T-service T-transport
modelcontextprotocol/rust-sdk#1425 · 2 comentários · 1 responsável ·
Mantenedores costumam responder em até 3 dias
-
OAuth discovery drops explicit default ports from advertised issuer identifiersTalvez já em andamento @gocamille assumiu há 7 dias. Abertabug P1 ready for work T-security T-transport
modelcontextprotocol/rust-sdk#1420 · 4 comentários · 1 responsável ·
Mantenedores costumam responder em até 3 dias
-
`register_client` drops `client_secret_expires_at` from the registration responseTalvez já em andamento @gocamille assumiu há 8 dias. Abertabug P2 ready for work T-security
modelcontextprotocol/rust-sdk#1416 · 1 comentário · 1 responsável ·
Mantenedores costumam responder em até 3 dias
Todas as issues de modelcontextprotocol/rust-sdk
Issues semelhantes
-
Shell `yoyo <verb> <sub>` missing its required argument prints usage and exits 0: `config get`Abertaagent-self
Dificuldade 1/5 Menos de uma hora Facilidade para iniciantes 88/100
yologdev/yoyo-evolve#1154 ·
-
Dificuldade 1/5 Menos de uma hora Facilidade para iniciantes 85/100
eliasstravik/herdr-projects#789 · 1 comentário ·
-
bug
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 64/100
Mantenedores costumam responder em até 1 dia
-
bug
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 82/100
Alex-Sebastian-Ene/mind-over-chicken#195 ·
Mantenedores costumam responder em até 1 dia
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 78/100
Mantenedores costumam responder em até 2 dias