单页应用程序:获取用于调用 API 的令牌

适用于:带白色勾号的绿色圆圈。 员工租户 带灰色 X 符号的白色圆圈。 外部租户(了解详细信息

使用 MSAL.js 获取 API 令牌的模式是首先尝试通过acquireTokenSilent方法进行静默令牌请求。 调用此方法时,该库会首先检查浏览器存储中的缓存,看是否存在未过期的访问令牌,在有的情况下会将其返回。 如果未找到访问令牌或找到的访问令牌已过期,它将尝试使用其刷新令牌来获取新的访问令牌。 如果刷新令牌的 24 小时生存期也已过期,MSAL.js 会打开一个隐藏的 iframe,以通过适用于与 Microsoft Entra ID 的现有活动会话(如果有)按无提示方式请求新的授权代码,然后使用这些代码交换一组新令牌(访问和刷新令牌)。

有关 Microsoft Entra ID 中的单一登录 (SSO) 会话和令牌生存期值的详细信息,请参阅令牌生存期。 有关 MSAL.js 缓存查找策略的详细信息,请参阅:获取访问令牌

可能会因某些原因(例如密码更改,或者更新的条件访问策略)而导致以无提示方式向 Microsoft Entra ID 请求令牌失败。 而失败更常见的原因是刷新令牌的 24 小时生存期已过期以及浏览器阻止第三方 Cookie,这会阻止使用隐藏的 Iframe 继续对用户进行身份验证。 在这些情况下,应调用其中一个交互式方法(可能会提示用户)来获取令牌:

在弹出窗口或重定向体验之间进行选择

在弹出窗口和重定向体验之间进行的选择取决于应用程序流:

  • 如果不希望用户在身份验证期间离开主应用程序页,建议使用弹出窗口方法。 由于身份验证重定向发生在弹出窗口中,系统会保留主应用程序的状态。
  • 如果用户的浏览器约束或策略禁用了弹出窗口,则可使用重定向方法。 请使用重定向方法,因为 Internet Explorer 浏览器弹出窗口存在已知问题。

可以设置在生成访问令牌请求时希望访问令牌包含的 API 范围。 可能不会在访问令牌中授予所有请求的范围。 具体取决于用户的许可。

通过弹出窗口获取令牌

以下代码将前面描述的模式与弹出体验的方法结合起来:

import {
  InteractionRequiredAuthError,
  InteractionStatus,
} from "@azure/msal-browser";
import { AuthenticatedTemplate, useMsal } from "@azure/msal-react";

function ProtectedComponent() {
  const { instance, inProgress, accounts } = useMsal();
  const [apiData, setApiData] = useState(null);

  useEffect(() => {
    if (!apiData && inProgress === InteractionStatus.None) {
      const accessTokenRequest = {
        scopes: ["user.read"],
        account: accounts[0],
      };
      instance
        .acquireTokenSilent(accessTokenRequest)
        .then((accessTokenResponse) => {
          // Acquire token silent success
          let accessToken = accessTokenResponse.accessToken;
          // Call your API with token
          callApi(accessToken).then((response) => {
            setApiData(response);
          });
        })
        .catch((error) => {
          if (error instanceof InteractionRequiredAuthError) {
            instance
              .acquireTokenPopup(accessTokenRequest)
              .then(function (accessTokenResponse) {
                // Acquire token interactive success
                let accessToken = accessTokenResponse.accessToken;
                // Call your API with token
                callApi(accessToken).then((response) => {
                  setApiData(response);
                });
              })
              .catch(function (error) {
                // Acquire token interactive failure
                console.log(error);
              });
          }
          console.log(error);
        });
    }
  }, [instance, accounts, inProgress, apiData]);

  return <p>Return your protected content here: {apiData}</p>;
}

function App() {
  return (
    <AuthenticatedTemplate>
      <ProtectedComponent />
    </AuthenticatedTemplate>
  );
}

或者,如果需要在 React 组件的外部获取令牌,可以调用 acquireTokenSilent,但如果失败,则不应回退到交互。 所有交互都应在组件树中的 MsalProvider 组件下进行。

// MSAL.js v2 exposes several account APIs, logic to determine which account to use is the responsibility of the developer
const account = publicClientApplication.getAllAccounts()[0];

const accessTokenRequest = {
  scopes: ["user.read"],
  account: account,
};

// Use the same publicClientApplication instance provided to MsalProvider
publicClientApplication
  .acquireTokenSilent(accessTokenRequest)
  .then(function (accessTokenResponse) {
    // Acquire token silent success
    let accessToken = accessTokenResponse.accessToken;
    // Call your API with token
    callApi(accessToken);
  })
  .catch(function (error) {
    //Acquire token silent failure
    console.log(error);
  });

通过重定向获取令牌

如果 acquireTokenSilent 失败,则回退到 acquireTokenRedirect。 此方法将启动全帧重定向,并在响应返回到应用程序时对其进行处理。 在从重定向返回后呈现此组件时,acquireTokenSilent 应该会成功,因为将从缓存中拉取令牌。

import {
  InteractionRequiredAuthError,
  InteractionStatus,
} from "@azure/msal-browser";
import { AuthenticatedTemplate, useMsal } from "@azure/msal-react";

function ProtectedComponent() {
  const { instance, inProgress, accounts } = useMsal();
  const [apiData, setApiData] = useState(null);

  useEffect(() => {
    const accessTokenRequest = {
      scopes: ["user.read"],
      account: accounts[0],
    };
    if (!apiData && inProgress === InteractionStatus.None) {
      instance
        .acquireTokenSilent(accessTokenRequest)
        .then((accessTokenResponse) => {
          // Acquire token silent success
          let accessToken = accessTokenResponse.accessToken;
          // Call your API with token
          callApi(accessToken).then((response) => {
            setApiData(response);
          });
        })
        .catch((error) => {
          if (error instanceof InteractionRequiredAuthError) {
            instance.acquireTokenRedirect(accessTokenRequest);
          }
          console.log(error);
        });
    }
  }, [instance, accounts, inProgress, apiData]);

  return <p>Return your protected content here: {apiData}</p>;
}

function App() {
  return (
    <AuthenticatedTemplate>
      <ProtectedComponent />
    </AuthenticatedTemplate>
  );
}

或者,如果需要在 React 组件的外部获取令牌,可以调用 acquireTokenSilent,但如果失败,则不应回退到交互。 所有交互都应在组件树中的 MsalProvider 组件下进行。

// MSAL.js v2 exposes several account APIs, logic to determine which account to use is the responsibility of the developer
const account = publicClientApplication.getAllAccounts()[0];

const accessTokenRequest = {
  scopes: ["user.read"],
  account: account,
};

// Use the same publicClientApplication instance provided to MsalProvider
publicClientApplication
  .acquireTokenSilent(accessTokenRequest)
  .then(function (accessTokenResponse) {
    // Acquire token silent success
    let accessToken = accessTokenResponse.accessToken;
    // Call your API with token
    callApi(accessToken);
  })
  .catch(function (error) {
    //Acquire token silent failure
    console.log(error);
  });

后续步骤