Given a lot of time has passed since the last v4.x release, and there's
been many changes (including breaking ones) merged to main, let's go
with an "RC" version before doing a final release.
If we don't hear any major issues, I plan to release a final version in
a couple of weeks.
Otherwise if we humanized the whole string, it could cause us to change
the output of strings with periods and maybe other side-effects, since
we're changing the whole string from i18n.
This is safer as it only changes the first char of the translated
message, and only if it is a match with the first translated auth key,
so we can more safely humanize & downcase all auth keys to interpolate
in the message whenever needed.
Also add changelog for the change.
"Invalid Email or password." is grammatically incorrect, a change
introduced a while ago by #4014.
Signed-off-by: Carlos Antonio da Silva <carlosantoniodasilva@gmail.com>
Used Rails' secure_compare method inside the definition of
secure_compare. This will handle the empty strings comparison and
return true when both the parameters are empty strings.
Fixes#4441, #4829
In Rack v3.1.0, the symbol for HTTP status code 422 was changed from `:unprocessable_entity` to `:unprocessable_content`.
As a result, when using rack 3.2 with the following configuration in `config/initializers/devise.rb`, a warning is shown on login failure:
```ruby
# config/initializers/devise.rb
Devise.setup do |config|
...
config.responder.error_status = :unprocessable_entity
```
Warning message:
```sh
/path-to-app/vendor/bundle/ruby/3.4.0/gems/devise-4.9.4/lib/devise/failure_app.rb:80: warning: Status code :unprocessable_entity is deprecated and will be removed in a future version of Rack. Please use :unprocessable_content instead.
```
This warning can be resolved by updating the config as follows:
```diff
# config/initializers/devise.rb
Devise.setup do |config|
...
+ config.responder.error_status = :unprocessable_content
- config.responder.error_status = :unprocessable_entity
```
This fixes the root cause of the warning for new apps by adjusting the generated config during `$ rails generate devise:install` depending on the rack version, so new apps using newer Rack versions generate `error_status = :unprocessable_content` instead of `:unprocessable_entity`.
Existing apps are handled by [latest versions of Rails, which will now transparently convert the code under the hood to avoid the Rack warning](https://github.com/rails/rails/pull/53383), and Devise will use that translation layer when available in the failure app to prevent the warning there as well (since that isn't covered by Rails automatic conversion).
Signed-off-by: Carlos Antonio da Silva <carlosantoniodasilva@gmail.com>
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
All of these have been deprecated for years, if we're releasing a new
major version, let's take the opportunity to do some cleanup.
* Remove deprecated `:bypass` option from `sign_in` helper,
use `bypass_sign_in` instead.
* Remove deprecated `devise_error_messages!` helper,
use `render "devise/shared/error_messages", resource: resource` instead.
* Remove deprecated `scope` second argument from `sign_in(resource, :admin)`
controller test helper, use `sign_in(resource, scope: :admin)` instead.
* Remove deprecated `Devise::TestHelpers`,
use `Devise::Test::ControllerHelpers` instead.
Closes#5739
Devise hasn't been tested with Mongoid since Rails version 5, only 4.x was still running those tests.
This enables the tests again on all currently supported Rails versions, with their respective mongoid supported versions. There were a couple of minor tweaks to make it happen, namely:
* The way we were dropping the session before doesn't work in later versions so I changed back to calling `purge!` which appears to work fine. We used to call `Mongoid.purge!` but that changed in #4686.
* Some of the configs in the Rails test app were setting Active Record values when outside of the AR ORM tests, updated those to make sure they are not set when running mongoid ORM tests.
* The validations added to the shared admin code in tests were only checking for Rails version 5.1, but we need to use the same check for AR 5.1 that is used in code, otherwise it will try to use methods not available in mongoid there.
Update argument name for config.warden [ci skip]
The argument for the block passed to `config.warden` is no a `Warden::Manager` instance but a `Warden::Config` instance, but it is confusingly named `manager` in the generated file.
Renaming this to `warden_config` for clarity.
When ActionMailer is not defined we have empty app/mailers/devise/mailer.rb file and Zeitwerk doesn't
like that and errors with
```
expected file app/mailers/devise/mailer.rb to define constant Devise::Mailer
```
The fix is to tell Zeitwerk to ignore that file if ActionMailer constant if not defined.
I tried to write a spec for it but since specs are run in the same process it's hard to have two
Rails applications where one of them has ActionMailer define and the seconds one doesn't.
Starting from Rails 8.0, routes are lazy-loaded by default in test and development environments.
However, Devise's mappings are built during the routes loading phase.
To ensure it works correctly, we need to load the routes first before accessing @@mappings.
A common usage of I18n with different locales is to create some around
callback in the application controller that sets the locale for the
entire action, via params/url/user/etc., which ensure the locale is
respected for the duration of that action, and resets at the end.
Devise was not respecting the locale when the authenticate failed and
triggered the failure app, because that happens in a warden middleware
right up in the change, by that time the controller around callback had
already reset the locale back to its default, and the failure app would
just translate flash messages using the default locale.
Now we are passing the current locale down to the failure app via warden
options, and wrapping it with an around callback, which makes the
failure app respect the set I18n locale by the controller at the time
the authentication failure is triggered, working as expected. (much more
like a normal controller would.)
I chose to introduce a callback in the failure app so we could wrap the
whole `respond` action processing rather than adding individual `locale`
options to the `I18n.t` calls, because that should ensure other possible
`I18n.t` calls from overridden failure apps would respect the set locale
as well, and makes it more like one would implement in a controller. I
don't recommend people using callbacks in their own failure apps though,
as this is not going to be documented as a "feature" of failures apps,
it's considered "internal" and could be refactored at any point.
It is possible to override the locale with the new `i18n_locale` method,
which simply defaults to the passed locale from the controller.
Closes#5247Closes#5246
Related to: #3052, #4823, and possible others already closed.
Related to warden: (may be closed there afterwards)
https://github.com/wardencommunity/warden/issues/180https://github.com/wardencommunity/warden/issues/170
- ### Context
Since version 2.0.0, Omniauth no longer recognizes `GET` request
on the auth path (`/users/auth/<provider>`). `POST` is the only
verb that is by default recognized in order to mitigate CSRF
attack. 66110da85e/lib/omniauth/strategy.rb (L205)
Ultimatelly, when a user try to access `GET /users/auth/facebook`,
Devise [passthru action](6d32d2447c/app/controllers/devise/omniauth_callbacks_controller.rb (L6))
will be called which just return a raw 404 page.
### Problem
There is no problem per se and everything work. However the
advantage of not matching GET request at the router layer allows
to get that same 404 page stylized for "free" (Rails ending up
rendering the 404 page of the app).
I believe it's also more consistent and less surprising for users
if this passthru action don't get called.
### Drawback
An application can no longer override the `passthru` to perform
the logic it wants (i.e. redirect the user).
If this is a dealbreaker, feel free to close this PR :).
Rails allow procs and lambda with either zero or more argument. Devise
however always tried to call instance_eval on those values, which does
always pass one argument: self.
There was a PR to fix this specific problem in Devise https://github.com/heartcombo/devise/pull/4627,
before the arity check was fixed in rails itself: https://github.com/rails/rails/pull/30391.
But even if the problem was fixed in Rails, Devise was still calling
the proc/lambas with instance_eval. That meant the fix added to Rails
did not apply to Devise.
The fix is to let Rails handle the :from and :reply_to defaults. We do
that by unsetting the headers instead of trying to replicate Rails handling
in Devise. This lets Rails handle it when setting up the mailer.
Even though this is considered an internal / non-public / nodoc method,
it seems some libraries relied on it internally, causing some breakage.
Known libraries so far are `devise-security` and
`devise-pwned_password`.
Closes#5580
Devise is able to work with a specific ORM, either Active Record or
Mongoid, but nothing stops apps from using multiple ORMs within the same
application -- they just need to pick one to use with Devise. That's
generally determined by the require that is added to the Devise
initializer, that will load up either ORM's extensions so you can call
things like `devise` on your model to set it up.
However, some conditional logic in Devise, more specifically around
dirty tracking, was only considering having Active Record loaded up
after a certain version, to determine which methods to call in parts of
the implementation. In a previous change we refactored all that dirty
tracking code into this `OrmDirtyTracking` module to make it easier to
view all the methods that were being conditionally called, and now we're
repurposing this into a more generic `Orm` module (that's nodoc'ed by
default) so that upon including it, we can conditionally include the
proper dirty tracking extensions but also check whether the including
model is really Active Record or not, so we can trigger the correct
dirty tracking behavior for Mongoid as well if both are loaded on the
same app, whereas previously the Mongoid behavior would always use the
new Active Record behavior, but support may differ.
While we are also working to ensure the latest versions of Mongoid are
fully running with Devise, this should improve the situation by giving
apps with multiple ORMs loaded a chance to rely on some of these Devise
bits of functionality better now that weren't working properly before
without some monkey-patching on their end.
Closes#5539Closes#4542
We have an number of conditions due to how dirty tracking changed around
Rails 5.1, that implement methods using one or another method call. I
might need more of this for mongo upgrades based on an initial
investigation, plus this makes the code really hard to reason about
sometimes with these many conditionals.
While I want to drop support for older versions of Rails soon, this
centralization of dirty methods that are used by devise conditionally
simplifies the usage considerably across the board, moves the version
condition to a single place, and will make it easier to refactor later
once we drop older Rails version by simply removing the `devise_*`
versions of the methods, alongside the prefix on the method calls for
the most part, since those methods follow the naming of the newer Rails
versions.