skip to Main Content

I’m trying to create a react SPA, using AzureAD/MSAL for authentication. I have a Landing page at / with a Login button and a separate app Home page on a /home route. If someone tries to navigate directly to /home without authenticating, I want to redirect them to the Landing page.

I do this with:

  const navigate = useNavigate();
  const { instance, inProgress } = useMsal();
  const isAuthenticated = useIsAuthenticated();

  useEffect(() => {
    if (inProgress === InteractionStatus.None && !isAuthenticated) {
      navigate('/');
    }
  }, [inProgress, isAuthenticated, navigate]);

Which seems to work as intended when navigating directly to /home.

The Issue I have is when the user first goes to the Landing page and logs in, it goes to /home then this redirect still kicks in and takes the user back to the Landing page. On clicking the Login button again, it does go to /home successfully. Is this because the first time /home is loaded before authentication is fully complete? How do I wait for auth to complete?

Code:

import React, { useEffect } from 'react';
import { BrowserRouter as Router, Routes, Route, Link, useNavigate } from 'react-router-dom';
import { MsalProvider, useMsal, useIsAuthenticated } from '@azure/msal-react';
import { InteractionStatus } from '@azure/msal-browser';
import ReactDOM from 'react-dom/client';
import { PublicClientApplication } from '@azure/msal-browser';
import { msalConfig } from './js/authConfig';

const LandingPage = () => {
  const { instance } = useMsal();

  const handleLogin = () => {
    instance.loginRedirect();
  };

  return (
    <div>
      <h1>Landing Page</h1>
      <button onClick={handleLogin}>Login</button>
    </div>
  );
};

const HomePage = () => {
  const navigate = useNavigate();
  const { instance, inProgress } = useMsal();
  const isAuthenticated = useIsAuthenticated();

  useEffect(() => {
    if (inProgress === InteractionStatus.None && !isAuthenticated) {
      navigate('/');
    }
  }, [inProgress, isAuthenticated, navigate]);

  return (
    <div>
      <h1>Home Page</h1>
    </div>
  );
};

const msalInstance = new PublicClientApplication(msalConfig);

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <MsalProvider instance={msalInstance}>
      <Router>
        <Routes>
          <Route path="/" element={<LandingPage />} />
          <Route path="/home" element={<HomePage />} />
        </Routes>
      </Router>
    </MsalProvider>
  </React.StrictMode>
);

My msalConfig has

  redirectUri: "/home",
  navigateToLoginRequestUrl: false,

2

Answers


  1. try with setting loading state

    const HomePage = () => {
      const navigate = useNavigate();
      const { instance, inProgress } = useMsal();
      const isAuthenticated = useIsAuthenticated();
    const [isLoading, setIsLoading] = useState(true);
      useEffect(() => {
        if (inProgress === InteractionStatus.None && !isAuthenticated) {
          navigate('/');
        }
    else{
    setIsLoading(false)
    }
      }, [inProgress, isAuthenticated, navigate]);
    
      return (
    <>
    {
    !loading && ( <div>
          <h1>Home Page</h1>
        </div>)
    }
    </>
      );
    };
    
    const msalInstance = new PublicClientApplication(msalConfig);
    
    ReactDOM.createRoot(document.getElementById('root')).render(
      <React.StrictMode>
        <MsalProvider instance={msalInstance}>
          <Router>
            <Routes>
              <Route path="/" element={<LandingPage />} />
              <Route path="/home" element={<HomePage />} />
            </Routes>
          </Router>
        </MsalProvider>
      </React.StrictMode>
    );
    
    Login or Signup to reply.
  2. From what I can find it seems that this is the correct way to handle this, when the HomePage component mounts, the authentification process should be either in progress or already finished and the user is authenticated unless there is a moment where both are false juste before isAuthenticated passes to true.

    you still can try this:

    const [isAuthCompleted, setIsAuthCompleted] = useState(null);
    
    useEffect(() => {
      if ( inProgress === InteractionStatus.None && !isAuthenticated && isAuthCompleted !== null) {
       if(!isAuthCompleted) navigate("/");
      }
    }, [inProgress, isAuthenticated, navigate]);
    
    useEffect(() => {
      if (isAuthenticated) {
        setIsAuthCompleted(true);
      }else {
        setIsAuthCompleted(false); 
      }
    }, [isAuthenticated]);
    
    

    if you cannot handle it this way then what about navigate to the HomePage only when the user is successfully authenticated? just leave the HomePage as it is and on the landing page, when you click the login button, you just login without redirect and set isAuthCompleted state to true then when isAuthenticated is true you navigate to the home page, it seems more logical, redirecting the user to the homepage only when he finishes the authentication process:

    const navigate = useNavigate();
    const isAuthenticated = useIsAuthenticated();
    
    const [isAuthCompleted, setIsAuthCompleted] = useState(false);
    
    useEffect(() => {
      if (isAuthCompleted && isAuthenticated) {
        navigate("/home");
      }
    }, [isAuthCompleted, isAuthenticated]);
    
    return (
      <>
        <button
          onClick={() => {
            //... authentificate with Azure
            setIsAuthCompleted(true);
          }}
        >
          login
        </button>
      </>
    )
    

    this should solve the issue.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search