Rails 7 API not passing session cookie header to front end

885 Views Asked by At

I have a Rails 7 API that connects to a React 18 front end deployed on a separate subdomain. In development, the Rails API runs on localhost:3000 and the front end runs on localhost:3001. The issue I'm describing is taking place in the dev environment. I don't know if it's happening in production because I don't want to deploy it till it works in dev.

I want a session cookie to be set on my front end and I've tried everything to get the API to send the Set-Cookie header, however, inspecting the response in the "Network" tab of my browser dev tools reveals that the header is not being set. The CSRF cookie (manually set using cookies[name] = {...}) is being set but not the session cookie. I'm at a loss.

My /config/application.rb file includes:

config.api_only = true

config.middleware.use ActionDispatch::Cookies
config.middleware.use ActionDispatch::Session::CookieStore,
                      key:       '_sim_session',
                      secure:    true,
                      same_site: :none,
                      domain:    :all

(Adding :httponly, :max_age, or :expires_at options with any value has no effect.)

In my ApplicationController:

class ApplicationController < ActionController::API
  include ActionController::Cookies
  include ActionController::RequestForgeryProtection

  private

  attr_reader :current_user

  def current_user=(user)
    session[:user_id] = user&.id
    @current_user = user
  end

  def generate_csrf_headers!
    value = form_authenticity_token

    cookies['CSRF-TOKEN'] = {
      value:,
      domain:    :all,
      same_site: :none,
      secure:    true
    }

    headers['X-CSRF-Token'] = value
    headers['X-CSRF-Param'] = request_forgery_protection_token
  end
end

In my sessions controller (this is simplified code because I'm actually using controller services and response objects that do a few other things):

class SessionsController < ApplicationController
  skip_before_action :validate_authenticity_token, only: :create

  def create
    # Validate JWT sent from front end is signed by Google and decrypt it
    payload = Google::Auth::IDTokens.verify_oidc(params[:token], aud: configatron.google_oauth_client_id)

    # Create a user with the given Google account ID or update an existing one
    # with current account information
    user = User.create_or_update_for_google(payload)

    current_user = user

    # This Set-Cookie header is being sent through to the browser, just not
    # the session cookie
    generate_csrf_headers!

    render json: user, status: :created
  end
end

And finally, my CORS initializer (I'm using rack-cors):

Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins configatron.client_origin

    resource '*',
             headers:     :any,
             methods:     %i[get post put patch delete options head],
             credentials: true
  end
end
0

There are 0 best solutions below