Skip to content

Commit a16f5f8

Browse files
committed
fix: include resource param in token refresh
1 parent adb6331 commit a16f5f8

1 file changed

Lines changed: 40 additions & 1 deletion

File tree

crates/rmcp/src/transport/auth.rs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1687,7 +1687,10 @@ impl AuthorizationManager {
16871687
debug!("refresh token present, attempting refresh");
16881688

16891689
let refresh_token_value = RefreshToken::new(refresh_token.secret().to_string());
1690-
let mut refresh_request = oauth_client.exchange_refresh_token(&refresh_token_value);
1690+
let mut refresh_request = oauth_client
1691+
.exchange_refresh_token(&refresh_token_value)
1692+
// RFC 8707: the resource indicator is required on token requests, including refreshes
1693+
.add_extra_param("resource", self.base_url.to_string());
16911694
let mut refresh_scopes = stored_credentials.granted_scopes;
16921695
self.add_offline_access_if_supported(&mut refresh_scopes);
16931696
for scope in refresh_scopes {
@@ -5482,6 +5485,42 @@ mod tests {
54825485
assert_eq!(scope_parts, vec!["read", "write"]);
54835486
}
54845487

5488+
#[tokio::test]
5489+
async fn refresh_token_includes_resource_parameter() {
5490+
let (base_url, captured) = start_token_server().await;
5491+
5492+
let mut manager = manager_with_metadata(Some(AuthorizationMetadata {
5493+
authorization_endpoint: format!("{}/authorize", base_url),
5494+
token_endpoint: format!("{}/token", base_url),
5495+
..Default::default()
5496+
}))
5497+
.await;
5498+
manager.configure_client(test_client_config()).unwrap();
5499+
5500+
let stored = StoredCredentials {
5501+
client_id: "my-client".to_string(),
5502+
token_response: Some(make_token_response_with_refresh(
5503+
"old-token",
5504+
"my-refresh-token",
5505+
)),
5506+
granted_scopes: vec![],
5507+
token_received_at: Some(AuthorizationManager::now_epoch_secs()),
5508+
};
5509+
manager.credential_store.save(stored).await.unwrap();
5510+
5511+
manager.refresh_token().await.unwrap();
5512+
5513+
let body = captured.lock().unwrap().take().unwrap();
5514+
let params: std::collections::HashMap<_, _> = url::form_urlencoded::parse(body.as_bytes())
5515+
.into_owned()
5516+
.collect();
5517+
assert_eq!(
5518+
params.get("resource").map(String::as_str),
5519+
Some("http://localhost/"),
5520+
"refresh requests must carry the RFC 8707 resource parameter, got body: {body}"
5521+
);
5522+
}
5523+
54855524
#[tokio::test]
54865525
async fn refresh_token_adds_offline_access_when_as_supports_it() {
54875526
let (base_url, captured) = start_token_server().await;

0 commit comments

Comments
 (0)