aaif-goose / aaif-goose/goose

Reopening old sessions re-triggers OAuth browser flows for uninstalled MCP extensions

オープン
#11,084 コメント 0 件 リアクション 0 件 担当者 2 名 @DOsinga に割り当て済み GitHub で見る
主要言語
Rust
スター
54.2k
フォーク
6.2k
平均マージ
3日 2時間
マージ済み PR(30日)
262

説明

## Summary

Reopening an old session eagerly reconnects **every extension recorded in that session's snapshot — including MCP extensions the user has since uninstalled**. For OAuth-based streamable HTTP extensions this means resuming an old chat can pop the vendor's browser auth window for an integration that is no longer installed. Uninstalling an extension also does not remove its stored OAuth credentials, so the credentials linger and the stale snapshot keeps exercising them.

Observed first-hand: after uninstalling an OAuth-based MCP, reopening older chats that had used it re-triggered the vendor's browser OAuth flow.

## Mechanism (verified in source at `2694fff7e`, still present on `main` at `1c1bd5299`)

1. **Sessions persist their own extension snapshot.** Each session stores its enabled extensions in per-session `extension_data` under the `enabled_extensions.v0` key (`EnabledExtensionsState`, `crates/goose/src/session/extension_data.rs`). Uninstalling an extension only mutates the global config (`remove_extension`, `crates/goose/src/config/extensions.rs`); existing session snapshots are never pruned.
2. **Session load keeps the stale snapshot.** `prepare_session_for_activation` (`crates/goose/src/acp/server.rs`) only rebuilds extension data from the current global config when the snapshot is *missing*. If a snapshot exists, it is used as-is.
3. **Resume eagerly connects everything in the snapshot.** `load_extensions_from_session` (`crates/goose/src/agents/agent.rs`, ~line 1330) connects every extension in the snapshot in parallel, with **no reconciliation against the currently installed/enabled extensions**.
4. **The only filter doesn't apply to MCPs.** `EnabledExtensionsState::from_extension_data` retains entries via `is_extension_available` (`crates/goose/src/config/extensions.rs`, ~line 34), which only drops unknown *Platform* extensions — every other variant, including remote streamable HTTP MCPs, passes unconditionally (`_ => true`).
5. **Connecting an OAuth HTTP extension can open the browser.** In `crates/goose/src/agents/extension_manager.rs` (streamable HTTP connect path, ~line 727): if stored credentials exist, `oauth_flow` force-refreshes them (see #11021); if credentials are missing or rejected, the unauthenticated connect gets a 401 and falls back to `oauth_flow` → **browser auth window** — for an extension the user uninstalled.
6. **Uninstall leaves credentials behind.** Nothing in the extension-removal path deletes the `oauth_creds_` secret (`GooseCredentialStore`, `crates/goose/src/oauth/persist.rs`), so uninstalled extensions keep live tokens in the keychain.

## Relationship to #11021

Same user-facing symptom family (surprise OAuth browser windows), different root cause. #11021 is a credential-erasure race in the refresh path; this issue is session resume replaying a stale per-session extension snapshot with no reconciliation against installed extensions. They compound: after #11021 wipes `oauth_creds_`, reopening *any* old chat whose snapshot contains that MCP hits the 401 → browser-auth path (step 5), even if the user responded to the loop by uninstalling the extension.

## Impact

- Uninstalling an MCP does not stop it from being contacted: old sessions keep reconnecting it on resume, including full OAuth browser flows.
- Users cannot make a misbehaving OAuth integration go away by removing it — old chats resurrect it.
- Stored OAuth tokens outlive the uninstall, which is also a credential-hygiene concern.

## Possible directions (non-prescriptive)

- Reconcile the session snapshot against currently installed extensions on resume (drop or mark-unavailable entries that no longer exist in config), the way unknown Platform extensions are already filtered.
- Treat resume-time connects for OAuth extensions as non-interactive: never open a browser during session load; surface a "needs re-auth" state instead.
- Delete `oauth_creds_` (and any other extension-scoped secrets) when an extension is removed.

コントリビューションガイド

コントリビューションガイドを開く

評価

この issue はまだ評価されていません。

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。