将 Cookie 从前端传递到后端(GCP Cloud Run 上的单独服务)

Passing cookies from frontend to backend (separate services on GCP Cloud Run)

提问人:Grigor 提问时间:11/8/2023 最后编辑:Grigor 更新时间:11/13/2023 访问量:101

问:

我面临着一个相当有趣的问题。我在 GCP Cloud Run 上分别运行前端和后端服务。FE 是 Next.js 14 + Apollo 客户端 + Supabase 用于身份验证。后端是 Express + Apollo Server Express + TypeGraphQL

这两个应用都已停靠,然后使用 GitHub Actions 部署到 Cloud Run。

我有一个设置,我在前端的 Apollo 客户端包含要发送到后端的 cookie。然后,我使用 cookie 值对服务器中的某些路径进行身份验证。为此,我通过在创建新的 HttpLink 实例时将凭据设置为 true 来实现此目的。

const httpLink = new HttpLink({
  uri: process.env.GRAPHQL_URI,
  credentials: 'include'
});

在后端,我有以下内容来配置 CORS。

const corsConfig = {
  credentials: true,
  origin: ['http://localhost:3000', 'https://frontend-instance.a.run.app'],
  methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
  allowedHeaders:
    'Origin, X-Requested-With, Content-Type, Accept, Authorization, Cookie',
};

app.use(cors(corsConfig));

...

const appServer = new ApolloServer({
  schema: buildSchemaSync({
  resolvers: [
    ...
  ]
});

appServer.applyMiddleware({
  cors: corsConfig,
  app,
  path: '/graphql',
});

这在 localhost 中一直按预期工作,但是在部署到生产服务器后,我可以观察到 cookie 不包含在对后端的 graphql 请求中。是什么原因导致的?这是 COR 设置的问题,还是 Cloud Run 的问题?鉴于这在 localhost 中按预期工作,我认为问题不在于设置,而在于 Cloud Run 如何处理 cookie 的传递。

任何指针都将不胜感激。

[编辑]

版本:

NextJS:                 14.0.1
Apollo Client:          3.8.0-rc.2
Supabase:               2.38.4
Express:                4.18.2
Apollo Server Express:  3.12.0
TypeGraphQL Prisma:     0.25.1
Express Cookie apollo-client google-cloud-run

评论


答:

1赞 VonC 11/11/2023 #1

根据您的设置:

┌─────────────────────────────────┐     ┌─────────────────────────────────┐
│ Frontend Service (FE)           │     │ Backend Service (BE)            │
├─────────────────────────────────┤     ├─────────────────────────────────┤
│ - Next.js 14                    │     │ - Express                       │
│ - Apollo Client + HttpLink      │     │ - Apollo Server Express         │
│ - Supabase (Auth)               │     │ - TypeGraphQL                   │
│ - Dockerized & Deployed         │     │ - Dockerized & Deployed         │
│   via GitHub Actions            │     │   via GitHub Actions            │
│   to GCP Cloud Run              │     │   to GCP Cloud Run              │
│                                 │     │                                 │
│    GraphQL Request              │     │                                 │
│    ┌──────────────────────┐     │     │                                 │
│    │ - Cookies            │──────────🞂│                                 │
│    │ - Authentication     │     │     │                                 │
│    │   Data               │     │     │                                 │
│    └──────────────────────┘     │     │                                 │
│                                 │     │                                 │
│                                 │     │    GraphQL Response             │
│                                 │     │    ┌──────────────────────┐     │
│                                 │🞀─────────│ - Data               │     │
│                                 │     │    │ - Session Management │     │
│                                 │     │    └──────────────────────┘     │
└─────────────────────────────────┘     └─────────────────────────────────┘

假设它在本地环境中按预期工作,它也应该在 GCP Cloud Run 上运行。使用的技术和配置(Next.js、Apollo Client、Express、Apollo Server Express、CORS 设置等)都与云兼容,通常用于基于云的部署。

但是,本地环境和云环境之间的主要区别通常在于域处理、网络配置和安全策略。这可能会影响 Cookie 的处理和传输方式。

我首先要确保前端设置的 cookie 的范围限定为后端可访问的域和路径。
范围限定为特定域或路径的 Cookie 可能不会发送到其他域。

如果您的生产环境是 HTTPS(可能适用于 Cloud Run),请确保使用 Cookie 设置了适当的 SameSite 属性。这些属性对于跨站点请求通常是必需的。SecureSameSite=None; Secure

document.cookie = "name=value; domain=your-domain.com; path=/; SameSite=None; Secure";

使用浏览器开发人员工具检查网络流量。在响应中查找标头,在请求中查找标头,以验证 Cookie 是否正确设置和发送。Set-CookieCookie

虽然 CORS 设置看起来正确,但请确保生产中的前端 URL 正确列在 CORS 配置的数组中。origin

查看 Cloud Run 日志,查看与 Cookie 处理或 CORS 相关的任何警告或错误。有时,生产环境具有额外的安全措施,可能会干扰 Cookie 传输。

如果 Cookie 仍然存在持续性问题,请考虑在标头中使用身份验证令牌(如 JWT)作为可能的替代方法。Authorization


  • 我已经确认源列表具有指向前端的正确 URL。
  • 我已经更新了我的代码,以在前端手动设置带有标志的 authToken cookie。观察到没有变化,cookie 仍然没有通过。SameSite=None; Secure
  • 我已经使用 Authorization 标头方法作为解决方法。

这种方法对我来说的问题是,因为我有一个 SPA,并且该部分是在服务器组件中完成的,因此我可能没有获得最新的令牌。

我也尝试从后端预检返回,这是完整列表:.仍然没有骰子。Same-SiteOrigin, X-Requested-With, Content-Type, Accept, Authorization, Cookie, Set-Cookie

鉴于其他详细信息:

- 由于源列表已确认包含正确的 URL,因此 CORS 配置不太可能成为问题。

  • 使用标志设置 cookie 应该有助于跨域 cookie 传输,但由于这没有解决问题,它指向了另一个原因。authTokenSameSite=None; Secure

  • 使用 Authorization 标头是一种可行的解决方法,但担心在 SPA 上下文中无法获取最新的令牌是有效的。

  • 在后端预检响应中添加到允许的标头列表中是一个很好的步骤,但由于它不起作用,因此问题可能不在于 CORS 标头本身。Set-Cookie

因此,问题可能出在 Cloud Run 上的特定部署或网络配置中,也可能出在浏览器在生产环境中处理 Cookie 的方式上。
详细检查浏览器的网络活动可能很有用,以查看 cookie 是否正在设置但未发送,或者它们是否因安全策略而被阻止。
并查看可能影响 Cookie 处理的任何特定 Cloud Run 设置或日志。


如果我将后端查询代理到前端的某个路径,比如(使用 nextjs config)怎么办。瞧,饼干现在正在发送!/api

这确实涉及巧妙地使用 Next.js 的重写规则通过前端服务代理 GraphQL 请求。
通过将请求定向到前端的路径(例如,),然后将此路径重写到后端 GraphQL URI,它们可以有效地确保范围限定在前端域的 cookie 包含在对后端的请求中。
/api

这种方法利用了 cookie 随请求发送到同一来源(在本例中为前端)的事实,并利用 Next.js 的功能将请求重写到外部 API。
通过前端代理请求,解决了跨域设置中未发送 cookie 的问题,这是现代 Web 开发中的常见挑战。

评论

0赞 Grigor 11/12/2023
感谢您抽出宝贵时间写下此答案。这里有一些注意事项: - 我已经确认源列表具有指向前端的正确 URL。- 我更新了我的代码,以在前端手动设置带有标志的 cookie。观察到没有变化,cookie 仍然没有通过。- 我已经使用 Authorization 标头方法作为解决方法。这种方法对我来说的问题是,因为我有一个 SPA,并且该部分是在服务器组件中完成的,因此我可能无法获得最新的令牌。authTokenSameSite=None; Secure
0赞 Grigor 11/12/2023
我还尝试从后端预检返回 Same-Site,这是完整列表:.仍然没有骰子。Origin, X-Requested-With, Content-Type, Accept, Authorization, Cookie, Set-Cookie
0赞 VonC 11/12/2023
@Grigor我已经编辑了答案以解决您的评论。
0赞 Grigor 11/12/2023
我想我想通了。在阅读了您的编辑并确认我已经尝试了所有这些方法后,我有了另一个想法。如果我将后端查询代理到前端的某个路径,比如 /api(使用 nextjs config),该怎么办?瞧,饼干现在正在发送!我将用这些信息来回答这个问题,以防其他人遇到类似的问题。
0赞 VonC 11/13/2023
@Grigor 好主意!我已经对你的回答投了赞成票,并在我的回答中加入了一些评论。
1赞 Grigor 11/12/2023 #2

该解决方案涉及使用 NextJs 的重写规则。我编辑并添加了以下内容:next.config.js

const nextConfig = {
  ...,
  async rewrites() {
    return [
      {
        source: '/api',
        destination: process.env.GRAPHQL_URI,
      },
    ];
  },
};

然后,在我创建后端 HTTP 链接的代码中,我简单地更新了 URI,如下所示:

const httpLink = createHttpLink({
  uri: BACKEND_API_PROXY_URI,
  credentials: 'include',
});

哪里像这样BACKEND_API_PROXY_URIhttp://localhost:3000/api

部署到 Cloud Run 后,我注意到 Cookie 现在可以正确发送!