import type { OAuthClientInformation, OAuthTokens, OAuthMetadata, } from "@modelcontextprotocol/sdk/shared/auth.js"; import { OAuthClientInformationSchema, OAuthTokensSchema, } from "@modelcontextprotocol/sdk/shared/auth.js"; import type { OAuthStorage } from "./storage.js"; import { type createOAuthStore, type ServerOAuthState } from "./store.js"; /** * Concrete OAuthStorage implementation parameterized on a Zustand store. * The store carries the storage adapter (sessionStorage, file, remote HTTP, …), * so the same body works for browser, Node, and remote environments. */ export class OAuthStorageBase implements OAuthStorage { private readonly store: ReturnType; constructor(store: ReturnType) { this.store = store; } async getClientInformation( serverUrl: string, isPreregistered?: boolean, ): Promise { const state = this.store.getState().getServerState(serverUrl); const clientInfo = isPreregistered ? state.preregisteredClientInformation : state.clientInformation; if (!clientInfo) { return undefined; } return await OAuthClientInformationSchema.parseAsync(clientInfo); } async saveClientInformation( serverUrl: string, clientInformation: OAuthClientInformation, ): Promise { this.store.getState().setServerState(serverUrl, { clientInformation, }); } async savePreregisteredClientInformation( serverUrl: string, clientInformation: OAuthClientInformation, ): Promise { this.store.getState().setServerState(serverUrl, { preregisteredClientInformation: clientInformation, }); } clearClientInformation(serverUrl: string, isPreregistered?: boolean): void { const updates: Partial = {}; if (isPreregistered) { updates.preregisteredClientInformation = undefined; } else { updates.clientInformation = undefined; } this.store.getState().setServerState(serverUrl, updates); } async getTokens(serverUrl: string): Promise { const state = this.store.getState().getServerState(serverUrl); if (!state.tokens) { return undefined; } return await OAuthTokensSchema.parseAsync(state.tokens); } async saveTokens(serverUrl: string, tokens: OAuthTokens): Promise { this.store.getState().setServerState(serverUrl, { tokens }); } clearTokens(serverUrl: string): void { this.store.getState().setServerState(serverUrl, { tokens: undefined }); } getCodeVerifier(serverUrl: string): string | undefined { const state = this.store.getState().getServerState(serverUrl); return state.codeVerifier; } async saveCodeVerifier( serverUrl: string, codeVerifier: string, ): Promise { this.store.getState().setServerState(serverUrl, { codeVerifier }); } clearCodeVerifier(serverUrl: string): void { this.store .getState() .setServerState(serverUrl, { codeVerifier: undefined }); } getScope(serverUrl: string): string | undefined { const state = this.store.getState().getServerState(serverUrl); return state.scope; } async saveScope(serverUrl: string, scope: string | undefined): Promise { this.store.getState().setServerState(serverUrl, { scope }); } clearScope(serverUrl: string): void { this.store.getState().setServerState(serverUrl, { scope: undefined }); } getServerMetadata(serverUrl: string): OAuthMetadata | null { const state = this.store.getState().getServerState(serverUrl); return state.serverMetadata || null; } async saveServerMetadata( serverUrl: string, metadata: OAuthMetadata, ): Promise { this.store .getState() .setServerState(serverUrl, { serverMetadata: metadata }); } clearServerMetadata(serverUrl: string): void { this.store .getState() .setServerState(serverUrl, { serverMetadata: undefined }); } clear(serverUrl: string): void { this.store.getState().clearServerState(serverUrl); } }