I’m facing an issue with session persistence in my Rails API application. I’ve set up a session in one controller action (nonce), but when I try to access it in another action (verify), it seems to be missing.
Controller
class UsersController < ApplicationController
before_action :log_response_headers
def log_response_headers
Rails.logger.info("Response Headers: #{response.headers}")
end
def nonce
nonce = SecureRandom.hex(16)
session[:nonce] = nonce
render json: { nonce: nonce }
end
def verify
nonce = session[:nonce]
# ... rest of the code ...
end
end
Application Configuration
module HueBackend
class Application < Rails::Application
config.session_store :cookie_store, key: '_hue_session'
config.session_options = {
httponly: true,
same_site: "None",
secure: false
}
# ... rest of the configuration ...
Rails.application.config.middleware.insert_before ActiveRecord::Migration::CheckPending, ActionDispatch::Cookies
Rails.application.config.middleware.insert_before ActionDispatch::Cookies, Rails.application.config.session_store, Rails.application.config.session_options
end
end
Cors configuration
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins 'localhost:5173'
resource '/api/*',
headers: :any,
methods: [:get, :post, :put, :patch, :delete, :options, :head],
credentials: true
end
end
Code call! I’ve just tried but didn’t change anything.
By the way here are the logs in the terminal. As you can see Session ID in #nonce is: 2301f806d159f7b3e215f94f4c30765c.
But in #verify it’s empty.
Started GET "/api/nonce" for ::1 at 2023-08-31 20:55:26 +0200
Processing by Api::UsersController#nonce as */*
Response Headers: {"X-Frame-Options"=>"SAMEORIGIN", "X-XSS-Protection"=>"0", "X-Content-Type-Options"=>"nosniff", "X-Download-Options"=>"noopen", "X-Permitted-Cross-Domain-Policies"=>"none", "Referrer-Policy"=>"strict-origin-when-cross-origin"}
inside the nonce function
Session ID in nonce: 2301f806d159f7b3e215f94f4c30765c
NONCE in session nonce 2: d741deacafd1af3868b1214966341f16
NONCE in session nonce 3: d741deacafd1af3868b1214966341f16
Completed 200 OK in 2ms (Views: 0.2ms | ActiveRecord: 0.0ms | Allocations: 1137)
Started POST "/api/verify" for ::1 at 2023-08-31 20:55:31 +0200
Processing by Api::UsersController#verify as */*
Parameters: {"message"=>{"domain"=>"localhost:5173", "address"=>"0x822BD6Dbd9Dd540c06512a858C2b3B9550581744", "statement"=>"Sign in with Ethereum to the app.", "uri"=>"http://localhost:5173", "version"=>"1", "chainId"=>280, "nonce"=>"d741deacafd1af3868b1214966341f16", "issuedAt"=>"2023-08-31T18:55:27.996Z"}, "signature"=>"0xb965568ec7d75c817429d9a5e7267adf030f57e4883ebe4f7ad6a0a01c5b06e97b8896a6033b9a8df037837418439c96702342034bb6a5aaeea7eaf2a685c8151c", "user"=>{}}
Response Headers: {"X-Frame-Options"=>"SAMEORIGIN", "X-XSS-Protection"=>"0", "X-Content-Type-Options"=>"nosniff", "X-Download-Options"=>"noopen", "X-Permitted-Cross-Domain-Policies"=>"none", "Referrer-Policy"=>"strict-origin-when-cross-origin"}
Session ID in verify:
Entire session data:
NONCE in verify:
MESSAGE in verify: {"domain"=>"localhost:5173", "address"=>"0x822BD6Dbd9Dd540c06512a858C2b3B9550581744", "statement"=>"Sign in with Ethereum to the app.", "uri"=>"http://localhost:5173", "version"=>"1", "chainId"=>280, "nonce"=>"d741deacafd1af3868b1214966341f16", "issuedAt"=>"2023-08-31T18:55:27.996Z"}
SIGNATURE in verify: 0xb965568ec7d75c817429d9a5e7267adf030f57e4883ebe4f7ad6a0a01c5b06e97b8896a6033b9a8df037837418439c96702342034bb6a5aaeea7eaf2a685c8151c
ADDRESS in verify: 0x822BD6Dbd9Dd540c06512a858C2b3B9550581744
msg hash in verify signature: �l��Oӓ�n�� >�9�gZ�TĢ2K�"
public key in verify signature: 04f61b6e8bc3e99978b43e82277ac34d22f42e223a30fcc6f47a3443ea7080b9bb1ca2403a088618fc5bfb5520437a215447207329dc507f981f5ffb6c27c8a637
recovered address in verify signature: 0x25f54C426b2A5CcdA9Cd523DA3dA3b3aD96864c6
address in verify signature: 0x822BD6Dbd9Dd540c06512a858C2b3B9550581744
Completed 401 Unauthorized in 14ms (Views: 0.2ms | ActiveRecord: 57.1ms | Allocations: 1990)
Yeah, it seems that your problem is in convincing the browser to set this cookie.
The way it works is that Rails just tells your browser “please store this cookie” when you go to /nonce, then browser does a whole bunch of verifications before it agrees to store it. Then when you go to /verify — the fetch will ship this cookie if it was stored, but it seems like it wasn’t. So it seems your browser doesn’t like something about the way you ask it to store the cookie on /nonce step. (I assume the session cookie is never stored, right?).
If that’s all true, you have to be very careful about every little thing. Follow list like this, and make sure you don’t have any discrepancies in domain name (localhost vs. 127.0.0.1 is not good).
Basically, this doesn’t seem to be a Rails problem, but rather adhering to the browser security problem.
Weird, it looks like rails doesn’t even send Set-Cookie header. Maybe it does have to do with the way Rails creates sessions, or a session misconfiguration.
_session_id v9vZB6aK6SRDhZH25R1TGcW49HPt4%2B0agXGe5ozNn5TbrakY2rWbPKx5Np%2FpvdwdpqImunqIxKImEmO9Bk00Zg8w38GK%2FLHbNKeVOOP92GVi7CGeUTUyzQAuz3s7B0dedAUlYjLL%2BKo89%2Fj%2FUYjQ%2BMvbaODhcQLrlIJp7az1rxzjpBNN100r1lSRjD7OPz37piJBI8HUXAskm8KbWlwG0SINN%2F6cmvf5BY95G8SQr%2BvGVighoLLBE1vFhA%3D%3D--MEiNRuGxTwlv9oPa--DYMOL3rRaxGapcnFVtc0yw%3D%3D localhost / Session 333 ✓
None Medium
What’s weird is the default name session_id when I clearly changed it in the application.rb. So the logs in the terminal are exactly the same. No session ID in the /verify.