微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Heroku 上的 Apollo Express 服务器和移动浏览器上的刷新令牌 Cookie

如何解决Heroku 上的 Apollo Express 服务器和移动浏览器上的刷新令牌 Cookie

在访问/刷新时,应用会检查 cookie 中的刷新令牌。如果有一个有效的,Apollo Express 服务器将提供一个访问令牌。这在我的桌面上运行良好,但在 iPhone 上使用 Chrome 或 Safari 时,用户每次刷新都会被发送到登录页面

React 应用程序与 Apollo 客户端

 useEffect(() => {
    fetchUser();
  },[]);

  const fetchUser = async () => {
    const res = await fetch('https://website.com/token',{
      method: 'POST',credentials: 'include',});
    const { accesstoken } = await res.json();
    if (accesstoken === '') {
      setIsLoggedIn(false);
    }
    setAccesstoken(accesstoken);
    setLoading(false);
  };

Apollo Client 还会检查访问令牌是否有效

const authLink = setContext((_,{ headers }) => {
  const token = getAccesstoken();

  if (token) {
    const { exp } = jwtDecode(token);

    if (Date.Now() <= exp * 1000) {
      return {
        headers: {
          ...headers,authorization: token ? `Bearer ${token}` : '',},};
    }
  }
  fetch('https://website.com/token',{
    method: 'POST',}).then(async (res) => {
    const { accesstoken } = await res.json();
    setAccesstoken(accesstoken);
    return {
      headers: {
        ...headers,authorization: accesstoken ? `Bearer ${accesstoken}` : '',};
  });
});

const client = new ApolloClient({
  link: from([authLink.concat(httpLink)]),cache: new InMemoryCache(),connectToDevTools: true,});

这会处理 Express 服务器上的令牌链接

app.use('/token',cookieParser());
    app.post('/token',async (req,res) => {
      const token = req.cookies.rt;

      if (!token) {
        return res.send({ ok: false,accesstoken: '' });
      }

      const user = await getUser(token);

      if (!user) {
        return res.send({ ok: false,accesstoken: '' });
      }

      sendRefreshToken(res,createRefreshToken(user));

      return res.send({ ok: true,accesstoken: createAccesstoken(user) });
    });

和cookie的设置

export const sendRefreshToken = (res,token) => {
  res.cookie('rt',token,{
    httpOnly: true,path: '/token',sameSite: 'none',secure: true,});
};

与前端在 Netlify 上相同的站点是“无”。

解决方法

经过一天的摆弄和研究,我找到了问题,以及使用自定义域时的一个解决方案。

问题是 iOS 将 sameSite 'none' 视为 sameSite 'strict'。我认为 iOS Chrome 会与 Safari 不同,但似乎没有。

如果您使用托管在 Netlify 上的前端,您自​​然会拥有与 Heroku 应用后端不同的域。由于我使用的是自定义域,并且 Netlify 提供了免费的 SSL,因此完成了一半的工作。

设置 httpOnly cookie 的唯一方法是将 cookie 设置为安全的。下一步是将 sameSite 设置为“none”,但如上所述,这不适用于 iOS。

设置 cookie 的域属性也不起作用,因为域属性涉及 cookie 的范围而不是 cookie 的来源。如果 cookie 来自不同的域(Heroku 后端),那么前端(在 Netlify 上)将无法使用它。

默认情况下,在 Heroku 上,免费 dyno 将为您提供一个域,例如“your-app.herokuapp.com”,这很棒,因为它还包含免费的 SSL。但是,为了使 cookie 起作用,我添加了与 Netlify 一起使用的自定义域。需要明确的是,Netlify 已经使用了我的顶点自定义域,因此我向 Heroku (api.domain.com) 添加了一个子域。 Cookie 确实适用于具有 sameSite 'strict' 的同一个域和子域。

最后一个问题是带有 Heroku 的自定义域不会自动获得 SSL,这就是为什么我认为升级到 $7/month hobby dyno 以避免手动管理 SSL 是值得的。我认为这是使用自定义域时唯一的解决方案。

另一方面,对于那些有同样问题并想要免费解决方案的人,您可以放弃使用自定义域,并在 Heroku 上托管您的静态前端和后端。

希望这将为单独部署后端和前端的任何人节省一些时间。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。