【问题标题】:Apollo Express Server on Heroku and Refresh Token Cookie on Mobile BrowserHeroku 上的 Apollo Express 服务器和移动浏览器上的刷新令牌 Cookie
【发布时间】:2021-04-22 21:34:36
【问题描述】:

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

使用 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 客户端还会检查访问令牌是否有效

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',
    credentials: 'include',
  }).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 上的站点相同。

【问题讨论】:

  • 我已将问题隔离为 cookie 设置。我还没有弄清楚 iOS Chrome 和 Safari 不喜欢哪个设置,但是在我的开发服务器上,没有相同的站点,并且安全,cookie 保存得很好。我还设置了一个 maxAge,这使它不再是会话 cookie。

标签: heroku apollo react-apollo netlify


【解决方案1】:

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

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

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

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

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

默认情况下,在 Heroku 上,免费的 dyno 会为您提供一个类似“your-app.herokuapp.com”的域,这很棒,因为它还包括免费的 SSL。但是,为了使 cookie 起作用,我添加了与 Netlify 一起使用的自定义域。需要明确的是,Netlify 已经使用了我的 apex 自定义域,所以我正在向 Heroku (api.domain.com) 添加一个子域。 Cookie 确实适用于具有相同站点“严格”的相同域和子域。

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-04-22
    • 1970-01-01
    • 2016-10-26
    • 2020-01-22
    • 2019-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多