Guided-auth removal, OAuth smoke testing, and TUI/CLI support (#1531)
* refactor(web): update OAuth E2E tests to use new authentication method - Replaced `runGuidedAuth` with `authenticate` in the InspectorClient OAuth end-to-end tests to support quick authentication with CIMD pre-registration. - Updated comments to reflect the change in authentication method and its support for HTTP test metadata URLs. - Ensured that the tests continue to validate the expected authorization URL after the change. Additionally, imported `ensureCimdClientRegistration` in the OAuthManager to streamline client registration during the authentication process. * Guided-auth removal, OAuth smoke testing, and TUI/CLI support * Added CLI OAuth runner flags tests * Added clarifying comment on port 0 binding * README notes on TUI oauth callback port, prettier fixes for CLI tests * CIMD validation uses inline like EMA IdP * Fixed TUI test failures from merge * test: bring 5 OAuth files over the 90 per-file coverage gate The guided-auth-removal work added/touched several files that fell below the repo's >=90 per-file coverage gate (npm run coverage). CI doesn't run the gate (it's local-only), so validate + test:integration passed while these gaps slipped through. Bring them all back over the line: - core/auth/node/runner-oauth-callback.ts: test bad-URL/non-http-scheme throws, no-port->80 default, and IPv6 hostname bracketing. Annotate the provably-dead guards (empty host, out-of-range port, String(err) fallback, pathname "/" fallback) -- all rejected by new URL() upstream -- with justified v8 ignore comments instead of lowering the gate. - core/auth/cimd.ts: test the AS-doesn't-support-CIMD early return. - core/react/useClientSettingsDraft.ts: test the functional-updater path, the stale-onChange-after-close prev===null guard, and the timer-fires-after-close value===null guard. - ClientSettingsForm.tsx: test the CIMD checkbox toggle and CIMD URL clear. - clients/tui/src/utils/oauthDisplay.ts: test static/dcr registration kinds, the expired IdP session, and the configured-scope/empty-scope branches of formatScopes. npm run coverage now passes (exit 0) across web, cli, tui, launcher. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: cliffhall <cliff@futurescale.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4a927d3978
commit
13dafe457b
1 changed files with 5702 additions and 4444 deletions
|
|
@ -57,7 +57,10 @@ import {
|
|||
serializeMcpConfig,
|
||||
} from "@inspector/core/mcp/serverList.js";
|
||||
import type { ClientConfig } from "@inspector/core/client/types.js";
|
||||
import { getActiveEnterpriseManagedAuthIdp } from "@inspector/core/client/types.js";
|
||||
import {
|
||||
getActiveCimdClientMetadataUrl,
|
||||
getActiveEnterpriseManagedAuthIdp,
|
||||
} from "@inspector/core/client/types.js";
|
||||
import { isEmaClientNotConfiguredError } from "@inspector/core/auth/ema/clientConfigError.js";
|
||||
import {
|
||||
loadClientConfigRemote,
|
||||
|
|
@ -146,6 +149,7 @@ import {
|
|||
OAUTH_PENDING_SERVER_KEY,
|
||||
isUnauthorizedError,
|
||||
} from "./utils/oauthFlow";
|
||||
import { clearServerOAuthState } from "./utils/clearServerOAuthState";
|
||||
|
||||
// OAuth redirect URL provider — points at the dev backend's `/oauth/callback`
|
||||
// handler. The InspectorClient only consults this when the active server
|
||||
|
|
@ -1224,6 +1228,12 @@ function App() {
|
|||
};
|
||||
}, [connectionStatus, inspectorClient]);
|
||||
|
||||
const connectionInfoCanClearOAuth =
|
||||
connectionStatus === "connected" &&
|
||||
!!inspectorClient &&
|
||||
(connectionInfoTransport === "streamable-http" ||
|
||||
connectionInfoTransport === "sse");
|
||||
|
||||
// Derive log entries from the message log. Filters for
|
||||
// `notifications/message` (the response to `logging/setLevel`).
|
||||
const logs = useMemo<LogEntryData[]>(
|
||||
|
|
@ -1315,6 +1325,7 @@ function App() {
|
|||
// from the InspectorClient options we're about to derive from it.
|
||||
const savedSettings = server.settings;
|
||||
const activeIdp = getActiveEnterpriseManagedAuthIdp(clientConfig);
|
||||
const activeCimdUrl = getActiveCimdClientMetadataUrl(clientConfig);
|
||||
// Flatten the persisted settings into the InspectorClient options shape.
|
||||
// Empty / zero values stay unset so the SDK defaults apply.
|
||||
const defaultMetadata = savedSettings?.metadata
|
||||
|
|
@ -1324,7 +1335,7 @@ function App() {
|
|||
.map((m) => [m.key, m.value]),
|
||||
)
|
||||
: undefined;
|
||||
const oauth =
|
||||
const oauthFromServer =
|
||||
savedSettings &&
|
||||
(savedSettings.oauthClientId ||
|
||||
savedSettings.oauthClientSecret ||
|
||||
|
|
@ -1345,6 +1356,13 @@ function App() {
|
|||
}),
|
||||
}
|
||||
: undefined;
|
||||
const oauth =
|
||||
oauthFromServer || activeCimdUrl
|
||||
? {
|
||||
...(oauthFromServer ?? {}),
|
||||
...(activeCimdUrl && { clientMetadataUrl: activeCimdUrl }),
|
||||
}
|
||||
: undefined;
|
||||
const client = new InspectorClient(server.config, {
|
||||
environment,
|
||||
// The Tasks tab needs the receiver-task pipeline; the
|
||||
|
|
@ -1450,7 +1468,7 @@ function App() {
|
|||
oauthCallbackHandledRef.current = true;
|
||||
|
||||
const params = parseOAuthCallbackParams(window.location.search);
|
||||
// The OAuth `state` round-trips `{execution}:{authId}`; the authId is the
|
||||
// The OAuth `state` round-trips the auth session id; the authId is the
|
||||
// session key the pre-redirect page saved the fetch log under, so the
|
||||
// rebuilt client can restore those `auth` entries. Read it before the
|
||||
// URL is cleared below.
|
||||
|
|
@ -2390,6 +2408,42 @@ function App() {
|
|||
// target isn't resolvable.
|
||||
const settingsModalIsStdio = settingsModalServerType === "stdio";
|
||||
|
||||
const clearServerOAuthAndDisconnect = useCallback(
|
||||
async (server: { id: string; name: string; config: MCPServerConfig }) => {
|
||||
const isActive = server.id === activeServerId;
|
||||
const cleared = clearServerOAuthState({
|
||||
config: server.config,
|
||||
inspectorClient: isActive ? inspectorClient : null,
|
||||
isActiveConnection: isActive,
|
||||
});
|
||||
if (!cleared) return;
|
||||
|
||||
if (isActive && inspectorClient) {
|
||||
await inspectorClient.disconnect();
|
||||
setConnectionInfoOAuthWhenConnected(undefined);
|
||||
}
|
||||
|
||||
notifications.show({
|
||||
title: "OAuth state cleared",
|
||||
message: isActive
|
||||
? "Stored tokens and client registration were removed. Reconnect to run a fresh authorization flow."
|
||||
: `Stored OAuth state was removed for "${server.name}". Connect to authorize again.`,
|
||||
color: "blue",
|
||||
});
|
||||
},
|
||||
[activeServerId, inspectorClient],
|
||||
);
|
||||
|
||||
const handleClearConnectionOAuth = useCallback(() => {
|
||||
if (!activeServer) return;
|
||||
void clearServerOAuthAndDisconnect(activeServer);
|
||||
}, [activeServer, clearServerOAuthAndDisconnect]);
|
||||
|
||||
const handleClearStoredOAuthFromSettings = useCallback(() => {
|
||||
if (!settingsModalTarget) return;
|
||||
void clearServerOAuthAndDisconnect(settingsModalTarget);
|
||||
}, [settingsModalTarget, clearServerOAuthAndDisconnect]);
|
||||
|
||||
const onSettingsModalClose = useCallback(() => {
|
||||
flushSettingsDraft();
|
||||
// Apply root edits to the live client once, on close — not on every
|
||||
|
|
@ -2689,6 +2743,9 @@ function App() {
|
|||
isStdio={settingsModalIsStdio}
|
||||
onClose={onSettingsModalClose}
|
||||
onSettingsChange={onSettingsChange}
|
||||
onClearStoredOAuth={
|
||||
settingsModalIsStdio ? undefined : handleClearStoredOAuthFromSettings
|
||||
}
|
||||
/>
|
||||
<ClientSettingsModal
|
||||
key={
|
||||
|
|
@ -2709,6 +2766,9 @@ function App() {
|
|||
clientCapabilities={clientCapabilities}
|
||||
transport={connectionInfoTransport}
|
||||
oauth={connectionInfoOAuth}
|
||||
onClearOAuth={
|
||||
connectionInfoCanClearOAuth ? handleClearConnectionOAuth : undefined
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<ServerRemoveConfirmModal
|
||||
|
|
|
|||
Loading…
Reference in a new issue