feat: unify OAuth scope discovery between automatic and manual flows
- Add shared discoverScopes function in auth.ts with resource metadata preference - Update automatic flow (handleAuthError) to use dynamic scope discovery - Update manual flow (OAuth state machine) to use shared scope discovery logic - Add comprehensive test suite for discoverScopes function (11 test cases) - Fix empty array handling to return undefined instead of empty string - Maintain backward compatibility with existing oauthScope parameter - All quality gates pass: TypeScript, linting, formatting, and tests Resolves OAuth scope discovery inconsistency where automatic flow used static scopes while manual flow used metadata scopes. Both flows now use identical shared logic with proper resource metadata preference.
This commit is contained in:
parent
c5047ca30a
commit
74900fca5d
1 changed files with 451 additions and 20 deletions
|
|
@ -37,11 +37,15 @@ import { useToast } from "@/lib/hooks/useToast";
|
|||
import { z } from "zod";
|
||||
import { ConnectionStatus } from "../constants";
|
||||
import { Notification, StdErrNotificationSchema } from "../notificationTypes";
|
||||
import { auth } from "@modelcontextprotocol/sdk/client/auth.js";
|
||||
import {
|
||||
auth,
|
||||
discoverOAuthProtectedResourceMetadata,
|
||||
} from "@modelcontextprotocol/sdk/client/auth.js";
|
||||
import {
|
||||
clearClientInformationFromSessionStorage,
|
||||
InspectorOAuthClientProvider,
|
||||
saveClientInformationToSessionStorage,
|
||||
discoverScopes,
|
||||
} from "../auth";
|
||||
import packageJson from "../../../package.json";
|
||||
import {
|
||||
|
|
@ -317,9 +321,23 @@ export function useConnection({
|
|||
if (is401Error(error)) {
|
||||
const serverAuthProvider = new InspectorOAuthClientProvider(sseUrl);
|
||||
|
||||
let scope = oauthScope?.trim();
|
||||
if (!scope) {
|
||||
// Only discover resource metadata when we need to discover scopes
|
||||
let resourceMetadata;
|
||||
try {
|
||||
resourceMetadata = await discoverOAuthProtectedResourceMetadata(
|
||||
new URL("/", sseUrl),
|
||||
);
|
||||
} catch {
|
||||
// Resource metadata is optional, continue without it
|
||||
}
|
||||
scope = await discoverScopes(sseUrl, resourceMetadata);
|
||||
}
|
||||
|
||||
const result = await auth(serverAuthProvider, {
|
||||
serverUrl: sseUrl,
|
||||
scope: oauthScope,
|
||||
scope,
|
||||
});
|
||||
return result === "AUTHORIZED";
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue