Rails 7.1 - authenticated_by
Rails provides has_secure_password
class method to store passwords securely. You need to have password_digest
attribute in your model to get it working.
For example, if you have a user model
class User < ApplicationRecord
has_secure_password
end
The migration for user model looks like
class CreateUsers < ActiveRecord::Migration[7.0]
def change
create_table :users do |t|
t.string :email
t.string :password_digest
t.timestamps
end
end
end
Now if you authenticate the user using authenticate
method
Before Rails 7.1
User.find_by(email: params[:email])&.authenticate
For the above code, there are three cases
If the user doesn't exist it just returns
nil
If the user exists and the password doesn't match it will return
false
If the user exists and the password match it returns
user
In scenarios where a user exists, the process takes an extended duration in contrast to situations where the user does not exist. This time discrepancy can potentially be exploited by an attacker to deduce the presence of a valid username. Subsequently, the attacker could proceed to test various compromised passwords (sourced from other platforms) for unauthorized access. This technique is commonly referred to as timing-based enumeration attacks.
In Rails 7.1
authenticated_by
method has been introduced. in this Pull Request https://github.com/rails/rails/pull/43765/files
User.authenticated_by(email: “test@example.com”, password: “password123”)
It will always take the same amount of time whether the user exists or does not exist
Unlike authenticate
it always returns nil
for non-existing users or for unmatched passwords and it will raise an exception if we don't provide email or password
References
https://guides.rubyonrails.org/active_model_basics.html#securepassword
https://blog.kiprosh.com/rails-7-1-adds-authenticated_by/
https://newsletter.shortruby.com/p/rails-71-authenticate_by-new-method