Spaces:
Running
Running
| 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<OAuthCallbackProps> = ({ | |
| serverUrl, | |
| onSuccess, | |
| onError, | |
| }) => { | |
| const [status, setStatus] = useState<string>("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 <div>{status}</div>; | |
| }; | |
| export default OAuthCallback; | |