From 929b93061865afed3bed38ad19ee08abf4e3003b Mon Sep 17 00:00:00 2001 From: Konstantin Konstantinov Date: Mon, 11 Aug 2025 21:24:26 +0300 Subject: [PATCH 1/2] reusable generateOAuthState helper, introduce in InspectorOAuthClientProvider and in oauth-state-machine, add test --- client/src/lib/auth.ts | 5 +++++ client/src/lib/oauth-state-machine.ts | 10 ++-------- .../{oauthUtils.ts => oauthUtils.test.ts} | 8 ++++++++ client/src/utils/oauthUtils.ts | 14 ++++++++++++++ 4 files changed, 29 insertions(+), 8 deletions(-) rename client/src/utils/__tests__/{oauthUtils.ts => oauthUtils.test.ts} (92%) diff --git a/client/src/lib/auth.ts b/client/src/lib/auth.ts index 9989e944..73217a60 100644 --- a/client/src/lib/auth.ts +++ b/client/src/lib/auth.ts @@ -8,6 +8,7 @@ import { OAuthMetadata, } from "@modelcontextprotocol/sdk/shared/auth.js"; import { SESSION_KEYS, getServerSpecificKey } from "./constants"; +import { generateOAuthState } from "@/utils/oauthUtils"; export const getClientInformationFromSessionStorage = async ({ serverUrl, @@ -75,6 +76,10 @@ export class InspectorOAuthClientProvider implements OAuthClientProvider { return window.location.origin + "/oauth/callback"; } + state(): string | Promise { + return generateOAuthState(); + } + get clientMetadata(): OAuthClientMetadata { return { redirect_uris: [this.redirectUrl], diff --git a/client/src/lib/oauth-state-machine.ts b/client/src/lib/oauth-state-machine.ts index 5078cfe4..eec7ce8d 100644 --- a/client/src/lib/oauth-state-machine.ts +++ b/client/src/lib/oauth-state-machine.ts @@ -12,6 +12,7 @@ import { OAuthMetadataSchema, OAuthProtectedResourceMetadata, } from "@modelcontextprotocol/sdk/shared/auth.js"; +import { generateOAuthState } from "@/utils/oauthUtils"; export interface StateMachineContext { state: AuthDebuggerState; @@ -113,13 +114,6 @@ export const oauthTransitions: Record = { scope = metadata.scopes_supported.join(" "); } - // Generate a random state - const array = new Uint8Array(32); - crypto.getRandomValues(array); - const state = Array.from(array, (byte) => - byte.toString(16).padStart(2, "0"), - ).join(""); - const { authorizationUrl, codeVerifier } = await startAuthorization( context.serverUrl, { @@ -127,7 +121,7 @@ export const oauthTransitions: Record = { clientInformation, redirectUrl: context.provider.redirectUrl, scope, - state: state, + state: generateOAuthState(), resource: context.state.resource ?? undefined, }, ); diff --git a/client/src/utils/__tests__/oauthUtils.ts b/client/src/utils/__tests__/oauthUtils.test.ts similarity index 92% rename from client/src/utils/__tests__/oauthUtils.ts rename to client/src/utils/__tests__/oauthUtils.test.ts index cc9674cb..c42f014e 100644 --- a/client/src/utils/__tests__/oauthUtils.ts +++ b/client/src/utils/__tests__/oauthUtils.test.ts @@ -1,6 +1,7 @@ import { generateOAuthErrorDescription, parseOAuthCallbackParams, + generateOAuthState, } from "@/utils/oauthUtils.ts"; describe("parseOAuthCallbackParams", () => { @@ -75,4 +76,11 @@ describe("generateOAuthErrorDescription", () => { "Error: invalid_request.\nDetails: The request could not be completed as dialed.\nMore info: https://example.com/error-docs.", ); }); + + describe("generateOAuthState", () => { + it("Returns a string", () => { + expect(generateOAuthState()).toBeDefined(); + expect(generateOAuthState()).toHaveLength(64); + }); + }); }); diff --git a/client/src/utils/oauthUtils.ts b/client/src/utils/oauthUtils.ts index c971271e..83de080b 100644 --- a/client/src/utils/oauthUtils.ts +++ b/client/src/utils/oauthUtils.ts @@ -48,6 +48,20 @@ export const parseOAuthCallbackParams = (location: string): CallbackParams => { }; }; +/** + * Generate a random state for the OAuth 2.0 flow. + * + * @returns A random state for the OAuth 2.0 flow. + */ +export const generateOAuthState = () => { + // Generate a random state + const array = new Uint8Array(32); + crypto.getRandomValues(array); + return Array.from(array, (byte) => byte.toString(16).padStart(2, "0")).join( + "", + ); +}; + export const generateOAuthErrorDescription = ( params: Extract, ): string => { From 60049a5b6ce1c8ad9ebc82960fd7e5c8287f1fe5 Mon Sep 17 00:00:00 2001 From: Konstantin Konstantinov Date: Tue, 12 Aug 2025 00:29:53 +0300 Subject: [PATCH 2/2] reorder methods --- client/src/lib/auth.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/src/lib/auth.ts b/client/src/lib/auth.ts index 73217a60..0e044037 100644 --- a/client/src/lib/auth.ts +++ b/client/src/lib/auth.ts @@ -76,10 +76,6 @@ export class InspectorOAuthClientProvider implements OAuthClientProvider { return window.location.origin + "/oauth/callback"; } - state(): string | Promise { - return generateOAuthState(); - } - get clientMetadata(): OAuthClientMetadata { return { redirect_uris: [this.redirectUrl], @@ -91,6 +87,10 @@ export class InspectorOAuthClientProvider implements OAuthClientProvider { }; } + state(): string | Promise { + return generateOAuthState(); + } + async clientInformation() { // Try to get the preregistered client information from session storage first const preregisteredClientInformation =