New Authentication Feature in Rails 7
Ruby on Rails Authentication Software Development
June 29, 2022 - Robert C.

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

has_secure_password

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 has_secure_password

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.

  User.find_by(email: "test@example.com")&.authenticate("password123")

authenticated_by

While 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: 'test@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.

YOU MAY ALSO LIKE

Formatting Dates in Ruby
Dec 9th 2021 - Erhan B.

Formatting Dates in Ruby