Skip to content

Commit 09d29d9

Browse files
authored
fix(auth): refresh with only granted scopes (#1331)
1 parent 8878a79 commit 09d29d9

1 file changed

Lines changed: 42 additions & 5 deletions

File tree

‎crates/rmcp/src/transport/auth.rs‎

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2561,10 +2561,8 @@ impl AuthorizationManager {
25612561
.exchange_refresh_token(&refresh_token_value)
25622562
// RFC 8707: the resource indicator is required on token requests, including refreshes
25632563
.add_extra_param("resource", self.oauth_resource().await);
2564-
let mut refresh_scopes = stored_credentials.granted_scopes.clone();
2565-
self.add_offline_access_if_supported(&mut refresh_scopes);
2566-
let requested_scopes = refresh_scopes.clone();
2567-
for scope in refresh_scopes {
2564+
let requested_scopes = stored_credentials.granted_scopes.clone();
2565+
for scope in requested_scopes.iter().cloned() {
25682566
refresh_request = refresh_request.add_scope(Scope::new(scope));
25692567
}
25702568
let mut token_result = match refresh_request
@@ -10082,7 +10080,7 @@ mod tests {
1008210080
}
1008310081

1008410082
#[tokio::test]
10085-
async fn refresh_token_adds_offline_access_when_as_supports_it() {
10083+
async fn refresh_token_does_not_add_ungranted_offline_access() {
1008610084
let (base_url, captured) = start_token_server().await;
1008710085

1008810086
let mut manager = manager_with_metadata(Some(AuthorizationMetadata {
@@ -10108,6 +10106,45 @@ mod tests {
1010810106

1010910107
manager.refresh_token().await.unwrap();
1011010108

10109+
let body = captured.lock().unwrap().take().unwrap();
10110+
let params: std::collections::HashMap<_, _> = url::form_urlencoded::parse(body.as_bytes())
10111+
.into_owned()
10112+
.collect();
10113+
let scope = params
10114+
.get("scope")
10115+
.expect("scope should be present in refresh request");
10116+
let mut scope_parts: Vec<&str> = scope.split_whitespace().collect();
10117+
scope_parts.sort_unstable();
10118+
assert_eq!(scope_parts, vec!["read"]);
10119+
}
10120+
10121+
#[tokio::test]
10122+
async fn refresh_token_keeps_offline_access_when_it_was_granted() {
10123+
let (base_url, captured) = start_token_server().await;
10124+
10125+
let mut manager = manager_with_metadata(Some(AuthorizationMetadata {
10126+
authorization_endpoint: format!("{}/authorize", base_url),
10127+
token_endpoint: format!("{}/token", base_url),
10128+
scopes_supported: Some(vec!["read".to_string(), "offline_access".to_string()]),
10129+
..Default::default()
10130+
}))
10131+
.await;
10132+
manager.configure_client(test_client_config()).unwrap();
10133+
10134+
let stored = StoredCredentials {
10135+
client_id: "my-client".to_string(),
10136+
token_response: Some(make_token_response_with_refresh(
10137+
"old-token",
10138+
"my-refresh-token",
10139+
)),
10140+
granted_scopes: vec!["read".to_string(), "offline_access".to_string()],
10141+
token_received_at: Some(AuthorizationManager::now_epoch_secs()),
10142+
issuer: None,
10143+
};
10144+
manager.credential_store.save(stored).await.unwrap();
10145+
10146+
manager.refresh_token().await.unwrap();
10147+
1011110148
let body = captured.lock().unwrap().take().unwrap();
1011210149
let params: std::collections::HashMap<_, _> = url::form_urlencoded::parse(body.as_bytes())
1011310150
.into_owned()

0 commit comments

Comments
 (0)