Warden doesn't preserve locale when thrown
JoeWoodward opened this issue · comments
We have been experiencing an issue where the wrong locale is being used for error messages returned from the Devise FailureApp, I believe it's due to the way that warden throws and catches.
The recommended way of setting the locale for a rails controller action is to use an around_action callback, e.g.
prepend_around_action :with_locale
def with_locale(action)
I18n.with_locale(current_locale) { action }
end
When warden fails to authenticate it throws and calls a rack endpoint, the failure app... This means that the around_action is exited so the locale is no longer applied.
I don't see any way to tell warden to preserve the locale that was applied when the throw was called. Should warden do this internally?
Or maybe this should be handled by Devise? Perhaps devise should be passing the current locale into the throw call
Related to heartcombo/devise#4823
Actually it's not devise that throws, it's warden in the authenticate!
method
I read through the related devise thread. It is important to remember that Warden only deals with Rack. We intentionally avoid any reliance on Rails (ala around_action
) because Warden works with all Rack-based frameworks (Rails, Sinatra, Hanami, Grape, etc).
Did you try the changes that were suggested in platformatec/devise#4823 ? It appears from that discussion that changing the middleware ordering will resolve the issue.
This is not related to the middleware stack in my case. I believe other users in that thread also have the same issue and failed to locate the problem. We have resolved the issue by using a custom failure app and manually setting the locale based on the params but this solution is suboptimal as the locale might not always come from the request params.
Another issue we're facing now is that this custom failure app isn't being used while Warden.test_mode!
is activated. Looking into a fix for that now.
From reading through the docs I have a feeling there is a solution to the problem that won't require any changes to warden or devise. Will investigate further and report back
I18n::Middleware
discussed in heartcombo/devise#4823 serves only to reset the locale after the request completes. It's not about setting the correct locale.
The problem here is specific to the use of I18n.with_locale
. I've run a number of tests with the "Invalid email/password" message.
Set I18n.locale
in before_action
in application_controller.rb
:
before_action do
I18n.locale = :es
end
Works.
Set I18n.locale
with middleware:
class LocaleMiddleware
def initialize(app)
@app = app
end
def call(env)
I18n.locale = :es
status, headers, body = @app.call(env)
end
end
config.middleware.use ::LocaleMiddleware
Works.
Use I18n.locale
in around_action
in application_controller.rb
:
around_action :test_set_locale
def test_set_locale(&action)
I18n.with_locale(:es, &action)
end
Doesn't work for the error message, works for the rest of the app.
Use I18n.locale
in middleware:
class LocaleMiddleware
def initialize(app)
@app = app
end
def call(env)
I18n.with_locale(:es) do
status, headers, body = @app.call(env)
end
end
end
config.middleware.use ::LocaleMiddleware
Doesn't work for anything.
(double posting of heartcombo/devise#5247 (comment))
I also had the same issue, and resulted in new rack middleware which handles query param, cookie and HTTP_ACCEPT_LANGUAGE header in this order, then store explicitly requested locale in cookie.
https://github.com/nov/rack-locale_memorable
ps.
Rack::Locale wasn't enough for our use-case since it does handle only HTTP_ACCEPT_LANGUAGE header.
Hey, I finally have a PR up with this change on the Devise side of things: heartcombo/devise#5567, basically carrying over the locale from the request to warden and wrapping the failure app with it. If you happen to still be having this issue and want to give it a shot, let me know if you run into any issues.
Just stumbled upon this issue and the latest PR did not help in my case at first, but after also adding this fix: heartcombo/devise#5602 (comment) - everything works as expected