import { useEffect, useState } from 'react'; import { useNavigate, useSearchParams } from 'react-router-dom'; import { springAuth } from '@app/auth/springAuthClient'; import { useAuth } from '@app/auth/UseSession'; import { useTranslation } from 'react-i18next'; import { useDocumentMeta } from '@app/hooks/useDocumentMeta'; import AuthLayout from '@app/routes/authShared/AuthLayout'; // Import login components import LoginHeader from '@app/routes/login/LoginHeader'; import ErrorMessage from '@app/routes/login/ErrorMessage'; import EmailPasswordForm from '@app/routes/login/EmailPasswordForm'; import OAuthButtons, { DEBUG_SHOW_ALL_PROVIDERS, oauthProviderConfig } from '@app/routes/login/OAuthButtons'; import DividerWithText from '@app/components/shared/DividerWithText'; import LoggedInState from '@app/routes/login/LoggedInState'; import { BASE_PATH } from '@app/constants/app'; export default function Login() { const navigate = useNavigate(); const [searchParams] = useSearchParams(); const { session, loading } = useAuth(); const { t } = useTranslation(); const [isSigningIn, setIsSigningIn] = useState(false); const [error, setError] = useState(null); const [successMessage, setSuccessMessage] = useState(null); const [showEmailForm, setShowEmailForm] = useState(false); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [enabledProviders, setEnabledProviders] = useState([]); const [hasSSOProviders, setHasSSOProviders] = useState(false); const [_enableLogin, setEnableLogin] = useState(null); // Fetch enabled SSO providers and login config from backend useEffect(() => { const fetchProviders = async () => { try { const response = await fetch(`${BASE_PATH}/api/v1/proprietary/ui-data/login`); if (response.ok) { const data = await response.json(); // Check if login is disabled - if so, redirect to home if (data.enableLogin === false) { console.debug('[Login] Login disabled, redirecting to home'); navigate('/'); return; } setEnableLogin(data.enableLogin ?? true); // Extract provider IDs from the providerList map // The keys are like "/oauth2/authorization/google" - extract the last part const providerIds = Object.keys(data.providerList || {}) .map(key => key.split('/').pop()) .filter((id): id is string => id !== undefined); setEnabledProviders(providerIds); } } catch (err) { console.error('[Login] Failed to fetch enabled providers:', err); } }; fetchProviders(); }, [navigate]); // Update hasSSOProviders and showEmailForm when enabledProviders changes useEffect(() => { // In debug mode, check if any providers exist in the config const hasProviders = DEBUG_SHOW_ALL_PROVIDERS ? Object.keys(oauthProviderConfig).length > 0 : enabledProviders.length > 0; setHasSSOProviders(hasProviders); // If no SSO providers, show email form by default if (!hasProviders) { setShowEmailForm(true); } }, [enabledProviders]); // Handle query params (email prefill, success messages, and session expiry) useEffect(() => { try { const emailFromQuery = searchParams.get('email'); if (emailFromQuery) { setEmail(emailFromQuery); } // Check if session expired (401 redirect) const expired = searchParams.get('expired'); if (expired === 'true') { setError(t('login.sessionExpired', 'Your session has expired. Please sign in again.')); } const messageType = searchParams.get('messageType') if (messageType) { switch (messageType) { case 'accountCreated': setSuccessMessage(t('login.accountCreatedSuccess', 'Account created successfully! You can now sign in.')) break case 'passwordChanged': setSuccessMessage(t('login.passwordChangedSuccess', 'Password changed successfully! Please sign in with your new password.')) break case 'credsUpdated': setSuccessMessage(t('login.credentialsUpdated', 'Your credentials have been updated. Please sign in again.')) break } } } catch (_) { // ignore } }, [searchParams, t]); const baseUrl = window.location.origin + BASE_PATH; // Set document meta useDocumentMeta({ title: `${t('login.title', 'Sign in')} - Stirling PDF`, description: t('app.description', 'The Free Adobe Acrobat alternative (10M+ Downloads)'), ogTitle: `${t('login.title', 'Sign in')} - Stirling PDF`, ogDescription: t('app.description', 'The Free Adobe Acrobat alternative (10M+ Downloads)'), ogImage: `${baseUrl}/og_images/home.png`, ogUrl: `${window.location.origin}${window.location.pathname}` }); // Show logged in state if authenticated if (session && !loading) { return ; } const signInWithProvider = async (provider: 'github' | 'google' | 'apple' | 'azure' | 'keycloak' | 'oidc') => { try { setIsSigningIn(true); setError(null); console.log(`[Login] Signing in with ${provider}`); // Redirect to Spring OAuth2 endpoint const { error } = await springAuth.signInWithOAuth({ provider, options: { redirectTo: `${BASE_PATH}/auth/callback` } }); if (error) { console.error(`[Login] ${provider} error:`, error); setError(t('login.failedToSignIn', { provider, message: error.message }) || `Failed to sign in with ${provider}`); } } catch (err) { console.error(`[Login] Unexpected error:`, err); setError(t('login.unexpectedError', { message: err instanceof Error ? err.message : 'Unknown error' }) || 'An unexpected error occurred'); } finally { setIsSigningIn(false); } }; const signInWithEmail = async () => { if (!email || !password) { setError(t('login.pleaseEnterBoth') || 'Please enter both email and password'); return; } try { setIsSigningIn(true); setError(null); console.log('[Login] Signing in with email:', email); const { user, session, error } = await springAuth.signInWithPassword({ email: email.trim(), password: password }); if (error) { console.error('[Login] Email sign in error:', error); setError(error.message); } else if (user && session) { console.log('[Login] Email sign in successful'); // Auth state will update automatically and Landing will redirect to home // No need to navigate manually here } } catch (err) { console.error('[Login] Unexpected error:', err); setError(t('login.unexpectedError', { message: err instanceof Error ? err.message : 'Unknown error' }) || 'An unexpected error occurred'); } finally { setIsSigningIn(false); } }; // Forgot password handler (currently unused, reserved for future implementation) // const handleForgotPassword = () => { // navigate('/auth/reset'); // }; return ( {/* Success message */} {successMessage && (

{successMessage}

)} {/* OAuth first */} {/* Divider between OAuth and Email - only show if SSO is available */} {hasSSOProviders && ( )} {/* Sign in with email button - only show if SSO providers exist */} {hasSSOProviders && !showEmailForm && (
)} {/* Email form - show by default if no SSO, or when button clicked */} {showEmailForm && (
)}
); }