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; | |