从 MSAL v1 迁移到
本文概述了如何从 MSAL v1 迁移到 @azure/msal-react 和 @azure/msal-browser迁移。 我们建议迁移到结合 PKCE 和条件访问的授权代码流,以提升性能并增强安全性。 此外,还有更好的单页应用程序支持。
先决条件
- 拥有有效订阅的 Azure 帐户。 免费创建帐户
- 在 Microsoft Entra 租户中注册的现有应用程序。
更新应用注册信息
@azure/msal-react 库是 @azure/msal-browser 的包装器,它实现了带有 PKCE 的授权代码流。 这是 MSAL v1 库中实现 隐式流的重要更新。
你需要创建新的应用注册或更新现有应用注册以使用新的 redirectUri 类型“SPA”。 有关详细信息 ,请参阅单页应用程序:应用注册 。
安装 @azure/msal-react 和 @azure/msal-browser
可以从 npm 安装两者 @azure/msal-react 及其对等依赖项 @azure/msal-browser 。 卸载旧 MSAL 包非常重要。 打开终端并运行以下命令。
npm uninstall msal
npm install @azure/msal-react @azure/msal-browser
从 react-aad-msal 升级
如果你的应用当前使用 React Microsoft Entra MSAL 进行身份验证,并且你希望迁移到@azure/msal-react本部分将概述两个库与需要进行的一些更改之间的差异。 React Microsoft Entra MSAL 是第三方库,MSAL React 是从头开始构建的,可能存在 MSAL React 未涵盖或不支持的某些边缘情况。
以下是 react-aad-msal 支持但 @azure/msal-react 不支持的功能:
- 在呈现受保护的组件之前验证 IdToken 到期时间和自动刷新过期的 IdTokens
- 开箱即用的 redux 存储支持(替代方案如下)
对于 react-aad-msal 可能但 @azure/msal-react 不再可能的情况,请在 microsoft-authentication-library-for-js 仓库中提出问题。
初始 化
在 react-aad-msal 中,您通过创建一个 MsalAuthProvider 对象来初始化 MSAL 实例,该对象随后会传递给 AzureAD 组件。
import { MsalAuthProvider } from "react-aad-msal";
const authProvider = new MsalAuthProvider(config, authenticationParameters, options);
在@azure/msal-react中,你使用从@azure/msal-browser导出的PublicClientApplication初始化 MSAL 实例,然后将该实例向下传递给从@azure/msal-react导出的MsalProvider组件。 配置选项在配置选项之间msal@azure/msal-browser大致相似,但可以参考配置类型,了解最新的配置选项。
在 react-aad-msal 中使用的 authenticationParameters 和 options 参数在 @azure/msal-react 中不使用,不过可以在各个单独组件上实现类似的功能。 本文档稍后将对此进行说明。
@azure/msal-react 使用 React Context API,使 PublicClientApplication 和认证状态在整个组件树中可用。
import { PublicClientApplication } from "@azure/msal-browser";
import { MsalProvider } from "@azure/msal-react";
const pca = new PublicClientApplication(config);
function App() {
return (
<MsalProvider instance={pca}>
<YourAppComponents />
</MsalProvider>
);
}
关于 MsalProvider 组件的一般说明:
- 所有需要访问身份验证状态或
@azure/msal-react暴露的挂钩/组件的组件,必须在组件树中更高处有MsalProvider,因此建议MsalProvider尽可能靠近根渲染。 - 你的应用不应在任何给定页面上呈现超过 1
MsalProvider个组件。 - 由于重新渲染的可能性,不建议在组件内初始化
PublicClientApplication
保护您的组件
在 react-aad-msal 组件中,使用 AzureAD 组件或 withAuthentication HOC(在底层用 AzureAD 包装你的组件)进行保护。
AzureAD仅当用户经过身份验证时,该组件才会呈现子组件,并在未对用户进行身份验证时(可选)启动登录。 用于登录的选项(例如作用域、使用弹出窗口还是重定向等)会在更早前创建 authProvider prop 时指定。
import { MsalAuthProvider } from "react-aad-msal";
const authProvider = new MsalAuthProvider(config, authenticationParameters, options);
function App() {
return (
<AzureAD provider={authProvider} forceLogin={true}>
<span>Only authenticated users can see me.</span>
</AzureAD>
);
}
@azure/msal-react另一方面,开发人员可以更好地控制他们想要向谁显示的内容。
- 如果用户已通过身份验证,
AuthenticatedTemplate组件将渲染子组件 - 如果用户未通过身份验证,
UnauthenticatedTemplate组件将渲染子组件 - 如果用户未通过身份验证,
MsalAuthenticationTemplate组件将自动启动登录,并在用户通过身份验证后渲染子组件。
import { PublicClientApplication, InteractionType } from "@azure/msal-browser";
import { MsalProvider, AuthenticatedTemplate, UnauthenticatedTemplate, MsalAuthenticationTemplate } from "@azure/msal-react";
const pca = new PublicClientApplication(config);
function App() {
return (
<MsalProvider instance={pca}>
<AuthenticatedTemplate>
<span>Only authenticated users can see me.</span>
</AuthenticatedTemplate>
<UnauthenticatedTemplate>
<span>Only unauthenticated users can see me.</span>
</UnauthenticatedTemplate>
<MsalAuthenticationTemplate interactionType={InteractionType.Popup} authenticationRequest={request}>
<span>Only authenticated users can see me. Unauthenticated users will get a popup asking them to login first.</span>
</MsalAuthenticationTemplate>
</MsalProvider>
);
}
此外,如果希望采用基于挂钩的方法 @azure/msal-react 提供了多个挂钩,可以使用这些挂钩来实现类似的结果。 这些仅是一些基本示例,你可以参考 MSAL React 钩子 获取更多信息。
import { PublicClientApplication, InteractionType } from "@azure/msal-browser";
import { MsalProvider, useIsAuthenticated, useMsalAuthentication } from "@azure/msal-react";
const pca = new PublicClientApplication(config);
function App() {
return (
<MsalProvider instance={pca}>
<ExampleComponent />
</MsalProvider>
);
}
function ExampleComponent() {
const isAuthenticated = useIsAuthenticated();
const { error } = useMsalAuthentication(InteractionType.Popup, request); // Will initiate a popup login if user is unauthenticated
if (isAuthenticated) {
return <span>Only authenticated users can see me.</span>
} else if (error) {
return <span>An error occurred during login!</span>
} else {
return <span>Only unauthenticated users can see me.</span>
}
}
获取访问令牌
react-aad-msal
getAccessToken公开可用于在调用 API 之前获取访问令牌的方法。
import { MsalAuthProvider } from "react-aad-msal";
const authProvider = new MsalAuthProvider(config, authenticationParameters, options);
const accessToken = authProvider.getAccessToken();
使用 @azure/msal-react 和 @azure/msal-browser 时,您将在 PublicClientApplication 实例上调用 acquireTokenSilent。
如果需要获取位于其下的 MsalProvider 组件或挂钩内的访问令牌,可以使用 useMsal 挂钩获取所需的对象。
import { useState } from "react";
import { useMsal } from "@azure/msal-react";
import { InteractionRequiredAuthError } from "@azure/msal-browser";
function useAccessToken() {
const { instance, accounts } = useMsal();
const [accessToken, setAccessToken] = useState(null);
if (accounts.length > 0) {
const request = {
scopes: ["User.Read"],
account: accounts[0]
};
instance.acquireTokenSilent(request).then(response => {
setAccessToken(response.accessToken);
}).catch(error => {
// acquireTokenSilent can fail for a number of reasons, fallback to interaction
if (error instanceof InteractionRequiredAuthError) {
instance.acquireTokenPopup(request).then(response => {
setAccessToken(response.accessToken);
});
}
});
}
return accessToken;
}
如果需要在上下文 MsalProvider 之外获取访问令牌,可以直接使用 PublicClientApplication 实例并调用 getAllAccounts() 以获取帐户对象。
Important
仅在 MsalProvider 上下文之外尝试静默获取令牌。 不应在上下文 MsalProvider之外调用交互式方法(重定向或弹出窗口)。
下面的示例演示了出于演示目的的 PublicClientApplication 初始化。
PublicClientApplication 每次页面加载时只应初始化一次,并且这里应使用提供给 MsalProvider 的同一个实例。
import { PublicClientApplication } from "@azure/msal-browser";
const pca = new PublicClientApplication(config);
const accounts = pca.getAllAccounts();
async function getAccessToken() {
if (accounts.length > 0) {
const request = {
scopes: ["User.Read"],
account: accounts[0]
}
const accessToken = await pca.acquireTokenSilent(request).then((response) => {
return response.accessToken;
}).catch(error => {
// Do not fallback to interaction when running outside the context of MsalProvider. Interaction should always be done inside context.
console.log(error);
return null;
});
return accessToken;
}
return null;
}
获取 ID 令牌
react-aad-msal 公开了用于 getIdToken 获取或续订 idToken 的函数。
import { MsalAuthProvider } from "react-aad-msal";
const authProvider = new MsalAuthProvider(config, authenticationParameters, options);
const token = await authProvider.getIdToken();
const idToken = token.idToken.rawIdToken;
你可能也熟悉仅请求 clientId 作为唯一范围以检索 idToken 的模式。
这在 @azure/msal-browser 中不再是受支持的模式。
在 @azure/msal-react 和 @azure/msal-browser 中,所有令牌请求都将同时返回访问令牌和 ID 令牌,并且所有访问令牌的续订也都会同时续订 ID 令牌。
如果需要获取位于其下的 MsalProvider 组件或挂钩内的 ID 令牌,可以使用 useMsal 挂钩获取所需的对象。
import { useState } from "react";
import { useMsal } from "@azure/msal-react";
function useIdToken() {
const { instance, accounts } = useMsal();
const [idToken, setIdToken] = useState(null);
if (accounts.length > 0) {
const request = {
scopes: ["openid"],
account: accounts[0]
};
instance.acquireTokenSilent(request).then(response => {
setIdToken(response.idToken);
}).catch(error => {
// acquireTokenSilent can fail for a number of reasons, fallback to interaction
if (error instanceof InteractionRequiredAuthError) {
instance.acquireTokenPopup(request).then(response => {
setIdToken(response.idToken);
});
}
});
}
return idToken;
}
如果需要在上下文 MsalProvider 之外获取 ID 令牌,则可以直接使用该 PublicClientApplication 实例并调用 getAllAccounts() 以获取帐户对象。
Important
仅在 MsalProvider 上下文之外尝试静默获取令牌。 不应在上下文 MsalProvider之外调用交互式方法(重定向或弹出窗口)。
下面的示例演示了出于演示目的的 PublicClientApplication 初始化。
PublicClientApplication 在每次页面加载时只应初始化一次,并且你应在此处使用提供给 MsalProvider 的同一个实例。
import { PublicClientApplication } from "@azure/msal-browser";
const pca = new PublicClientApplication(config);
const accounts = pca.getAllAccounts();
async function getIdToken() {
if (accounts.length > 0) {
const request = {
scopes: ["openid"],
account: accounts[0]
}
const idToken = await pca.acquireTokenSilent(request).then((response) => {
return response.idToken;
}).catch (error => {
// Do not fallback to interaction when running outside the context of MsalProvider. Interaction should always be done inside context.
console.log(error);
return null;
});
return idToken
}
return null;
}
更新 Redux 存储集成/响应事件
react-aad-msal 提供开箱即用的 redux 存储集成,在登录或注销等事件发生时调度操作。
@azure/msal-react不提供此功能,但是,可以使用公开@azure/msal-browser 来实现类似的功能。
可以注册每次广播事件时都会调用的事件回调(例如 LOGIN_SUCCESS)。 回调函数可以检查事件,并使用有效负载执行某些操作。 如果你想继续使用现有的 redux 存储,可以注册一个事件回调,将操作调度到你的存储。
import { PublicClientApplication, EventType } from "@azure/msal-browser";
import { store } from "your-redux-store-implementation";
const msalInstance = new PublicClientApplication(config);
const callbackId = msalInstance.addEventCallback((message: EventMessage) => {
if (message.eventType === EventType.LOGIN_SUCCESS) {
store.dispatchAction({type: "AAD_LOGIN_SUCCESS", payload: message.payload});
}
});
msal v1 和 @azure/msal-browser 之间的有效负载可能有所不同,因此,如果您的应用程序依赖于特定字段或对象结构,则可能需要进行一些调整。 typedocs 包含事件类型和有效负载类型的最新列表,可以在事件文档中找到两者之间的映射。