如何在最初通过 Safari 14/iOS 14 加载的 PWA 中保持登录状态?

Vel*_*jet 16 safari mobile-safari ios progressive-web-apps

我们的要求是让我们的用户通过 URL 登录到一个应用程序,并且在将应用程序作为 PWA 添加到他们的主屏幕后,保持登录状态,这样就不需要第二次登录到已安装的 PWA 了。这在 Android/Chrome 下当然是可能的,其中登录状态最初可以由 PWA 通过各种机制(包括 cookie、IndexedDB、缓存)存储和访问。

但是,现在在我们看来,iOS 14/iPadOS 14 下的 PWA 是紧密沙箱化的,Safari 无法将登录状态传递给它。多年来,通过各种版本的 iOS,提供了各种共享机制,但在后续版本中已过时。这些包括:

  1. 缓存,通过假端点(ref)访问
  2. 一个会话 cookie ( ref )

一种不依赖浏览器共享存储的机制是将服务器生成的令牌添加到 URL ( ref ), ( ref ) - 这里的问题是它扰乱了 Android/Chrome,它start_url在网络中使用未修改的应用清单。

多年来,这是一个引发了许多 SO 问题的问题(上面提到了其中三个),其中一些已经得到了解决方案的回答,这些解决方案显然在早期版本的 iOS 下有效。我们现在想要的是一个既能在最新版本下运行又能在 Android/Chrome 下运行的解决方案。有什么优惠吗?

Poo*_*hri 8

生成 Webapp 清单并进行更改start_url有其自身的后果。

例如,有时我们想要传递的数据不能立即可用,而且如果数据在 url 中传递,我们应该确保传递的登录数据在第一次打开 Web 应用程序后无效,因为否则共享书签也会共享用户的登录凭据。通过这样做,您将失去这种能力,start_url这意味着如果用户在添加您的网站时,该网站将始终在之后subdirectory1打开。subdirectory1

还有什么选择呢?

从ios 14开始,safari与Webapps共享cacheStorage。因此开发人员可以将凭据作为缓存保存在cacheStorage中并在Web应用程序中访问它们。

代码兼容性

关于ios14的可用性,我们应该考虑到,在ios 14之前,超过90%的用户已经更新到ios 13,并且所有支持ios 13的设备都支持ios 14,我们可以假设ios 14的使用率很快就会达到90%+另外约 5% 的人可以随时在 Webapp 内再次登录。根据statcounter,12 天内已达到 28%

在此输入图像描述 代码示例

这是我在 Web 应用程序中使用的代码,它可以成功地与 ios 添加到主屏幕一起使用。

    ///change example.com with your own domain or relative path.
    function createCookie(name, value, days) {
      if (days) {
        var date = new Date();
        date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
        var expires = "; expires=" + date.toGMTString();
      } else var expires = "";
      document.cookie =
        name + "=" + value + expires + "; path=/; domain=.example.com";
    }
    
    function readCookie(name) {
      var nameEQ = name + "=";
      var ca = document.cookie.split(";");
      for (var i = 0; i < ca.length; i++) {
        var c = ca[i];
        while (c.charAt(0) == " ") c = c.substring(1, c.length);
        if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
      }
      return undefined;
    }
    
    
    async function setAuthFromCookie() {
      caches.open("auth").then(function (cache) {
        console.log("Set Cookie");
        return cache.add(["https://example.com/cacheAuth.php"]);
      });
    }
    async function setAuthToCookie() {
      var uid = readCookie("uid");
      var authKey = readCookie("authKey");
      caches.open("auth").then((cache) => {
        cache
          .match("https://example.com/cacheAuth.php", {
            ignoreSearch: true,
          })
          .then((response) => response.json())
          .then((body) => {
            if (body.uid && uid == "undefined") {
              /// and if cookie is empty
              console.log(body.authKey);
              createCookie("authKey", body.authKey, 3000);
            }
          })
          .catch((err) => {
            console.log("Not cached yet");
          });
      });
    }

    setTimeout(() => {
      setAuthFromCookie();
      //this is for setting cookie from server
    }, 1000);

    setAuthToCookie();
Run Code Online (Sandbox Code Playgroud)

  • 经过深思熟虑,我们决定坚持我们的方法。在我们的应用程序中,处理了修改“start_url”的所有已识别后果:(1)要传递的登录数据在“start_url”重写之前可用;(2) 登录数据在访问权限被检查后立即失效;(3) 因为我们的应用程序是 SPA,所以当它位于子目录中时,不存在将其添加到主屏幕的可能性。 (2认同)

Vel*_*jet 5

可以办到。以下是我们如何成功做到这一点:

  1. 当用户最初在浏览器中登录应用程序时,我们会在服务器上生成一个 UID。
  2. 我们将此 UID 与服务器文件 ( access.data) 中的用户名配对。
  3. 我们动态生成 W​​eb 应用程序清单。在其中,我们将 设置start_url为索引页面并附加一个包含 UID 的查询字符串,例如"start_url": "/<appname>/index.html?accessID=<UID>"
  4. 我们创建一个 cookie 来验证应用程序是否已被访问,例如access=granted
  5. 当用户作为 iOS PWA 访问应用程序时,应用程序会查找此 cookie 并没有找到它(狡猾;)-我们使用 iOS 缺陷之一(未在 Safari 和 PWA 之间共享 cookie)来克服同样的缺陷)。
  6. accesscookie的缺失告诉应用程序从查询字符串中提取 UID。
  7. 它将 UID 发送回服务器,后者在access.data.
  8. 如果服务器找到匹配项,它会告诉应用 PWA 用户已经登录,无需再次显示登录屏幕。任务完成!

注意:Android/Chrome 只是忽略accessID查询字符串中的 - 我的问题暗示 Android/Chrome 需要未修改的start_url.