diff --git a/src/App.tsx b/src/App.tsx index 48bc69e..1ce9a7d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -4,17 +4,24 @@ import { ConversationApp } from '@/components/ConversationApp' import { OAuthCallback } from '@/components/OAuthCallback' function App() { - // Simple routing based on pathname - const pathname = window.location.pathname; - const isInferenceOAuthCallback = pathname === '/oauth/inference/callback'; - const isMcpOAuthCallback = pathname === '/oauth/mcp/callback'; - - if (isInferenceOAuthCallback) { - return ; + // Check if this is an OAuth callback based on query parameters + const urlParams = new URLSearchParams(window.location.search); + const code = urlParams.get('code'); + const state = urlParams.get('state'); + + // Determine OAuth callback type from state parameter + let oauthType: 'inference' | 'mcp' | null = null; + if (code && state) { + // The state parameter includes the callback type + if (state.includes('inference:')) { + oauthType = 'inference'; + } else if (state.includes('mcp:')) { + oauthType = 'mcp'; + } } - if (isMcpOAuthCallback) { - return ; + if (oauthType) { + return ; } return ( diff --git a/src/mcp/connection.ts b/src/mcp/connection.ts index 22ec803..e00cadd 100644 --- a/src/mcp/connection.ts +++ b/src/mcp/connection.ts @@ -69,7 +69,7 @@ class MCPOAuthProvider implements OAuthClientProvider { } get redirectUrl(): string { - return `${window.location.origin}/oauth/mcp/callback`; + return `${window.location.origin}${window.location.pathname}`; } get clientMetadata(): OAuthClientMetadata { @@ -85,7 +85,7 @@ class MCPOAuthProvider implements OAuthClientProvider { state(): string { // Encode connection ID in the state parameter for callback identification const randomPart = this.generateRandomString(8); - return `${this.connectionId}.${randomPart}`; + return `mcp:${this.connectionId}.${randomPart}`; } // Generate a consistent key for the server based on URL @@ -141,7 +141,7 @@ class MCPOAuthProvider implements OAuthClientProvider { // Extract connection ID from state parameter const state = event.data.state; - if (!state || !state.startsWith(this.connectionId + '.')) return; + if (!state || !state.startsWith(`mcp:${this.connectionId}.`)) return; window.removeEventListener('message', handleMessage); popup.close(); diff --git a/src/providers/openrouter/oauth-provider.ts b/src/providers/openrouter/oauth-provider.ts index abbfb3f..15f8623 100644 --- a/src/providers/openrouter/oauth-provider.ts +++ b/src/providers/openrouter/oauth-provider.ts @@ -33,7 +33,7 @@ export class OpenRouterOAuthProvider extends InferenceProvider { constructor(config?: OpenRouterOAuthConfig) { super(); this.oauthConfig = { - redirectUri: `${window.location.origin}/oauth/inference/callback`, + redirectUri: `${window.location.origin}${window.location.pathname}`, ...config, }; this.client = new OpenRouterClient(config || {}); @@ -253,7 +253,11 @@ export class OpenRouterOAuthProvider extends InferenceProvider { } const storedState: OAuthState = JSON.parse(storedStateJson); - if (storedState.state !== state || Date.now() > storedState.expiresAt) { + // Extract the actual state without the prefix for comparison + const stateWithoutPrefix = state.replace(/^inference:/, ''); + const storedStateWithoutPrefix = storedState.state.replace(/^inference:/, ''); + + if (storedStateWithoutPrefix !== stateWithoutPrefix || Date.now() > storedState.expiresAt) { localStorage.removeItem('openrouter_oauth_state'); throw new Error('Invalid or expired OAuth state'); } @@ -319,7 +323,9 @@ export class OpenRouterOAuthProvider extends InferenceProvider { private generateState(): string { const array = new Uint8Array(16); crypto.getRandomValues(array); - return btoa(String.fromCharCode.apply(null, Array.from(array))); + const randomPart = btoa(String.fromCharCode.apply(null, Array.from(array))); + // Include callback type in state for routing + return `inference:${randomPart}`; } private storeTokens(): void { diff --git a/vite.config.ts b/vite.config.ts index 2d42021..f4734f6 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -5,6 +5,9 @@ import path from 'path' // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], + // Set base to '/' for local testing, or to your repo name for GitHub Pages + // e.g., base: '/example-remote-client/' for https://username.github.io/example-remote-client/ + base: '/', resolve: { alias: { '@': path.resolve(__dirname, './src'),