Rails 7.1 adds ActiveRecord::Base::generates_token_for

railsDecember 05, 2023Dotby Alkesh Ghorpade

Rails 7.1 introduced the ActiveRecord::Base::generates_token_for method, which provides a convenient way to generate and validate tokens for various use cases in Rails applications. This feature streamlines token generation and validation, bolstering the security and user experience of our application. It facilitates the implementation of features like password reset, email verification, and other functionalities that rely on one-time tokens.

Before Rails 7.1

Before Rails 7.1, signed_id was used to generate tokens. It is primarily used to prevent tampering or unauthorized access to data by generating a secure digital signature.

The primary usage of signed_id is as below:

user = User.last

# signed_id for last user
signed_id = user.signed_id
=> "eyJfcmFpbHMiOnsibWVzc2FnZSI6Ik9RPT0iLCJleHAiOm51bGwsInB1ciI6InVzZXIifX0=--ab2c33729462c2c1febe02b4a46ebb8c5f024c292b62987c3323049a591aaab6"

# Find user with signed_id
User.find_signed(signed_id)
=> #<User:0x00000001156c57d0 id: 9, name: "sam@example.com".....

The validity period for a signed ID is specified during its generation using the signed_id(expires_in: 15.minutes) instance method. If the validity period has passed before attempting to find a record using a signed ID, the signed ID will no longer be valid and will return nil.

user = User.last

# signed_id for last user
signed_id = user.signed_id(expires_in: 15.minutes)
=> "eyJfcmFpbHMiOnsibWVzc2FnZSI6Ik9RPT0iLCJleHAiOm51bGwsInB1ciI6InVzZXIifX0=--ab2c33729462c2c1febe02b4a46ebb8c5f024c292b62987c3323049a591aaab6"

# after 5 mins
User.find_signed(signed_id)
=> #<User:0x00000001156c57d0 id: 9, name: "sam@example.com".....

# after 15 mins
User.find_signed(signed_id)
=> nil

While signed IDs provide a secure way to authenticate users, they cannot track the usage of tokens. Therefore, if a token is meant for single use, it must be stored and monitored in a database until it expires to prevent unauthorized reuse.

In Rails 7.1

Rails 7.1 adds the ActiveRecord::Base::generates_token_for method. The generates_token_for method allows embedding data from a record into the generated token. When the token is used to retrieve the corresponding record, the data stored in the token is compared with the current data of the record. If there's a mismatch, the token is deemed invalid, similar to an expired token.

class User < ActiveRecord::Base
  has_secure_password

  generates_token_for :password_reset
end

user = User.last
token = user.generate_token_for(:password_reset)

User.find_by_token_for(:password_reset, token)
=> #<User:0x00000001156c57d0 id: 9, name: "sam@example.com".....

The generated tokens never expire; to specify the expiry time, you can pass the expires_in option.

class User < ActiveRecord::Base
  has_secure_password

  generates_token_for :password_reset, expires_in: 1.day
end

This method offers several advantages over manually generating and validating tokens:

Simplicity: It simplifies the process of generating and validating tokens, reducing the amount of code required and making it easier to maintain.

Security: It incorporates security measures to prevent token forgery and ensure the integrity of tokens.

Flexibility: It allows for embedding additional data in the token, which can help validate specific conditions or identify its purpose.

Expiration: It supports setting an expiration time for tokens, ensuring that tokens become invalid after a specified period, enhancing security and preventing misuse.

Consistency: It promotes consistency in token generation and validation across the application, reducing the risk of errors.

To know more about this feature, please refer to this PR.

Closing Remark

Could your team use some help with topics like this and others covered by ShakaCode's blog and open source? We specialize in optimizing Rails applications, especially those with advanced JavaScript frontends, like React. We can also help you optimize your CI processes with lower costs and faster, more reliable tests. Scraping web data and lowering infrastructure costs are two other areas of specialization. Feel free to reach out to ShakaCode's CEO, Justin Gordon, at justin@shakacode.com or schedule an appointment to discuss how ShakaCode can help your project!
Are you looking for a software development partner who can
develop modern, high-performance web apps and sites?
See what we've doneArrow right