feat: support manual entry of OAuth client information
This commit is contained in:
parent
b39528d02b
commit
b8120d9f85
1 changed files with 242 additions and 24 deletions
|
|
@ -9,8 +9,67 @@ import {
|
|||
} from "@modelcontextprotocol/sdk/shared/auth.js";
|
||||
import { SESSION_KEYS, getServerSpecificKey } from "./constants";
|
||||
|
||||
export const getClientInformationFromSessionStorage = async ({
|
||||
serverUrl,
|
||||
isPreregistered,
|
||||
}: {
|
||||
serverUrl: string;
|
||||
isPreregistered?: boolean;
|
||||
}) => {
|
||||
const key = getServerSpecificKey(
|
||||
isPreregistered
|
||||
? SESSION_KEYS.PREREGISTERED_CLIENT_INFORMATION
|
||||
: SESSION_KEYS.CLIENT_INFORMATION,
|
||||
serverUrl,
|
||||
);
|
||||
|
||||
const value = sessionStorage.getItem(key);
|
||||
if (!value) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return await OAuthClientInformationSchema.parseAsync(JSON.parse(value));
|
||||
};
|
||||
|
||||
export const saveClientInformationToSessionStorage = ({
|
||||
serverUrl,
|
||||
clientInformation,
|
||||
isPreregistered,
|
||||
}: {
|
||||
serverUrl: string;
|
||||
clientInformation: OAuthClientInformation;
|
||||
isPreregistered?: boolean;
|
||||
}) => {
|
||||
const key = getServerSpecificKey(
|
||||
isPreregistered
|
||||
? SESSION_KEYS.PREREGISTERED_CLIENT_INFORMATION
|
||||
: SESSION_KEYS.CLIENT_INFORMATION,
|
||||
serverUrl,
|
||||
);
|
||||
sessionStorage.setItem(key, JSON.stringify(clientInformation));
|
||||
};
|
||||
|
||||
export const clearClientInformationFromSessionStorage = ({
|
||||
serverUrl,
|
||||
isPreregistered,
|
||||
}: {
|
||||
serverUrl: string;
|
||||
isPreregistered?: boolean;
|
||||
}) => {
|
||||
const key = getServerSpecificKey(
|
||||
isPreregistered
|
||||
? SESSION_KEYS.PREREGISTERED_CLIENT_INFORMATION
|
||||
: SESSION_KEYS.CLIENT_INFORMATION,
|
||||
serverUrl,
|
||||
);
|
||||
sessionStorage.removeItem(key);
|
||||
};
|
||||
|
||||
export class InspectorOAuthClientProvider implements OAuthClientProvider {
|
||||
constructor(public serverUrl: string) {
|
||||
constructor(
|
||||
protected serverUrl: string,
|
||||
protected resource?: string,
|
||||
) {
|
||||
// Save the server URL to session storage
|
||||
sessionStorage.setItem(SESSION_KEYS.SERVER_URL, serverUrl);
|
||||
}
|
||||
|
|
@ -31,24 +90,29 @@ export class InspectorOAuthClientProvider implements OAuthClientProvider {
|
|||
}
|
||||
|
||||
async clientInformation() {
|
||||
const key = getServerSpecificKey(
|
||||
SESSION_KEYS.CLIENT_INFORMATION,
|
||||
this.serverUrl,
|
||||
);
|
||||
const value = sessionStorage.getItem(key);
|
||||
if (!value) {
|
||||
return undefined;
|
||||
}
|
||||
// Try to get the preregistered client information from session storage first
|
||||
const preregisteredClientInformation = await getClientInformationFromSessionStorage({
|
||||
serverUrl: this.serverUrl,
|
||||
isPreregistered: true,
|
||||
});
|
||||
|
||||
return await OAuthClientInformationSchema.parseAsync(JSON.parse(value));
|
||||
// If no preregistered client information is found, get the dynamically registered client information
|
||||
return (
|
||||
preregisteredClientInformation ??
|
||||
(await getClientInformationFromSessionStorage({
|
||||
serverUrl: this.serverUrl,
|
||||
isPreregistered: false,
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
saveClientInformation(clientInformation: OAuthClientInformation) {
|
||||
const key = getServerSpecificKey(
|
||||
SESSION_KEYS.CLIENT_INFORMATION,
|
||||
this.serverUrl,
|
||||
);
|
||||
sessionStorage.setItem(key, JSON.stringify(clientInformation));
|
||||
// Save the dynamically registered client information to session storage
|
||||
saveClientInformationToSessionStorage({
|
||||
serverUrl: this.serverUrl,
|
||||
clientInformation,
|
||||
isPreregistered: false,
|
||||
});
|
||||
}
|
||||
|
||||
async tokens() {
|
||||
|
|
@ -67,6 +131,18 @@ export class InspectorOAuthClientProvider implements OAuthClientProvider {
|
|||
}
|
||||
|
||||
redirectToAuthorization(authorizationUrl: URL) {
|
||||
/**
|
||||
* Note: This resource parameter is for testing purposes in Inspector.
|
||||
* Once MCP Client SDK supports resource indicators, this parameter
|
||||
* will be passed to the SDK's auth method similar to how scope is passed.
|
||||
*
|
||||
* See: https://github.com/modelcontextprotocol/typescript-sdk/pull/498
|
||||
*
|
||||
* TODO: @xiaoyijun Remove this once MCP Client SDK supports resource indicators.
|
||||
*/
|
||||
if (this.resource) {
|
||||
authorizationUrl.searchParams.set("resource", this.resource);
|
||||
}
|
||||
window.location.href = authorizationUrl.href;
|
||||
}
|
||||
|
||||
|
|
@ -92,9 +168,10 @@ export class InspectorOAuthClientProvider implements OAuthClientProvider {
|
|||
}
|
||||
|
||||
clear() {
|
||||
sessionStorage.removeItem(
|
||||
getServerSpecificKey(SESSION_KEYS.CLIENT_INFORMATION, this.serverUrl),
|
||||
);
|
||||
clearClientInformationFromSessionStorage({
|
||||
serverUrl: this.serverUrl,
|
||||
isPreregistered: false,
|
||||
});
|
||||
sessionStorage.removeItem(
|
||||
getServerSpecificKey(SESSION_KEYS.TOKENS, this.serverUrl),
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in a new issue