import { useEffect, useState } from "react"; import { exchangeCodeForToken } from "../services/oauth"; import { secureStorage } from "../utils/storage"; import type { MCPServerConfig } from "../types/mcp"; import { STORAGE_KEYS, DEFAULTS } from "../config/constants"; interface OAuthTokens { access_token: string; refresh_token?: string; expires_in?: number; token_type?: string; [key: string]: string | number | undefined; } interface OAuthCallbackProps { serverUrl: string; onSuccess?: (tokens: OAuthTokens) => void; onError?: (error: Error) => void; } const OAuthCallback: React.FC = ({ serverUrl, onSuccess, onError, }) => { const [status, setStatus] = useState("Authorizing..."); useEffect(() => { const params = new URLSearchParams(window.location.search); const code = params.get("code"); // Always persist MCP server URL for robustness localStorage.setItem(STORAGE_KEYS.OAUTH_MCP_SERVER_URL, serverUrl); if (code) { exchangeCodeForToken({ serverUrl, code, redirectUri: window.location.origin + DEFAULTS.OAUTH_REDIRECT_PATH, }) .then(async (tokens) => { await secureStorage.setItem(STORAGE_KEYS.OAUTH_ACCESS_TOKEN, tokens.access_token); // Add MCP server to MCPClientService for UI const mcpServerUrl = localStorage.getItem(STORAGE_KEYS.OAUTH_MCP_SERVER_URL); if (mcpServerUrl) { // Use persisted name and transport from initial add const serverName = localStorage.getItem(STORAGE_KEYS.MCP_SERVER_NAME) || mcpServerUrl; const serverTransport = (localStorage.getItem(STORAGE_KEYS.MCP_SERVER_TRANSPORT) as MCPServerConfig['transport']) || DEFAULTS.MCP_TRANSPORT; // Build config and add to mcp-servers const serverConfig = { id: `server_${Date.now()}`, name: serverName, url: mcpServerUrl, enabled: true, transport: serverTransport, auth: { type: "bearer" as const, token: tokens.access_token, }, }; // Load existing servers let servers: MCPServerConfig[] = []; try { const stored = localStorage.getItem(STORAGE_KEYS.MCP_SERVERS); if (stored) servers = JSON.parse(stored); } catch {} // Add or update const exists = servers.some((s: MCPServerConfig) => s.url === mcpServerUrl); if (!exists) { servers.push(serverConfig); localStorage.setItem(STORAGE_KEYS.MCP_SERVERS, JSON.stringify(servers)); } // Clear temp values from localStorage for clean slate localStorage.removeItem(STORAGE_KEYS.MCP_SERVER_NAME); localStorage.removeItem(STORAGE_KEYS.MCP_SERVER_TRANSPORT); localStorage.removeItem(STORAGE_KEYS.OAUTH_MCP_SERVER_URL); } setStatus("Authorization successful! Redirecting..."); if (onSuccess) onSuccess(tokens); // Redirect to main app page after short delay setTimeout(() => { window.location.replace("/"); }, 1000); }) .catch((err) => { setStatus("OAuth token exchange failed: " + err.message); if (onError) onError(err); }); } else { setStatus("Missing authorization code in callback URL."); } }, [serverUrl, onSuccess, onError]); return
{status}
; }; export default OAuthCallback;