仅在未使用 @azure/msal-react 进行身份验证时重定向 onLoad

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

我正在尝试调整 sample project 以满足我的需要。

我的需求本质上是:

  1. 如果用户导航到根路由时未通过身份验证,则自动重定向登录...
  2. 如果是,加载受保护的子组件。

我已完成第 1 步。按预期工作。但是,在他们登录后,它似乎正在尝试再次重新路由,我得到:

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 | 

无论是否有 !isAuthenticated 条件,它都会执行此操作。

useIsAuthenticated 的用法来自 this documentation,即使用户已经登录,它的计算结果似乎也是 false

这是我目前所拥有的:

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;

关于如何解决这个问题的建议?

好的,我在一些帮助下解决了这个问题:

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;

根据@cjones 解决方案,我尝试了几种方法来稍微调整解决方案以获得适合我的更好版本并在此处发布相同版本。如果有人有同样的问题,这会有所帮助。

他的解决方案有 2 个问题。

  1. LoginRedirect 被调用了两次。
  2. 如果您在身份验证成功后有任何 API 调用要进行,由于第二次调用 LoginRedirect,该调用将首先被取消。它在第二次 LoginRedirect 调用后执行(尽管第二次登录尝试不会要求提供凭据,但它会刷新页面。

我尝试通过以下方法解决这个问题。

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 **/

}

您可以简单地使用 MsalAuthenticationTemplate component 而不是 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>
      )
};

更好的解决方案是在 App.jsx 中的路由器中放置一个 MsalAuthenticationTemplate,如下所示:

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

这会导致在尝试访问 MsalAuthenticationTemplate 中的任何路由时重定向到登录页面。