Authentication in Rails 7.1
When securing a web application there are two things that must be taken into consideration: Authentication and Authorization. At a high-level Authentication is “who are you?” and authorization is “are you allowed to be here?”. A great example of this is your driver’s license. You can often use your license to prove who you are (Authentication). But it doesn’t necessarily give you permission to be somewhere (Authorization). If you’re trying to get to a safe deposit box at a bank, your driver’s license may prove who you are, but your bank account lets you into the box.
A while ago, we looked at pundit and cancan for Authorization. Today, we’ll look at a new feature in Rail 7.1 called authenticated_by (and has_secure_password).
TheNew Feature for Authentication in Rails 7.1
Before we talk about the new feature, we have to talk about
has_secure_password for a moment. This method has been around for a while now. Essentially, you create a model with an attribute
password_digest and then call
class User < ApplicationRecord validates :email, presence: true, uniqueness: true has_secure_password # there is an attribute called "password_digest" end
This allows for some very simple, but secure password-based authentication.
has_secure_password does allow for some simple authentication, it also has a pretty solid problem. The code we used to “find_by” will short circuit if no record can be found. Programmatically that is what we want, but it opens you up to a “Timing Based Enumeration Attack”.
In short, the method returns faster if no user exists, and longer if the user exists. Once I know if a user exists, then I can try to get into their account, either by brute forcing, or by trying a number of that user’s known passwords.
That is were
authenticated_by comes in. In a model with
has_secure_password. You can use
authenticated_by to make each check take roughly the same amount of time.
User.authenticate_by(email: 'email@example.com', password: 'password123')
It accomplishes this by cryptographically hashing the password attribute, and then doing a lookup based on email and the hashed password. Because it always hashes the password even if the user doesn’t exist there is less variation between a failure because of a bad user and a failure due to an incorrect password.