From 051f94a49831d04cf7c9f034f82fe90d46a19f6d Mon Sep 17 00:00:00 2001 From: Carlos Antonio da Silva Date: Wed, 31 Dec 2025 09:12:25 -0300 Subject: [PATCH] Pass locale with activatable / timeoutable hooks (#5815) We need to explicitly pass the `locale` around from the options (passed to `warden.authenticate!` for instance) or the `I18n.locale` when logging out and redirecting the user via `throw :warden`, otherwise in a multi-locale app we'd lose the locale previously set / passed around and fallback to the default for that flash message. This is a follow-up of the fixes in #5567 where we implemented the locale passing logic down to the failure app, but it missed these places where we were using `throw :warden`. Closes #5812 --- lib/devise/hooks/activatable.rb | 2 +- lib/devise/hooks/timeoutable.rb | 2 +- test/integration/confirmable_test.rb | 9 +++++++++ test/integration/timeoutable_test.rb | 11 +++++++++++ test/rails_app/app/controllers/admins_controller.rb | 7 ------- .../app/controllers/application_controller.rb | 11 +++++++++++ test/support/locale/pt-BR.yml | 2 ++ 7 files changed, 35 insertions(+), 9 deletions(-) diff --git a/lib/devise/hooks/activatable.rb b/lib/devise/hooks/activatable.rb index b2eaea19..9feb9630 100644 --- a/lib/devise/hooks/activatable.rb +++ b/lib/devise/hooks/activatable.rb @@ -7,6 +7,6 @@ Warden::Manager.after_set_user do |record, warden, options| if record && record.respond_to?(:active_for_authentication?) && !record.active_for_authentication? scope = options[:scope] warden.logout(scope) - throw :warden, scope: scope, message: record.inactive_message + throw :warden, scope: scope, message: record.inactive_message, locale: options.fetch(:locale, I18n.locale) end end diff --git a/lib/devise/hooks/timeoutable.rb b/lib/devise/hooks/timeoutable.rb index 772eb142..f1e7f6d5 100644 --- a/lib/devise/hooks/timeoutable.rb +++ b/lib/devise/hooks/timeoutable.rb @@ -25,7 +25,7 @@ Warden::Manager.after_set_user do |record, warden, options| record.timedout?(last_request_at) && !proxy.remember_me_is_active?(record) Devise.sign_out_all_scopes ? proxy.sign_out : proxy.sign_out(scope) - throw :warden, scope: scope, message: :timeout + throw :warden, scope: scope, message: :timeout, locale: options.fetch(:locale, I18n.locale) end unless env['devise.skip_trackable'] diff --git a/test/integration/confirmable_test.rb b/test/integration/confirmable_test.rb index c951eb0b..c29d7aba 100644 --- a/test/integration/confirmable_test.rb +++ b/test/integration/confirmable_test.rb @@ -136,6 +136,15 @@ class ConfirmationTest < Devise::IntegrationTest end end + test 'not confirmed user redirect respects i18n locale set' do + swap Devise, allow_unconfirmed_access_for: 0.days do + sign_in_as_user(confirm: false, visit: new_user_session_path(locale: "pt-BR")) + + assert_contain 'Você precisa confirmar seu email para continuar' + assert_not warden.authenticated?(:user) + end + end + test 'not confirmed user should not see confirmation message if invalid credentials are given' do swap Devise, allow_unconfirmed_access_for: 0.days do sign_in_as_user(confirm: false) do diff --git a/test/integration/timeoutable_test.rb b/test/integration/timeoutable_test.rb index d11d5910..d7e31ba6 100644 --- a/test/integration/timeoutable_test.rb +++ b/test/integration/timeoutable_test.rb @@ -167,6 +167,17 @@ class SessionTimeoutTest < Devise::IntegrationTest end end + test 'error message redirect respects i18n locale set' do + user = sign_in_as_user + + get expire_user_path(user) + get root_path(locale: "pt-BR") + follow_redirect! + + assert_contain 'Sua sessão expirou. Por favor faça o login novamente para continuar.' + assert_not warden.authenticated?(:user) + end + test 'time out not triggered if remembered' do user = sign_in_as_user remember_me: true get expire_user_path(user) diff --git a/test/rails_app/app/controllers/admins_controller.rb b/test/rails_app/app/controllers/admins_controller.rb index 957aa6f0..c732f589 100644 --- a/test/rails_app/app/controllers/admins_controller.rb +++ b/test/rails_app/app/controllers/admins_controller.rb @@ -1,15 +1,8 @@ # frozen_string_literal: true class AdminsController < ApplicationController - around_action :set_locale before_action :authenticate_admin! def index end - - private - - def set_locale - I18n.with_locale(params[:locale] || I18n.default_locale) { yield } - end end diff --git a/test/rails_app/app/controllers/application_controller.rb b/test/rails_app/app/controllers/application_controller.rb index e60ba0c2..616845d7 100644 --- a/test/rails_app/app/controllers/application_controller.rb +++ b/test/rails_app/app/controllers/application_controller.rb @@ -5,9 +5,20 @@ class ApplicationController < ActionController::Base protect_from_forgery + around_action :set_locale before_action :current_user, unless: :devise_controller? before_action :authenticate_user!, if: :devise_controller? respond_to(*Mime::SET.map(&:to_sym)) devise_group :commenter, contains: [:user, :admin] + + private + + def set_locale + I18n.with_locale(params[:locale] || I18n.default_locale) { yield } + end + + def default_url_options + {locale: params[:locale]}.compact + end end diff --git a/test/support/locale/pt-BR.yml b/test/support/locale/pt-BR.yml index 5c57e190..687cc87a 100644 --- a/test/support/locale/pt-BR.yml +++ b/test/support/locale/pt-BR.yml @@ -3,3 +3,5 @@ pt-BR: failure: invalid: "%{authentication_keys} ou senha inválidos." unauthenticated: "Para continuar, faça login ou registre-se." + timeout: "Sua sessão expirou. Por favor faça o login novamente para continuar." + unconfirmed: "Você precisa confirmar seu email para continuar."