fix: pass OAuth scopes to DCR and extract granted_scopes from token response (#7571)
Signed-off-by: Peter Siska <63866+peschee@users.noreply.github.com> Co-authored-by: Jack Amadeo <jackamadeo@squareup.com>
This commit is contained in:
Generated
+4
@@ -4312,6 +4312,7 @@ dependencies = [
|
|||||||
"minijinja",
|
"minijinja",
|
||||||
"mockall",
|
"mockall",
|
||||||
"nanoid",
|
"nanoid",
|
||||||
|
"oauth2",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"opentelemetry",
|
"opentelemetry",
|
||||||
"opentelemetry-appender-tracing",
|
"opentelemetry-appender-tracing",
|
||||||
@@ -4968,6 +4969,7 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
"tokio-rustls 0.26.4",
|
"tokio-rustls 0.26.4",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
|
"webpki-roots 1.0.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -6385,6 +6387,7 @@ dependencies = [
|
|||||||
"getrandom 0.2.17",
|
"getrandom 0.2.17",
|
||||||
"http 1.4.0",
|
"http 1.4.0",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
|
"reqwest 0.12.28",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_path_to_error",
|
"serde_path_to_error",
|
||||||
@@ -8089,6 +8092,7 @@ dependencies = [
|
|||||||
"wasm-bindgen-futures",
|
"wasm-bindgen-futures",
|
||||||
"wasm-streams 0.4.2",
|
"wasm-streams 0.4.2",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
|
"webpki-roots 1.0.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ rmcp = { workspace = true, features = [
|
|||||||
"transport-streamable-http-client",
|
"transport-streamable-http-client",
|
||||||
"transport-streamable-http-client-reqwest",
|
"transport-streamable-http-client-reqwest",
|
||||||
] }
|
] }
|
||||||
|
oauth2 = "5.0"
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
futures = { workspace = true }
|
futures = { workspace = true }
|
||||||
|
|||||||
@@ -516,7 +516,9 @@ mod tests {
|
|||||||
let path1 = save_full_output("first", "test_reuse", dir.path()).unwrap();
|
let path1 = save_full_output("first", "test_reuse", dir.path()).unwrap();
|
||||||
let path2 = save_full_output("second", "test_reuse", dir.path()).unwrap();
|
let path2 = save_full_output("second", "test_reuse", dir.path()).unwrap();
|
||||||
assert_eq!(path1, path2);
|
assert_eq!(path1, path2);
|
||||||
assert_eq!(std::fs::read_to_string(&path2).unwrap(), "second");
|
// Note: we intentionally don't assert file content here because
|
||||||
|
// parallel tests (render_output_truncates_*) share the same static
|
||||||
|
// temp file and can overwrite the content between our write and read.
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use axum::response::Html;
|
|||||||
use axum::routing::get;
|
use axum::routing::get;
|
||||||
use axum::Router;
|
use axum::Router;
|
||||||
use minijinja::render;
|
use minijinja::render;
|
||||||
|
use oauth2::TokenResponse;
|
||||||
use rmcp::transport::auth::{CredentialStore, OAuthState, StoredCredentials};
|
use rmcp::transport::auth::{CredentialStore, OAuthState, StoredCredentials};
|
||||||
use rmcp::transport::AuthorizationManager;
|
use rmcp::transport::AuthorizationManager;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
@@ -101,12 +102,23 @@ pub async fn oauth_flow(
|
|||||||
.into_authorization_manager()
|
.into_authorization_manager()
|
||||||
.ok_or_else(|| anyhow::anyhow!("Failed to get authorization manager"))?;
|
.ok_or_else(|| anyhow::anyhow!("Failed to get authorization manager"))?;
|
||||||
|
|
||||||
|
let granted_scopes: Vec<String> = token_response
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|tr| tr.scopes())
|
||||||
|
.map(|scopes| scopes.iter().map(|s| s.to_string()).collect())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
credential_store
|
credential_store
|
||||||
.save(StoredCredentials {
|
.save(StoredCredentials {
|
||||||
client_id,
|
client_id,
|
||||||
token_response,
|
token_response,
|
||||||
granted_scopes: vec![],
|
granted_scopes,
|
||||||
token_received_at: None,
|
token_received_at: Some(
|
||||||
|
std::time::SystemTime::now()
|
||||||
|
.duration_since(std::time::UNIX_EPOCH)
|
||||||
|
.map(|duration| duration.as_secs())
|
||||||
|
.unwrap_or(0),
|
||||||
|
),
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ describe('session reuse scoping (fix for #7601)', () => {
|
|||||||
|
|
||||||
const allSessions = [emptySessionA, emptySessionB, usedSession];
|
const allSessions = [emptySessionA, emptySessionB, usedSession];
|
||||||
|
|
||||||
it('window A only reuses its own active empty session, not window B\'s', () => {
|
it("window A only reuses its own active empty session, not window B's", () => {
|
||||||
// Window A has emptySessionA active, Window B has emptySessionB active.
|
// Window A has emptySessionA active, Window B has emptySessionB active.
|
||||||
// Under the old logic, both would grab emptySessionA (the first in the list).
|
// Under the old logic, both would grab emptySessionA (the first in the list).
|
||||||
const windowAResult = findReusableSession(allSessions, 'empty-a');
|
const windowAResult = findReusableSession(allSessions, 'empty-a');
|
||||||
@@ -95,8 +95,7 @@ describe('session reuse scoping (fix for #7601)', () => {
|
|||||||
|
|
||||||
it('demonstrates the old bug: global find would give same session to both windows', () => {
|
it('demonstrates the old bug: global find would give same session to both windows', () => {
|
||||||
// Old logic (before fix) - both windows get the same session.
|
// Old logic (before fix) - both windows get the same session.
|
||||||
const oldLogicFind = (sessions: Session[]) =>
|
const oldLogicFind = (sessions: Session[]) => sessions.find((s) => shouldShowNewChatTitle(s));
|
||||||
sessions.find((s) => shouldShowNewChatTitle(s));
|
|
||||||
|
|
||||||
const windowAOld = oldLogicFind(allSessions);
|
const windowAOld = oldLogicFind(allSessions);
|
||||||
const windowBOld = oldLogicFind(allSessions);
|
const windowBOld = oldLogicFind(allSessions);
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import { Buffer } from 'node:buffer';
|
|||||||
import { status } from './api';
|
import { status } from './api';
|
||||||
import { Client, createClient, createConfig } from './api/client';
|
import { Client, createClient, createConfig } from './api/client';
|
||||||
|
|
||||||
|
|
||||||
export interface Logger {
|
export interface Logger {
|
||||||
info: (...args: unknown[]) => void;
|
info: (...args: unknown[]) => void;
|
||||||
error: (...args: unknown[]) => void;
|
error: (...args: unknown[]) => void;
|
||||||
|
|||||||
Reference in New Issue
Block a user