默认情况下,当应用在 iframe 内呈现时,MSAL 会阻止全帧重定向到Microsoft Entra ID身份验证终结点,这意味着不能使用重定向 API 与 IdP 进行用户交互:
- 实施此限制,因为Microsoft Entra ID将拒绝在 iframe 中呈现任何要求用户交互(例如凭据输入、同意、注销等)的提示,方法是引发
X-FRAME OPTIONS SET TO DENY错误,这是防止点击劫持攻击所采取的措施。 - 而是,如果需要用户交互,则必须依赖 MSAL 的弹出式 API;如果可以避免用户交互,则必须依赖无提示 API(
ssoSilent()、acquireTokenSilent())。 - 同样,必须使用 logoutPopup() API 进行注销(:警告:如果你的应用使用的是低于 v2.13 的版本
msal-browser,请确保升级并替换logout()API,因为它将尝试完整帧重定向到 Microsoft Entra ID)。 - 使用 弹出窗口 API 时,需要考虑到父应用施加的任何 沙盒 限制。 具体而言,当 iframe 处于沙盒模式时,父应用需要设置
allow-popups标志。
Azure AD B2C 提供嵌入式登录体验,允许在 iframe 中呈现自定义登录 UI。 由于 MSAL 默认阻止 iframe 中的重定向,因此需要将 allowRedirectInIframe 配置选项设置为 true 才能使用此功能。 请注意,不建议在Microsoft Entra ID上为应用启用此选项,因为存在上述限制。
浏览器限制
由于 iframe 中的Microsoft Entra会话 Cookie 被视为第三方 Cookie,因此某些浏览器(例如处于隐身模式的 Safari 或 Chrome)默认会阻止或清除这些 Cookie。 这会影响 iframed 应用的 单一登录 体验,因为它们无法访问 IdP 的会话 Cookie(请参阅: 单一登录)。
此外,当 Chrome 中禁用第三方 Cookie 时,iframed MSAL 应用将无法访问本地或会话存储。 在这种情况下,MSAL 将回退到内存存储。
单一登录
您可以在 iframe 内嵌应用与父应用之间实现单点登录,无论是同源还是跨源,前提是您需将账户提示从父应用传递给 iframe 内嵌应用。
具有相同源的应用
如果两个应用都配置 MSAL 以使用 本地存储 进行缓存,则具有相同源的 Iframed 和父应用可能有权访问同一 MSAL.js 缓存实例,并且能够登录而不出现提示。 有关详细信息,请参阅: 使用 MSAL.js进行单一登录
具有跨源的应用
跨源的 iframe 应用和父应用可以使用 ssoSilent() API 来实现单点登录。 为此,父应用应将 帐户、 loginHint (用户名)或 会话 ID (sid)传递给 iframed 应用。
应用可以尝试在没有上述任何参数的情况下使用 ssoSilent 。 但是请注意,在使用时还有其他ssoSilent,而不提供有关用户会话的任何信息。
对于 iframed 和父应用之间的跨域通信,可以考虑以下几种替代方法:
- 可以在父应用中将查询字符串添加到 iframe 的源,并在子应用中稍后检索它们:
// Create the main myMSALObj instance
// configuration parameters are located at authConfig.js
const myMSALObj = new msal.PublicClientApplication({
auth: {
clientId: "ENTER_CLIENT_ID",
authority: "https://login.microsoftonline.com/ENTER_TENANT_ID",
redirectUri: "/redirect", // set to a blank page for handling auth code response via popups
},
cache: {
cacheLocation: "localStorage", // set your cache location to local storage
},
});
window.onload = () => {
const urlParams = new URLSearchParams(window.location.search);
const sid = urlParams.get("sid");
// attempt SSO
myMSALObj.ssoSilent({
sid: sid
}).then((response) => {
// do something with response
}).catch(error => {
// handle errors
});
}
- 可以在父应用中使用 postMessage() API,并在子应用中侦听消息事件:
// Create the main myMSALObj instance
// configuration parameters are located at authConfig.js
const myMSALObj = new msal.PublicClientApplication({
auth: {
clientId: "ENTER_CLIENT_ID",
authority: "https://login.microsoftonline.com/ENTER_TENANT_ID",
redirectUri: "/redirect", // set to a blank page for handling auth code response via popups
},
cache: {
cacheLocation: "localStorage", // set your cache location to local storage
},
});
const parentDomain = "http://localhost:3001";
window.addEventListener("message", (event) => {
// check the origin of the data
if (event.origin === parentDomain) {
const sid = event.data;
// attempt SSO
myMSALObj.ssoSilent({
sid: sid
}).then((response) => {
// do something with response
}).catch(error => {
// handle errors
});
}
});
错误处理
如果 ssoSilent() 失败,应捕获并处理任何错误。 具体而言:
- InteractionRequiredError:如果需要同意,用户需要执行 MFA 等,将引发。通常,只需启动交互式 API 即可处理此错误。
-
BrowserAuthError:如果未提供 账户提示、提供了无效的 账户提示、弹出窗口被阻止等,则会引发此错误。你需要检查
errorCode并进行相应处理。
myMSALObj.ssoSilent({
sid: sid
}).then((response) => {
// do something with response
}).catch(error => {
if (error instanceof msal.InteractionRequiredAuthError) {
myMSALObj.loginPopup()
.then((response) => {
// do something with response
});
} else if (error instanceof msal.BrowserAuthError) {
if (error.errorCode === "silent_sso_error") {
// e.g. username is null
}
if (error.errorCode === "popup_window_error") {
// e.g. popups are blocked
}
} else {
console.log(error);
}
});
用户交互
如果要最大程度地减少与需要用户交互的 IdP 的通信,或者出于任何原因遇到弹出窗口问题,则可以考虑以下一些选项:
单一退出登录
可以将 MSAL.js 与 前端通道注销 URI 配合使用,以实现 iframed 和父应用之间的 单一注销 效果。 例如,如果希望用户在从父应用退出登录时,也自动从嵌入在 iframe 中的应用退出登录,则应为这些应用启用前端通道注销。 为此,请参阅: 如何配置前端通道注销 URI。