Newer
Older
cortex-hub / ui / client-app / src / pages / LoginPage.js
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;