auth: refresh_token() requests offline_access that was never granted, so refreshes fail with invalid_scope

Fechada Para iniciantes
#1,330 0 comentários 0 reações 0 responsáveis Ver no GitHub

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

bug P1 ready for work T-security T-transport

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

  1. Run an authorization server that strips offline_access without prompt=consent (node-oidc-provider 9.x does by default), still issues refresh tokens to the client (for node-oidc-provider, an issueRefreshToken that returns true for clients allowed the refresh_token grant), and lists offline_access in scopes_supported.
  2. Authorize with rmcp. The token response's scope doesn't include offline_access, but a refresh token is issued.
  3. Let the access token expire, or call refresh_token(). The token endpoint returns invalid_scope for offline_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

Abrir no Codespaces

Inicia o contêiner de desenvolvimento do projeto no navegador, com a sua própria conta do GitHub.

Primeiros passos

  1. Leia a issue inteira e depois o guia de contribuição do projeto.
  2. Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
  3. Faça um fork do repositório e trabalhe em uma branch.
  4. Abra um pull request que referencie o número da issue.

Mais de modelcontextprotocol/rust-sdk

Todas as issues de modelcontextprotocol/rust-sdk

Issues semelhantes

Mais issues de Rust

Receba novas issues na sua caixa de entrada

Um resumo curto de issues do GitHub para quem está começando.