即使用户正在使用应用程序

时间:2017-08-22 14:10:09

标签: azure-web-sites azure-active-directory

我们有一个单页应用程序(SPA),它使用Azure Active Directory" Easy Auth",例如,无代码解决方案。当用户首次打开应用程序时,这似乎工作正常。它们被重定向到Microsoft登录页面,他们可以进行身份​​验证,然后访问该应用程序。

然后,因为它是SPA,用户将导航并仅触发Ajax请求。会话cookie到期后大约24小时后出现问题。用户可能仍然打开相同的浏览器选项卡,并且不执行整页刷新。然后他们可能正在处理一个记录,并且在某些时候他们的下一个Ajax PUT请求失败并且具有Redirect HTTP状态并且他们失去了他们的工作。

所以关键问题是:

我们如何使SPA Ajax请求扩展当前用户的会话,以便他们的会话在积极使用应用程序时不会过期?

似乎Azure AD Easy Auth服务没有"荣誉"用户方面的活动,这使我们相信会话cookie永远不会更新。

注意:我们最近使用/.auth/refresh端点进行了一些测试,但这也无法解决问题。

2 个答案:

答案 0 :(得分:3)

有几种方法可以解决这个问题。以下是我能想到的一些内容:

  1. 使用本地存储:您提到的问题是用户因重定向而丢失了工作。如果您将正在进行的状态保留在本地存储中,以便在将它们重定向回页面时可以使用,则可以解决丢失工作的问题。
  2. 切换到使用令牌:使用AAD时,/.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片段中解析身份验证令牌。
  3. 使用隐藏的iframe :我自己没有尝试过这个问题,但是如果你能够使用它,可能需要更少量的代码更改。我们的想法是,当您检测到用户处于活动状态时,您可以使用隐藏的iframe定期重新登录用户。 iframe需要指向类似./auth/login/aad?prompt=none&domain_hint={userdomain.com}的内容,其中{userdomain.com}是用户电子邮件地址的最后一部分 - 例如contoso.com。这些参数将传递到AAD登录页面,登录应自动完成,无需任何用户交互。在浏览器窗口中手动测试几次以确保其正常工作。结果应该是更新的auth cookie,并且有新的到期日期。
  4. 如果您对这些选项有任何疑问或问题,请在评论中告知我们。

答案 1 :(得分:1)

使用实现示例扩展 Chris Gillum 的 answer

场景:具有渐进式 Web 应用程序 (PWA) 功能的单页应用程序 (SPA),托管在 Azure Web 应用程序中。添加了使用 Azure Web 身份验证/EasyAuth 的身份验证。

遇到类似/相同的问题: SPA 的初始加载工作正常,但经过数小时(令牌过期)应用程序“中断” - 在 iOS 平板电脑上的 SPA 中显示对我来说无休止的白屏而且似乎没有实际的修复(强制杀戮没有解决)。抛出的错误消息范围从 401(可以理解)到 service-worker 拒绝处理脚本/处理 302 重定向/等(问题可能不太明显)。

SPA + Azure Web 身份验证/EasyAuth 调整:

  1. 如果使用 MDM,请在此应用的 MDM 中禁用“阻止 Safari 导航菜单栏”功能。这似乎允许应用程序在强制终止后按预期工作(它会重新加载页面,查看过期的令牌,重定向到登录然后返回到应用程序)。我不确定此行为是否可以在 manifest.json 中控制,可能是 iOS 特定功能。

Disable "Block Safari navigation menu bar

  1. 令牌的隐藏 iframe 刷新 + 定时器/定期检查令牌(在 ajax 调用等中):

注意:截至 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.");
  }
}
  1. Nicety: 启用对 PWA 图标的匿名访问(如果可能)。将 PWA 保存到主屏幕时,iOS 要求图标可公开访问,否则使用应用程序屏幕截图而不是正式图标:https://stackoverflow.com/a/67116374/7650275