feat: add default Authorization Bearer header and clean up legacy auth
- Provide "Authorization: Bearer " as default starting header - Remove legacy auth props from components - Centralize migration logic and fix localStorage cleanup - Update tests for new auth architecture
This commit is contained in:
parent
f36c649bb9
commit
e08ce8cee9
7 changed files with 45 additions and 62 deletions
|
|
@ -23,10 +23,6 @@ export default tseslint.config(
|
||||||
"warn",
|
"warn",
|
||||||
{ allowConstantExport: true },
|
{ allowConstantExport: true },
|
||||||
],
|
],
|
||||||
"@typescript-eslint/no-unused-vars": [
|
|
||||||
"error",
|
|
||||||
{ argsIgnorePattern: "^_" },
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -154,7 +154,14 @@ const App = () => {
|
||||||
return migrateFromLegacyAuth(legacyToken, legacyHeaderName);
|
return migrateFromLegacyAuth(legacyToken, legacyHeaderName);
|
||||||
}
|
}
|
||||||
|
|
||||||
return [];
|
// Default to Authorization: Bearer as the most common case
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: "Authorization",
|
||||||
|
value: "Bearer ",
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
const [pendingSampleRequests, setPendingSampleRequests] = useState<
|
const [pendingSampleRequests, setPendingSampleRequests] = useState<
|
||||||
|
|
@ -243,8 +250,6 @@ const App = () => {
|
||||||
args,
|
args,
|
||||||
sseUrl,
|
sseUrl,
|
||||||
env,
|
env,
|
||||||
bearerToken,
|
|
||||||
headerName,
|
|
||||||
customHeaders,
|
customHeaders,
|
||||||
oauthClientId,
|
oauthClientId,
|
||||||
oauthScope,
|
oauthScope,
|
||||||
|
|
@ -334,11 +339,19 @@ const App = () => {
|
||||||
}, [transportType]);
|
}, [transportType]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
localStorage.setItem("lastBearerToken", bearerToken);
|
if (bearerToken) {
|
||||||
|
localStorage.setItem("lastBearerToken", bearerToken);
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem("lastBearerToken");
|
||||||
|
}
|
||||||
}, [bearerToken]);
|
}, [bearerToken]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
localStorage.setItem("lastHeaderName", headerName);
|
if (headerName) {
|
||||||
|
localStorage.setItem("lastHeaderName", headerName);
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem("lastHeaderName");
|
||||||
|
}
|
||||||
}, [headerName]);
|
}, [headerName]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -858,10 +871,6 @@ const App = () => {
|
||||||
setEnv={setEnv}
|
setEnv={setEnv}
|
||||||
config={config}
|
config={config}
|
||||||
setConfig={setConfig}
|
setConfig={setConfig}
|
||||||
bearerToken={bearerToken}
|
|
||||||
setBearerToken={setBearerToken}
|
|
||||||
headerName={headerName}
|
|
||||||
setHeaderName={setHeaderName}
|
|
||||||
customHeaders={customHeaders}
|
customHeaders={customHeaders}
|
||||||
setCustomHeaders={setCustomHeaders}
|
setCustomHeaders={setCustomHeaders}
|
||||||
oauthClientId={oauthClientId}
|
oauthClientId={oauthClientId}
|
||||||
|
|
|
||||||
|
|
@ -53,12 +53,7 @@ interface SidebarProps {
|
||||||
setSseUrl: (url: string) => void;
|
setSseUrl: (url: string) => void;
|
||||||
env: Record<string, string>;
|
env: Record<string, string>;
|
||||||
setEnv: (env: Record<string, string>) => void;
|
setEnv: (env: Record<string, string>) => void;
|
||||||
// Legacy auth props (for backward compatibility)
|
// Custom headers support
|
||||||
bearerToken: string;
|
|
||||||
setBearerToken: (token: string) => void;
|
|
||||||
headerName?: string;
|
|
||||||
setHeaderName?: (name: string) => void;
|
|
||||||
// New custom headers support
|
|
||||||
customHeaders: CustomHeadersType;
|
customHeaders: CustomHeadersType;
|
||||||
setCustomHeaders: (headers: CustomHeadersType) => void;
|
setCustomHeaders: (headers: CustomHeadersType) => void;
|
||||||
oauthClientId: string;
|
oauthClientId: string;
|
||||||
|
|
@ -86,10 +81,6 @@ const Sidebar = ({
|
||||||
setSseUrl,
|
setSseUrl,
|
||||||
env,
|
env,
|
||||||
setEnv,
|
setEnv,
|
||||||
bearerToken: _bearerToken,
|
|
||||||
setBearerToken: _setBearerToken,
|
|
||||||
headerName: _headerName,
|
|
||||||
setHeaderName: _setHeaderName,
|
|
||||||
customHeaders,
|
customHeaders,
|
||||||
setCustomHeaders,
|
setCustomHeaders,
|
||||||
oauthClientId,
|
oauthClientId,
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,4 @@
|
||||||
import {
|
import { render, screen, fireEvent, act } from "@testing-library/react";
|
||||||
render,
|
|
||||||
screen,
|
|
||||||
fireEvent,
|
|
||||||
act,
|
|
||||||
waitFor,
|
|
||||||
} from "@testing-library/react";
|
|
||||||
import "@testing-library/jest-dom";
|
import "@testing-library/jest-dom";
|
||||||
import { describe, it, beforeEach, jest } from "@jest/globals";
|
import { describe, it, beforeEach, jest } from "@jest/globals";
|
||||||
import Sidebar from "../Sidebar";
|
import Sidebar from "../Sidebar";
|
||||||
|
|
@ -54,10 +48,6 @@ describe("Sidebar", () => {
|
||||||
setOauthScope: jest.fn(),
|
setOauthScope: jest.fn(),
|
||||||
env: {},
|
env: {},
|
||||||
setEnv: jest.fn(),
|
setEnv: jest.fn(),
|
||||||
bearerToken: "",
|
|
||||||
setBearerToken: jest.fn(),
|
|
||||||
headerName: "",
|
|
||||||
setHeaderName: jest.fn(),
|
|
||||||
customHeaders: [],
|
customHeaders: [],
|
||||||
setCustomHeaders: jest.fn(),
|
setCustomHeaders: jest.fn(),
|
||||||
onConnect: jest.fn(),
|
onConnect: jest.fn(),
|
||||||
|
|
|
||||||
|
|
@ -649,9 +649,17 @@ describe("useConnection", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test("preserves server Authorization header when proxy auth is configured", async () => {
|
test("preserves server Authorization header when proxy auth is configured", async () => {
|
||||||
|
const customHeaders: CustomHeaders = [
|
||||||
|
{
|
||||||
|
name: "Authorization",
|
||||||
|
value: "Bearer server-auth-token",
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const propsWithBothAuth = {
|
const propsWithBothAuth = {
|
||||||
...defaultProps,
|
...defaultProps,
|
||||||
bearerToken: "server-auth-token",
|
customHeaders,
|
||||||
config: {
|
config: {
|
||||||
...DEFAULT_INSPECTOR_CONFIG,
|
...DEFAULT_INSPECTOR_CONFIG,
|
||||||
MCP_PROXY_AUTH_TOKEN: {
|
MCP_PROXY_AUTH_TOKEN: {
|
||||||
|
|
@ -811,14 +819,18 @@ describe("useConnection", () => {
|
||||||
expect(headers).not.toHaveProperty("X-Disabled");
|
expect(headers).not.toHaveProperty("X-Disabled");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("migrates from legacy bearerToken and headerName", async () => {
|
test("handles migrated legacy auth via custom headers", async () => {
|
||||||
const propsWithLegacyAuth = {
|
// Simulate what App.tsx would do - migrate legacy auth to custom headers
|
||||||
|
const customHeaders: CustomHeaders = [
|
||||||
|
{ name: "X-Custom-Auth", value: "legacy-token", enabled: true },
|
||||||
|
];
|
||||||
|
|
||||||
|
const propsWithMigratedAuth = {
|
||||||
...defaultProps,
|
...defaultProps,
|
||||||
bearerToken: "legacy-token",
|
customHeaders,
|
||||||
headerName: "X-Custom-Auth",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const { result } = renderHook(() => useConnection(propsWithLegacyAuth));
|
const { result } = renderHook(() => useConnection(propsWithMigratedAuth));
|
||||||
|
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
await result.current.connect();
|
await result.current.connect();
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ import {
|
||||||
import { getMCPServerRequestTimeout } from "@/utils/configUtils";
|
import { getMCPServerRequestTimeout } from "@/utils/configUtils";
|
||||||
import { InspectorConfig } from "../configurationTypes";
|
import { InspectorConfig } from "../configurationTypes";
|
||||||
import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
|
import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
|
||||||
import { CustomHeaders, migrateFromLegacyAuth } from "../types/customHeaders";
|
import { CustomHeaders } from "../types/customHeaders";
|
||||||
|
|
||||||
interface UseConnectionOptions {
|
interface UseConnectionOptions {
|
||||||
transportType: "stdio" | "sse" | "streamable-http";
|
transportType: "stdio" | "sse" | "streamable-http";
|
||||||
|
|
@ -65,10 +65,7 @@ interface UseConnectionOptions {
|
||||||
args: string;
|
args: string;
|
||||||
sseUrl: string;
|
sseUrl: string;
|
||||||
env: Record<string, string>;
|
env: Record<string, string>;
|
||||||
// Legacy auth support (for backward compatibility)
|
// Custom headers support
|
||||||
bearerToken?: string;
|
|
||||||
headerName?: string;
|
|
||||||
// New custom headers support
|
|
||||||
customHeaders?: CustomHeaders;
|
customHeaders?: CustomHeaders;
|
||||||
oauthClientId?: string;
|
oauthClientId?: string;
|
||||||
oauthScope?: string;
|
oauthScope?: string;
|
||||||
|
|
@ -90,8 +87,6 @@ export function useConnection({
|
||||||
args,
|
args,
|
||||||
sseUrl,
|
sseUrl,
|
||||||
env,
|
env,
|
||||||
bearerToken,
|
|
||||||
headerName,
|
|
||||||
customHeaders,
|
customHeaders,
|
||||||
oauthClientId,
|
oauthClientId,
|
||||||
oauthScope,
|
oauthScope,
|
||||||
|
|
@ -384,16 +379,8 @@ export function useConnection({
|
||||||
// Create an auth provider with the current server URL
|
// Create an auth provider with the current server URL
|
||||||
const serverAuthProvider = new InspectorOAuthClientProvider(sseUrl);
|
const serverAuthProvider = new InspectorOAuthClientProvider(sseUrl);
|
||||||
|
|
||||||
// Handle custom headers (new approach) or legacy auth (backward compatibility)
|
// Use custom headers (migration is handled in App.tsx)
|
||||||
let finalHeaders: CustomHeaders = [];
|
let finalHeaders: CustomHeaders = customHeaders || [];
|
||||||
|
|
||||||
if (customHeaders && customHeaders.length > 0) {
|
|
||||||
// Use new custom headers approach
|
|
||||||
finalHeaders = customHeaders;
|
|
||||||
} else if (bearerToken || headerName) {
|
|
||||||
// Migrate from legacy auth approach
|
|
||||||
finalHeaders = migrateFromLegacyAuth(bearerToken, headerName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add OAuth token if available and no custom headers are set
|
// Add OAuth token if available and no custom headers are set
|
||||||
if (finalHeaders.length === 0) {
|
if (finalHeaders.length === 0) {
|
||||||
|
|
|
||||||
|
|
@ -58,9 +58,7 @@ export const migrateFromLegacyAuth = (
|
||||||
bearerToken?: string,
|
bearerToken?: string,
|
||||||
headerName?: string,
|
headerName?: string,
|
||||||
): CustomHeaders => {
|
): CustomHeaders => {
|
||||||
if (!bearerToken) {
|
return bearerToken
|
||||||
return [];
|
? [createHeaderFromBearerToken(bearerToken, headerName)]
|
||||||
}
|
: [];
|
||||||
|
|
||||||
return [createHeaderFromBearerToken(bearerToken, headerName)];
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue