import { useEffect, useState, useCallback, Suspense, useRef } from 'react'; import { useNavigate, useSearchParams } from 'react-router-dom'; import { httpClient } from '@/app/infra/http/HttpClient'; import { beginAuthenticatedSession, beginSupportAdminSession, bootstrapWorkspaceSession, getPendingInvitationToken, } from '@/app/infra/http'; import { toast } from 'sonner'; import { useTranslation } from 'react-i18next'; import { Loader2, AlertCircle, CheckCircle2, AlertTriangle, } from 'lucide-react'; import { Card, CardContent, CardHeader, CardTitle, CardDescription, } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { LoadingSpinner } from '@/components/ui/loading-spinner'; import langbotIcon from '@/app/assets/langbot-logo.webp'; type SpaceOAuthLoginResult = { token: string; user?: string; workspace_uuid?: string; principal_type?: 'account' | 'support_admin'; actor_account_uuid?: string; }; const pendingSpaceOAuthLogins = new Map< string, Promise >(); function getOrCreateSpaceOAuthLoginPromise( authCode: string, state: string, workspaceUuid?: string, launchAssertion?: string, ): Promise { const requestKey = `${authCode}:${state}:${workspaceUuid ?? ''}:${launchAssertion ?? ''}`; const pendingRequest = pendingSpaceOAuthLogins.get(requestKey); if (pendingRequest) { return pendingRequest; } const requestPromise = httpClient .exchangeSpaceOAuthCode(authCode, state, workspaceUuid, launchAssertion) .finally(() => { pendingSpaceOAuthLogins.delete(requestKey); }); pendingSpaceOAuthLogins.set(requestKey, requestPromise); return requestPromise; } function SpaceOAuthCallbackContent() { const navigate = useNavigate(); const [searchParams] = useSearchParams(); const { t } = useTranslation(); const isMountedRef = useRef(true); const directLaunchFragmentRef = useRef<{ workspaceUuid: string | null; launchAssertion: string | null; } | null>(null); const [status, setStatus] = useState< 'loading' | 'confirm' | 'success' | 'error' >('loading'); const [errorMessage, setErrorMessage] = useState(''); const [terminalErrorCode, setTerminalErrorCode] = useState< 'space_account_not_registered' | 'space_account_binding_required' | null >(null); const [isBindMode, setIsBindMode] = useState(false); const [code, setCode] = useState(null); const [isProcessing, setIsProcessing] = useState(false); const [localEmail, setLocalEmail] = useState(''); const handleOAuthCallback = useCallback( async ( authCode: string, state: string, workspaceUuid?: string, launchAssertion?: string, ) => { try { const response = await getOrCreateSpaceOAuthLoginPromise( authCode, state, workspaceUuid, launchAssertion, ); if (!isMountedRef.current) { return; } if (response.principal_type === 'support_admin') { if (!response.workspace_uuid) { throw new Error('Support admin launch did not include a Workspace'); } beginSupportAdminSession(response.token, response.workspace_uuid); await bootstrapWorkspaceSession(); navigate('/home', { replace: true }); return; } beginAuthenticatedSession(response.token, response.user); if (getPendingInvitationToken()) { navigate('/invitations/accept', { replace: true }); return; } const workspaceResult = await bootstrapWorkspaceSession({ preferredWorkspaceUuid: response.workspace_uuid, }); if (workspaceResult.status === 'unavailable') { throw new Error('No Workspace is available for this Account'); } if (response.workspace_uuid) { navigate('/home', { replace: true }); return; } setStatus('success'); toast.success(t('common.spaceLoginSuccess')); // If wizard state exists, redirect back to wizard instead of home const wizardState = localStorage.getItem('langbot_wizard_state'); const destination = wizardState ? '/wizard' : '/home'; const redirectTo = workspaceResult.status === 'selection-required' ? `/workspaces/select?returnTo=${encodeURIComponent(destination)}` : destination; setTimeout(() => { navigate(redirectTo); }, 1000); } catch (err) { if (!isMountedRef.current) { return; } setStatus('error'); const errorObj = err as { code?: string; msg?: string }; if ( errorObj.code === 'space_account_not_registered' || errorObj.code === 'space_account_binding_required' ) { setTerminalErrorCode(errorObj.code); setErrorMessage(t(`account.${errorObj.code}`)); return; } const errMsg = (errorObj?.msg || '').toLowerCase(); if (errMsg.includes('account email mismatch')) { setErrorMessage(t('account.spaceEmailMismatch')); } else { setErrorMessage(t('common.spaceLoginFailed')); } } }, [navigate, t], ); const [bindState, setBindState] = useState(null); const handleBindAccount = useCallback( async (authCode: string, state: string) => { setIsProcessing(true); try { const response = await httpClient.bindSpaceAccount(authCode, state); if (!isMountedRef.current) { return; } beginAuthenticatedSession(response.token, response.user); const workspaceResult = await bootstrapWorkspaceSession(); if (workspaceResult.status === 'unavailable') { throw new Error('No Workspace is available for this Account'); } setStatus('success'); toast.success(t('account.bindSpaceSuccess')); const redirectTo = workspaceResult.status === 'selection-required' ? '/workspaces/select?returnTo=%2Fhome' : '/home'; setTimeout(() => { navigate(redirectTo); }, 1000); } catch (err) { if (!isMountedRef.current) { return; } setStatus('error'); const errorObj = err as { code?: string; msg?: string }; if (errorObj.code === 'space_account_email_mismatch') { setErrorMessage(t('account.spaceEmailMismatch')); return; } const errMsg = (errorObj?.msg || '').toLowerCase(); if (errMsg.includes('account email mismatch')) { setErrorMessage(t('account.spaceEmailMismatch')); } else { setErrorMessage(t('account.bindSpaceFailed')); } } finally { if (isMountedRef.current) { setIsProcessing(false); } } }, [navigate, t], ); useEffect(() => { isMountedRef.current = true; const authCode = searchParams.get('code'); const error = searchParams.get('error'); const errorDescription = searchParams.get('error_description'); const mode = searchParams.get('mode'); const state = searchParams.get('state'); if (directLaunchFragmentRef.current === null) { const fragmentParams = new URLSearchParams( window.location.hash.startsWith('#') ? window.location.hash.slice(1) : window.location.hash, ); directLaunchFragmentRef.current = { workspaceUuid: fragmentParams.get('workspace_uuid'), launchAssertion: fragmentParams.get('launch_assertion'), }; if (window.location.hash) { window.history.replaceState( null, '', `${window.location.pathname}${window.location.search}`, ); } } const workspaceUuid = directLaunchFragmentRef.current.workspaceUuid ?? searchParams.get('workspace_uuid'); const launchAssertion = directLaunchFragmentRef.current.launchAssertion; if (error) { setStatus('error'); setErrorMessage( errorDescription || error || t('common.spaceLoginFailed'), ); return; } if (mode === 'bind') { if (!authCode) { setStatus('error'); setErrorMessage(t('common.spaceLoginNoCode')); return; } setCode(authCode); // Bind mode - verify state (token) exists if (!state) { setStatus('error'); setErrorMessage(t('account.bindSpaceInvalidState')); return; } setBindState(state); setIsBindMode(true); setLocalEmail(localStorage.getItem('userEmail') || ''); setStatus('confirm'); } else if (workspaceUuid || launchAssertion) { if (!workspaceUuid || !launchAssertion) { setStatus('error'); setErrorMessage(t('common.spaceLoginFailed')); return; } handleOAuthCallback( authCode ?? '', state ?? '', workspaceUuid, launchAssertion, ); } else { if (!authCode) { setStatus('error'); setErrorMessage(t('common.spaceLoginNoCode')); return; } setCode(authCode); if (!state) { setStatus('error'); setErrorMessage(t('common.spaceLoginFailed')); return; } handleOAuthCallback(authCode, state); } return () => { isMountedRef.current = false; }; }, [searchParams, handleOAuthCallback, t]); const handleConfirmBind = () => { if (code && bindState) { handleBindAccount(code, bindState); } }; const handleCancelBind = () => { navigate('/home'); }; return (
LangBot {status === 'loading' && t('common.spaceLoginProcessing')} {status === 'confirm' && t('account.bindSpaceConfirmTitle')} {status === 'success' && (isBindMode ? t('account.bindSpaceSuccess') : t('common.spaceLoginSuccess'))} {status === 'error' && (terminalErrorCode ? t(`account.${terminalErrorCode}Title`) : isBindMode ? t('account.bindSpaceFailed') : t('common.spaceLoginError'))} {status === 'loading' && t('common.spaceLoginProcessingDescription')} {status === 'confirm' && t('account.bindSpaceConfirmDescription')} {status === 'success' && t('common.spaceLoginSuccessDescription')} {status === 'error' && errorMessage} {status === 'loading' && } {status === 'confirm' && ( <>

{t('account.bindSpaceWarning', { localEmail: localEmail || '-', })}

)} {status === 'success' && ( )} {status === 'error' && ( <> )}
); } function LoadingFallback() { return (
); } export default function SpaceOAuthCallback() { return ( }> ); }