Redirect onLoad only if not authenticated with @azure/msal-react

Viewed 6754

I'm trying to adapt the sample project for my needs.

My needs are essentially:

  1. Automatically redirect login if the user is not authenticated when they navigate to the root route...
  2. If they are, load the protected child components.

I have step 1. working as expected. However, after they have been signed in it seems like it is trying to reroute again and I get:

interaction_in_progress: Interaction is currently in progress. Please ensure that this interaction has been completed before calling an interactive API. For more visit: aka.ms/msaljs/browser-errors

  70 | 
  71 | useEffect(() => {
  72 |   if (!isAuthenticated) {
> 73 |     msalInstance.loginRedirect(loginRequest);
     | ^  74 |   }
  75 | })
  76 | 

It does this whether or not there is the !isAuthenticated conditional.

The usage of the useIsAuthenticated comes from this documentation and appears to evaluate to false even if the user is logged in already.

This is what I have thus far:

import { Configuration, RedirectRequest } from '@azure/msal-browser';

// Config object to be passed to Msal on creation
export const msalConfig: Configuration = {
  auth: {
    clientId: '<client_id>',
    authority: 'https://login.microsoftonline.com/<tenant_id>',
  },
};

// Add here scopes for id token to be used at MS Identity Platform endpoints.
export const loginRequest: RedirectRequest = {
  scopes: ['User.Read'],
};

// Add here the endpoints for MS Graph API services you would like to use.
export const graphConfig = {
  graphMeEndpoint: 'https://graph.microsoft.com/v1.0/me',
};

// index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App';
import { PublicClientApplication } from '@azure/msal-browser';
import { msalConfig } from './components/authConfig';
import { MsalProvider } from '@azure/msal-react';

const msalInstance = new PublicClientApplication(msalConfig);

ReactDOM.render(
  <React.StrictMode>
    <MsalProvider instance={msalInstance}>
      <App />
    </MsalProvider>
  </React.StrictMode>,
  document.getElementById('root')
);
// App.tsx
import React, { useEffect } from 'react';
import {
  AuthenticatedTemplate,
  UnauthenticatedTemplate,
  useMsal,
  useIsAuthenticated,
} from '@azure/msal-react';
import { loginRequest } from './authConfig';

const App = () => {
  const isAuthenticated = useIsAuthenticated();
  const { instance } = useMsal();

  console.log(isAuthenticated);

  useEffect(() => {
    if (!isAuthenticated) {
      instance.loginRedirect(loginRequest);
    }
  });

  return (
    <div className='App'>
      <AuthenticatedTemplate>
        <div>Signed in...</div>
      </AuthenticatedTemplate>

      <UnauthenticatedTemplate>
        <div>Signing in...</div>
      </UnauthenticatedTemplate>
    </div>
  );
};

export default App;

Suggestions for how to resolve this?

4 Answers

Ok, I was able to sort this out with some help:

const App = () => {
  const isAuthenticated = useIsAuthenticated();
  const { instance, inProgress } = useMsal();

  if (inProgress === InteractionStatus.None && !isAuthenticated) {
    instance.loginRedirect(loginRequest);
  }

  return (
    <div className='App'>
      <AuthenticatedTemplate>
        <div>Signed in...</div>
      </AuthenticatedTemplate>

      <UnauthenticatedTemplate>
        <div>Signing in...</div>
      </UnauthenticatedTemplate>
    </div>
  );
};

export default App;

You can use simply MsalAuthenticationTemplate component instead of AuthenticatedTemplate/UnauthenticatedTemplate:

import React from "react";
import { MsalAuthenticationTemplate } from "@azure/msal-react";
import { InteractionType } from "@azure/msal-browser";

function ErrorComponent({error}) {
    return <p>An Error Occurred: {error}</p>;
}

function LoadingComponent() {
    return <p>Authentication in progress...</p>;
}

export function Example() {
    const authRequest = {
        scopes: ["openid", "profile"]
    };

    return (
        // authenticationRequest, errorComponent and loadingComponent props are optional
        <MsalAuthenticationTemplate 
            interactionType={InteractionType.Popup} 
            errorComponent={ErrorComponent} 
            loadingComponent={LoadingComponent}
        >
            <p>At least one account is signed in!</p>
        </MsalAuthenticationTemplate>
      )
};

As per @cjones solution I tried several approaches tweaking the solution a bit to get a better version suitable for me and posting the same here. This would help if someone has same issue.

There are 2 issues with his solution.

  1. LoginRedirect was being called twice.
  2. If you have any API calls to be made after authentication success, that would get cancelled first because of the second call for LoginRedirect. It executes after second LoginRedirect call(Though, this second login attempt will not ask for credentials, but it does the refreshing of page.

I tried to solve this problem with following approach.

const App = () => {
    const isAuthenticated = useIsAuthenticated();   
    const { inProgress, instance, accounts } = useMsal();

    if (inProgress === InteractionStatus.None && !isAuthenticated) {
        setTimeout(() => {
            if (accounts.length === 0) {
                instance.loginRedirect();
            }
        }, 500)
    }
 
 /** Rest of the code here **/

}

A better solution is to put an MsalAuthenticationTemplate in the Router in App.jsx like so:

return (
    <Router>
      <MsalProvider instance={instance}>
        <MsalAuthenticationTemplate
          interactionType={InteractionType.Redirect}
          authenticationRequest={authRequest}
          errorComponent={ErrorComponent}
          loadingComponent={Loading}
          >

This has the effect of causing a redirect to sign-in page when trying to access any route within the MsalAuthenticationTemplate.

Related