Merge pull request #871 from stdevi/fix/set-scope-dcr
fix: set user specified scope in DCR
This commit is contained in:
commit
c91960c5e6
5 changed files with 65 additions and 21 deletions
|
|
@ -102,16 +102,40 @@ export const clearClientInformationFromSessionStorage = ({
|
||||||
sessionStorage.removeItem(key);
|
sessionStorage.removeItem(key);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getScopeFromSessionStorage = (
|
||||||
|
serverUrl: string,
|
||||||
|
): string | undefined => {
|
||||||
|
const key = getServerSpecificKey(SESSION_KEYS.SCOPE, serverUrl);
|
||||||
|
const value = sessionStorage.getItem(key);
|
||||||
|
return value || undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const saveScopeToSessionStorage = (
|
||||||
|
serverUrl: string,
|
||||||
|
scope: string | undefined,
|
||||||
|
) => {
|
||||||
|
const key = getServerSpecificKey(SESSION_KEYS.SCOPE, serverUrl);
|
||||||
|
if (scope) {
|
||||||
|
sessionStorage.setItem(key, scope);
|
||||||
|
} else {
|
||||||
|
sessionStorage.removeItem(key);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const clearScopeFromSessionStorage = (serverUrl: string) => {
|
||||||
|
const key = getServerSpecificKey(SESSION_KEYS.SCOPE, serverUrl);
|
||||||
|
sessionStorage.removeItem(key);
|
||||||
|
};
|
||||||
|
|
||||||
export class InspectorOAuthClientProvider implements OAuthClientProvider {
|
export class InspectorOAuthClientProvider implements OAuthClientProvider {
|
||||||
constructor(
|
constructor(protected serverUrl: string) {
|
||||||
protected serverUrl: string,
|
|
||||||
scope?: string,
|
|
||||||
) {
|
|
||||||
this.scope = scope;
|
|
||||||
// Save the server URL to session storage
|
// Save the server URL to session storage
|
||||||
sessionStorage.setItem(SESSION_KEYS.SERVER_URL, serverUrl);
|
sessionStorage.setItem(SESSION_KEYS.SERVER_URL, serverUrl);
|
||||||
}
|
}
|
||||||
scope: string | undefined;
|
|
||||||
|
get scope(): string | undefined {
|
||||||
|
return getScopeFromSessionStorage(this.serverUrl);
|
||||||
|
}
|
||||||
|
|
||||||
get redirectUrl() {
|
get redirectUrl() {
|
||||||
return window.location.origin + "/oauth/callback";
|
return window.location.origin + "/oauth/callback";
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ export const SESSION_KEYS = {
|
||||||
PREREGISTERED_CLIENT_INFORMATION: "mcp_preregistered_client_information",
|
PREREGISTERED_CLIENT_INFORMATION: "mcp_preregistered_client_information",
|
||||||
SERVER_METADATA: "mcp_server_metadata",
|
SERVER_METADATA: "mcp_server_metadata",
|
||||||
AUTH_DEBUGGER_STATE: "mcp_auth_debugger_state",
|
AUTH_DEBUGGER_STATE: "mcp_auth_debugger_state",
|
||||||
|
SCOPE: "mcp_scope",
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
// Generate server-specific session storage keys
|
// Generate server-specific session storage keys
|
||||||
|
|
|
||||||
|
|
@ -112,6 +112,8 @@ jest.mock("../../auth", () => ({
|
||||||
})),
|
})),
|
||||||
clearClientInformationFromSessionStorage: jest.fn(),
|
clearClientInformationFromSessionStorage: jest.fn(),
|
||||||
saveClientInformationToSessionStorage: jest.fn(),
|
saveClientInformationToSessionStorage: jest.fn(),
|
||||||
|
saveScopeToSessionStorage: jest.fn(),
|
||||||
|
clearScopeFromSessionStorage: jest.fn(),
|
||||||
discoverScopes: jest.fn(),
|
discoverScopes: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,8 @@ import {
|
||||||
clearClientInformationFromSessionStorage,
|
clearClientInformationFromSessionStorage,
|
||||||
InspectorOAuthClientProvider,
|
InspectorOAuthClientProvider,
|
||||||
saveClientInformationToSessionStorage,
|
saveClientInformationToSessionStorage,
|
||||||
|
saveScopeToSessionStorage,
|
||||||
|
clearScopeFromSessionStorage,
|
||||||
discoverScopes,
|
discoverScopes,
|
||||||
} from "../auth";
|
} from "../auth";
|
||||||
import {
|
import {
|
||||||
|
|
@ -142,6 +144,15 @@ export function useConnection({
|
||||||
});
|
});
|
||||||
}, [oauthClientId, oauthClientSecret, sseUrl]);
|
}, [oauthClientId, oauthClientSecret, sseUrl]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!oauthScope) {
|
||||||
|
clearScopeFromSessionStorage(sseUrl);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
saveScopeToSessionStorage(sseUrl, oauthScope);
|
||||||
|
}, [oauthScope, sseUrl]);
|
||||||
|
|
||||||
const pushHistory = (request: object, response?: object) => {
|
const pushHistory = (request: object, response?: object) => {
|
||||||
setRequestHistory((prev) => [
|
setRequestHistory((prev) => [
|
||||||
...prev,
|
...prev,
|
||||||
|
|
@ -346,10 +357,9 @@ export function useConnection({
|
||||||
}
|
}
|
||||||
scope = await discoverScopes(sseUrl, resourceMetadata);
|
scope = await discoverScopes(sseUrl, resourceMetadata);
|
||||||
}
|
}
|
||||||
const serverAuthProvider = new InspectorOAuthClientProvider(
|
|
||||||
sseUrl,
|
saveScopeToSessionStorage(sseUrl, scope);
|
||||||
scope,
|
const serverAuthProvider = new InspectorOAuthClientProvider(sseUrl);
|
||||||
);
|
|
||||||
|
|
||||||
const result = await auth(serverAuthProvider, {
|
const result = await auth(serverAuthProvider, {
|
||||||
serverUrl: sseUrl,
|
serverUrl: sseUrl,
|
||||||
|
|
|
||||||
|
|
@ -80,13 +80,16 @@ export const oauthTransitions: Record<OAuthStep, StateTransition> = {
|
||||||
const metadata = context.state.oauthMetadata!;
|
const metadata = context.state.oauthMetadata!;
|
||||||
const clientMetadata = context.provider.clientMetadata;
|
const clientMetadata = context.provider.clientMetadata;
|
||||||
|
|
||||||
// Prefer scopes from resource metadata if available
|
// Priority: user-provided scope > discovered scopes
|
||||||
const scopesSupported =
|
if (!context.provider.scope || context.provider.scope.trim() === "") {
|
||||||
context.state.resourceMetadata?.scopes_supported ||
|
// Prefer scopes from resource metadata if available
|
||||||
metadata.scopes_supported;
|
const scopesSupported =
|
||||||
// Add all supported scopes to client registration
|
context.state.resourceMetadata?.scopes_supported ||
|
||||||
if (scopesSupported) {
|
metadata.scopes_supported;
|
||||||
clientMetadata.scope = scopesSupported.join(" ");
|
// Add all supported scopes to client registration
|
||||||
|
if (scopesSupported) {
|
||||||
|
clientMetadata.scope = scopesSupported.join(" ");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try Static client first, with DCR as fallback
|
// Try Static client first, with DCR as fallback
|
||||||
|
|
@ -113,10 +116,14 @@ export const oauthTransitions: Record<OAuthStep, StateTransition> = {
|
||||||
const metadata = context.state.oauthMetadata!;
|
const metadata = context.state.oauthMetadata!;
|
||||||
const clientInformation = context.state.oauthClientInfo!;
|
const clientInformation = context.state.oauthClientInfo!;
|
||||||
|
|
||||||
const scope = await discoverScopes(
|
// Priority: user-provided scope > discovered scopes
|
||||||
context.serverUrl,
|
let scope = context.provider.scope;
|
||||||
context.state.resourceMetadata ?? undefined,
|
if (!scope || scope.trim() === "") {
|
||||||
);
|
scope = await discoverScopes(
|
||||||
|
context.serverUrl,
|
||||||
|
context.state.resourceMetadata ?? undefined,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const { authorizationUrl, codeVerifier } = await startAuthorization(
|
const { authorizationUrl, codeVerifier } = await startAuthorization(
|
||||||
context.serverUrl,
|
context.serverUrl,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue