我们有一个单页应用程序(SPA),它使用Azure Active Directory" Easy Auth",例如,无代码解决方案。当用户首次打开应用程序时,这似乎工作正常。它们被重定向到Microsoft登录页面,他们可以进行身份验证,然后访问该应用程序。
然后,因为它是SPA,用户将导航并仅触发Ajax请求。会话cookie到期后大约24小时后出现问题。用户可能仍然打开相同的浏览器选项卡,并且不执行整页刷新。然后他们可能正在处理一个记录,并且在某些时候他们的下一个Ajax PUT请求失败并且具有Redirect HTTP状态并且他们失去了他们的工作。
所以关键问题是:
我们如何使SPA Ajax请求扩展当前用户的会话,以便他们的会话在积极使用应用程序时不会过期?
似乎Azure AD Easy Auth服务没有"荣誉"用户方面的活动,这使我们相信会话cookie永远不会更新。
注意:我们最近使用/.auth/refresh端点进行了一些测试,但这也无法解决问题。
答案 0 :(得分:3)
有几种方法可以解决这个问题。以下是我能想到的一些内容:
/.auth/refresh
端点不会刷新AppServiceAuthSession,因为AAD不支持刷新用户信息。您可以使用x-zumo-auth
令牌对您的后端进行身份验证。 /.auth/refresh
端点将正确刷新这些令牌。如果您使用/.auth/login/aad
显式登录用户,则可以将session_mode=token
添加为查询字符串参数。如果您使用Mobile Apps JavaScript SDK,则可以使用此功能。如果登录是自动的,那么您需要在身份验证配置的session_mode=token
设置中添加additionalLoginParams
。然后,您可以从登录完成后添加到URL的#token片段中解析身份验证令牌。./auth/login/aad?prompt=none&domain_hint={userdomain.com}
的内容,其中{userdomain.com}
是用户电子邮件地址的最后一部分 - 例如contoso.com。这些参数将传递到AAD登录页面,登录应自动完成,无需任何用户交互。在浏览器窗口中手动测试几次以确保其正常工作。结果应该是更新的auth cookie,并且有新的到期日期。如果您对这些选项有任何疑问或问题,请在评论中告知我们。
答案 1 :(得分:1)
使用实现示例扩展 Chris Gillum 的 answer:
场景:具有渐进式 Web 应用程序 (PWA) 功能的单页应用程序 (SPA),托管在 Azure Web 应用程序中。添加了使用 Azure Web 身份验证/EasyAuth 的身份验证。
遇到类似/相同的问题: SPA 的初始加载工作正常,但经过数小时(令牌过期)应用程序“中断” - 在 iOS 平板电脑上的 SPA 中显示对我来说无休止的白屏而且似乎没有实际的修复(强制杀戮没有解决)。抛出的错误消息范围从 401(可以理解)到 service-worker 拒绝处理脚本/处理 302 重定向/等(问题可能不太明显)。
注意:截至 2021 年 4 月,基于 Chromium 的浏览器使用隐藏的 iframe 方法。对于其他浏览器,AAD 页面会遇到错误并失败 - 当前建议的解决方案是存储应用程序状态 -> 导航到带有重定向参数的 AAD 登录页面 -> 用户登录并重定向回应用程序 -> 应用程序状态恢复并刷新令牌.
refreshAuthToken() {
//Chrome based browsers work with silent iFrame based token reAuth
if (this.browserChromium()) {
let domainHint = "contoso.com"; //Domain of your organization users (e.g. me@contoso.com)
//Remove existing iframe (if exists), to minimize history/back button entries
let existingFrame = document.getElementById("authIFrame");
if (existingFrame) {
existingFrame.remove();
}
//Inject iFrame that will call endpoint to refresh token/cookie
console.log("Refreshing auth token (quietly)...");
let iframe = document.createElement("iframe");
iframe.id = "authIFrame";
iframe.style =
"width: 0; height: 0; border: 0; border: none; position: absolute; visibility: hidden;";
iframe.src = `/.auth/login/aad?prompt=none&domain_hint=${domainHint}`;
document.body.appendChild(iframe);
new Promise(r => setTimeout(r, 2000)).finally(() => resolve()); //Hacky method of "waiting" for iframe to finish
} else {
console.log("Refreshing auth token (via page reload)...");
window.location.replace("/.auth/login/aad?post_login_redirect_url=/?restoreData=true");
}
},
//
// Timer example:
//
setInterval(() => {this.refreshAuthToken()}, 1000 * 60 * 5); //Fire every 5 minutes
//
// And/or periodically call this function maintain token freshness
//
checkAuthToken() {
//this.authEnd = JWT from /.auth/me "exp" claim
let now = new Date() / 1000;
let expirationWindow = this.authEnd - 600; // Consider token expiring if 10 minutes or less remaining
if (now >= expirationWindow) {
console.log("Auth Token expired - Refreshing...")
this.refreshAuthToken();
} else {
// console.log("Auth token still healthy.");
}
}