refactor: migrate to RemoteOAuthStorage for web OAuth handling

This commit is contained in:
Bob Dickinson 2026-07-03 13:11:13 -07:00
commit 7bd26c938e
2 changed files with 317 additions and 44 deletions

View file

@ -1,14 +1,16 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import { getBrowserOAuthStorage } from "@inspector/core/auth/browser/index.js";
import { describe, it, expect, beforeEach, vi } from "vitest";
import { BrowserOAuthStorage } from "@inspector/core/auth/browser/storage.js";
import { clearServerOAuthState } from "./clearServerOAuthState";
describe("clearServerOAuthState", () => {
let storage: BrowserOAuthStorage;
beforeEach(() => {
getBrowserOAuthStorage().clear("https://mcp.example.com/mcp");
storage = new BrowserOAuthStorage();
storage.clear("https://mcp.example.com/mcp");
});
it("clears storage by server URL when not the active connection", async () => {
const storage = getBrowserOAuthStorage();
await storage.saveTokens("https://mcp.example.com/mcp", {
access_token: "tok",
token_type: "Bearer",
@ -17,6 +19,7 @@ describe("clearServerOAuthState", () => {
const cleared = clearServerOAuthState({
config: { type: "streamable-http", url: "https://mcp.example.com/mcp" },
isActiveConnection: false,
oauthStorage: storage,
});
expect(cleared).toBe(true);
@ -26,18 +29,18 @@ describe("clearServerOAuthState", () => {
});
it("uses the live client when clearing the active connection", () => {
const inspectorClient = {
clearOAuthTokens: vi.fn(),
};
const clearOAuthTokens = vi.fn<() => void>();
const inspectorClient = { clearOAuthTokens };
const cleared = clearServerOAuthState({
config: { type: "streamable-http", url: "https://mcp.example.com/mcp" },
inspectorClient: inspectorClient as never,
inspectorClient,
isActiveConnection: true,
oauthStorage: storage,
});
expect(cleared).toBe(true);
expect(inspectorClient.clearOAuthTokens).toHaveBeenCalledTimes(1);
expect(clearOAuthTokens).toHaveBeenCalledTimes(1);
});
it("returns false for stdio servers", () => {
@ -45,6 +48,7 @@ describe("clearServerOAuthState", () => {
clearServerOAuthState({
config: { type: "stdio", command: "node", args: [] },
isActiveConnection: false,
oauthStorage: storage,
}),
).toBe(false);
});

View file

@ -1,4 +1,4 @@
import { getBrowserOAuthStorage } from "@inspector/core/auth/browser/index.js";
import type { OAuthStorage } from "@inspector/core/auth/storage.js";
import { getOAuthServerUrl } from "@inspector/core/mcp/config.js";
import type { InspectorClient } from "@inspector/core/mcp/inspectorClient.js";
import type { MCPServerConfig } from "@inspector/core/mcp/types.js";
@ -6,8 +6,10 @@ import type { MCPServerConfig } from "@inspector/core/mcp/types.js";
export interface ClearServerOAuthStateParams {
config: MCPServerConfig;
/** When set and this server is the active connection, clear via the live client. */
inspectorClient?: InspectorClient | null;
inspectorClient?: Pick<InspectorClient, "clearOAuthTokens"> | null;
isActiveConnection: boolean;
/** Shared web OAuth store; required so clear hits the same blob as connect. */
oauthStorage: OAuthStorage;
}
/**
@ -26,7 +28,7 @@ export function clearServerOAuthState(
if (params.isActiveConnection && params.inspectorClient) {
params.inspectorClient.clearOAuthTokens();
} else {
getBrowserOAuthStorage().clear(serverUrl);
params.oauthStorage.clear(serverUrl);
}
return true;
}