使用跨站点 cookie 从 Chrome 扩展 post 到 Rails API

Using cross-site cookies to post to Rails API from Chrome extension

我构建了一个 Chrome 扩展程序,可以将网络内容保存到我的 Rails 应用程序中。最初我能够依靠现有的 Rails/Devise 用户会话来确保内容被保存到正确的用户,只要在我的 API 控制器上打开 CORS 设置(见下面的代码)。只要用户已登录,AJAX 从 Chrome 扩展程序调用我的网站就会得到正确的身份验证,无论该扩展程序在哪个站点上使用。

但是,在 2020 年初,Chrome 对他们处理跨站点请求的方式进行了更改(请参阅 here, here, and here)。具体来说,cookie 的 SameSite 属性现在默认为 'Lax' 而不是 'None',因此要使用跨站点 cookie,需要将 cookie 设置显式设置为 SameSite=None; Secure

Rails'自己的用户会话 cookie 没有 SameSite=None; Secure 设置,因此使用 Rails 会话验证我的 Chrome 扩展程序的请求不再是一个选项。

我的解决方法是在用户登录应用程序时生成我自己的 API 身份验证 cookie,这确实应用了必要的 SameSite=None; Secure。我能够使用此 cookie 验证来自我的 Chrome 分机的 API 调用,一切正常。

然后在 2020 年 9 月上旬突然停止工作。 Rails 不再从 Chrome 扩展请求中读取跨站点 cookie。没有错误或警告,值为 null。

API 控制器:

  # This gets called when user logs into app:
  def set_cross_site_cookie

    # NOTE: Won't work in dev because secure = true
    cookies[:foo_cookie] = {
      value: 'bar',
      expires: 1.year.from_now,
      same_site: :none, # Required in order to access from Chrome extension on different site
      secure: true # Required in order to access from Chrome extension on different site
    }
    cookie = cookies[:foo_cookie]
    render json: {cookie: cookie}

  end

  # This SHOULD work when called from our Chrome extension:
  def get_cross_site_cookie

    # Add headers to allow CORS requests
    # SEE: 
    response.headers['Access-Control-Allow-Origin'] = '*'
    response.headers['Access-Control-Request-Method'] = %w{GET POST OPTIONS}.join(",")

    cookie = cookies[:foo_cookie]
    render json: {cookie: cookie}

  end

Rails 5、机架2.1 (注意:为了使用选项 same_site: none 设置 Rails cookie,您显然需要使用高于 2.1.0 的机架版本 - 请参见:https://github.com/rails/rails/pull/28297#issuecomment-600566751

有人知道发生了什么吗?

我仍然不知道为什么 cross-site cookie 突然停止工作,但我是这样破解它的:

解决方法是使用 Chrome extension cookie API 将我的 Rails API 身份验证 cookie 读取到 Chrome 的本地存储中。由于我们可以在扩展清单中启用对任何特定站点的 cookie 的访问,因此实际上它们是否 cross-site cookie 并不重要。

一旦 API cookie 被读入存储,我们就可以将其作为身份验证令牌与每个请求一起传递,基本上将其用作 pseudo-cookie.

所以完整的流程是用户点击扩展按钮,扩展根据它对该域的显式 cookie 权限读取 API 身份验证 cookie,如果 cookie 丢失或过时,它会强制用户登录。如果 cookie 有效,它会在每个 API 调用的参数或 headers 中作为身份验证令牌传递。

PRE-FLIGHT 选项请求的旁注:

您可能还必须处理将与某些 cross-site AJAX 一起发送的 OPTIONS pre-flight 请求(我认为这只是 content-type JSON 发布但不要引用我的话),因为它们会在 Rails 中触发 ActionController::RoutingError (No route matches [OPTIONS]) 错误。推荐的答案是使用 rack-cors gem,确实解决了问题。

参见: