import React, { useState, useEffect } from 'react'; import { login, getUserStatus, logout } from '../services/apiService'; const LoginPage = () => { const [user, setUser] = useState(null); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); useEffect(() => { // We now look for a 'user_id' in the URL, which is provided by the backend // after a successful OIDC login and callback. const params = new URLSearchParams(window.location.search); const userIdFromUrl = params.get('user_id'); // First, check localStorage for a saved user ID for persistent login const storedUserId = localStorage.getItem('userId'); const userId = userIdFromUrl || storedUserId; if (userId) { setIsLoading(true); // Fetch the full user details using the user ID from the URL. // This is a more secure and robust way to handle the final callback. const fetchUserDetails = async () => { try { const userStatus = await getUserStatus(userId); setUser(userStatus); // Store the user ID for future requests (e.g., in localStorage) localStorage.setItem('userId', userStatus.id); // Clean up the URL by removing the query parameter window.history.replaceState({}, document.title, window.location.pathname); } catch (err) { setError('Failed to get user status. Please try again.'); console.error(err); } finally { setIsLoading(false); } }; fetchUserDetails(); } }, []); const handleLogin = () => { // Redirect to the backend's /users/login endpoint // The backend handles the OIDC redirect from there. login(); }; const handleLogout = async () => { setIsLoading(true); try { await logout(); localStorage.removeItem('userId'); setUser(null); setError(null); } catch (err) { setError('Failed to log out. Please try again.'); console.error(err); } finally { setIsLoading(false); } }; const renderContent = () => { if (isLoading) { return ( <div className="flex items-center justify-center p-4"> <svg className="animate-spin h-5 w-5 mr-3 text-blue-500" viewBox="0 0 24 24"> <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle> <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path> </svg> <span className="text-gray-600 dark:text-gray-400">Processing login...</span> </div> ); } if (error) { return ( <div className="p-4 bg-red-100 dark:bg-red-900 border border-red-400 dark:border-red-600 rounded-lg text-red-700 dark:text-red-300"> <p className="font-bold">Error:</p> <p>{error}</p> </div> ); } if (user) { return ( <div className="p-4 text-green-700 dark:text-green-300 bg-green-100 dark:bg-green-900 border border-green-400 dark:border-green-600 rounded-lg"> <h3 className="text-xl font-bold mb-2">Login Successful!</h3> <p>Welcome, <span className="font-semibold">{user.email}</span>.</p> <p>User ID: <span className="font-mono text-sm break-all">{user.id}</span></p> <button onClick={handleLogout} className="mt-4 w-full bg-red-600 hover:bg-red-700 text-white font-bold py-3 px-4 rounded-lg transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-opacity-50" > Log Out </button> </div> ); } return ( <> <h2 className="text-3xl font-bold mb-4">Login</h2> <p className="mb-6 text-gray-600 dark:text-gray-400"> Click the button below to log in using OpenID Connect (OIDC). </p> <button onClick={handleLogin} className="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-4 rounded-lg transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50" > Login with OIDC </button> </> ); }; return ( <div className="flex flex-col items-center justify-center h-full min-h-screen bg-gray-100 dark:bg-gray-900 text-gray-900 dark:text-gray-100"> <div className="bg-white dark:bg-gray-800 p-8 rounded-lg shadow-xl text-center max-w-sm w-full"> {renderContent()} </div> </div> ); }; export default LoginPage;