Search code examples
ruby-on-railsrubycookiesactiondispatchrack-cors

ActionDispatch::Cookies not setting Set-Cookie header in response but response.set_cookie does


I have a Rails 5 API only app and want to send cookies in the response of a JSON request. When I use ActionDispatch::Cookies to set a cookie in the response of the request it doesn't set Set-Cookie header in the response. Although response.set_cookie does work.

I also tested with making an after_action hook in the application controller, and what I saw is Set-Cookie header is not present in response.headers but cookies['name_of_cookie'] is present. One question I also want to ask is when do these cookies(ActionDispatch::Cookies) set header in response? Does it happen in rack?

Requirement

  1. We want to set cookies in CORS request.
  2. We want to set cookies for all the origin's

What is already implemented

  1. The client send a request with withCredentials
  2. In the response Access-Control-Allow-Credentials: true and Access-Control-Allow-Origin: <origin (request.headers['origin'])> are present
  3. As it is Rails API only app we have included the following middlewares in the application
config.middleware.use ActionDispatch::Cookies
config.middleware.use ActionDispatch::Session::CookieStore, key: '_namespace_key'

Question

  1. Why ActionDispatch::Cookies is not working but response.set_cookie is working?
  2. When does ActionDispatch::Cookies set headers in response? Does it happen in any after callback hook in rails app, or it happen in rack? Because until after_action in applicaton_controller the headers wasn't there.

Strange

  1. While in development env and on my local machine it works, but not in production. Does it have to do something with the domain setting of cookie? I also use secure and httponly cookie in production, but only httponly in development.

What am I missing here? I have spend days searching for the problem but I couldn't find a fix. Any lead will be very helpful. Thanks


Edit 1

cookies[@name.to_sym] = {
  value: @value,
  expires: @expire,
  httponly: true,
  same_site: 'None',
  secure: false            # It works when I set it to false but doesn't work with `true` in production. In development env it works with either one.
}

response.set_cookie(
  @name.to_sym,
  value: @value,
  expires: @expire,
  httponly: true,
  secure: true
)

I observed one thing. If I set secure: false in cookies then it works in production but not with secure: true. On other hand secure: true works with response.set_cookie in production.

My request.protocol is HTTP. Is it possible that ActionDispatch::Cookies does not even set cookies if request protocol is HTTP and secure: true option is configured.

But I think secure: true is for client side. Like if a cookie is secure then browser will only send it over https protocol only. What am I missing?


Solution

  • I got the reason for this behavior. Actually other team deployed an application gateway on production. This result in the Rails server getting HTTP request which was HTTPS initially.

    Now as I have marked my cookies to be secure: true, it looks like ActionDispatch::Cookies does not even send cookies in response to a request if the protocol is HTTP. It will only send if protocol is HTTPS. If I disable the secure by secure: false then it work or if I disable the application gateway then it work with ActionDispatch::Cookie.

    It seems strange as secure is a client side attribute, but from behaviour of ActionDispatch::Cookies it looks like it don't even send cookies from server if protocol is HTTP and secure is set to true.

    Although I didn't find any documentation of this behavior, but this is how it's working.