diff --git a/app/controllers/concerns/authentication.rb b/app/controllers/concerns/authentication.rb index 799fefd..f9f51ae 100644 --- a/app/controllers/concerns/authentication.rb +++ b/app/controllers/concerns/authentication.rb @@ -73,7 +73,15 @@ module Authentication # Set domain for cross-subdomain authentication if we can extract it cookie_options[:domain] = domain if domain.present? - cookies.signed.permanent[:session_id] = cookie_options + # When "Remember me" is off, issue a browser-session cookie (no Expires) + # so closing the browser signs the user out — especially important on + # shared devices. The server Session#expires_at still enforces the + # 24h / 30d window regardless. + if remember_me + cookies.signed.permanent[:session_id] = cookie_options + else + cookies.signed[:session_id] = cookie_options + end # Create a one-time token for immediate forward auth after authentication # This solves the race condition where browser hasn't processed cookie yet diff --git a/test/controllers/sessions_controller_test.rb b/test/controllers/sessions_controller_test.rb index c273f59..90d6b74 100644 --- a/test/controllers/sessions_controller_test.rb +++ b/test/controllers/sessions_controller_test.rb @@ -30,4 +30,22 @@ class SessionsControllerTest < ActionDispatch::IntegrationTest assert_redirected_to signin_path assert_empty cookies[:session_id] end + + test "session cookie has no Expires attribute when remember_me is off" do + post session_path, params: {email_address: @user.email_address, password: "password", remember_me: "0"} + + set_cookie = Array(response.headers["Set-Cookie"]).find { |c| c.start_with?("session_id=") } + assert set_cookie, "session_id cookie should be set" + refute_match(/expires=/i, set_cookie, + "without Remember me, the session cookie must be a browser-session cookie (no Expires)") + end + + test "session cookie has long-lived Expires attribute when remember_me is on" do + post session_path, params: {email_address: @user.email_address, password: "password", remember_me: "1"} + + set_cookie = Array(response.headers["Set-Cookie"]).find { |c| c.start_with?("session_id=") } + assert set_cookie, "session_id cookie should be set" + assert_match(/expires=/i, set_cookie, + "with Remember me, the cookie should have an Expires attribute") + end end