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