Rails 7.1 Introduces ActiveRecord::Base::generates_token_for

With ActiveRecord::Base.generates_token_for we can generate tokens for specific purposes and verify their authenticity. This feature simplifies the process of generating and validating tokens, enhancing the security and user experience of our application. Can be used to implement features like password reset, email confirmation, and other features that require single-use tokens.

generates_token_for takes these below arguments:

  • Purpose (Symbol or String) as the first argument, which is used to distinguish between different types of tokens in our application. The purpose helps us categorize and manage tokens for various use cases, such as authentication, email confirmation, password reset, or other custom scenarios. It is typically represented as a symbol or string.

  • expires_in (Duration): Specifies the token’s expiration time, which is the length of time the token remains valid. we can use a duration, such as 1.day, 2.hours, etc. Tokens will automatically expire after the specified duration. By default, tokens do not expire.

  • Block: An optional block that defines how the token data is generated. The block is evaluated in the context of the model instance for which the token is being generated. The block’s return value is embedded in the token as JSON data. When fetching a record using the token, the block is evaluated again in the context of the fetched record, and the two JSON values are compared for validation.

  • Token only gets expires when the duration passed to expires_in is completed or the value passed to the block gets updated.

class User < ActiveRecord::Base
  validates :name, presence: true

  generates_token_for :name_confirmation, expires_in: 24.hours do
    name
  end
end

user = User.create(name: 'John Doe')
token = user.generate_token_for(:name_confirmation)
User.find_by_token_for(:name_confirmation, token) # => user

user.update!(name: "Sam Smith")
User.find_by_token_for(:name_confirmation, token) # => nil
generate_token_for(purpose)

Generate token on a record by passing the predefined purpose. Return the generated token.

user = User.first
token = user.generate_token_for(:name_confirmation)
find_by_token_for(purpose, token)

Finds a record using a given token for a predefined purpose. Returns nil if the token is invalid or the record was not found.

User.find_by_token_for(:name_confirmation, token)
find_by_token_for!(purpose, token)

Finds a record using a given token for a predefined purpose. Raises ActiveSupport::MessageVerifier::InvalidSignature if the token is invalid (e.g. expired, bad format, etc). Raises ActiveRecord::RecordNotFound if the token is valid but the record was not found.

User.find_by_token_for!(:name_confirmation, token)
Generate token with no block

When we dont pass any block which contains the attributes to generates_token_for. Even after updating the record the token won’t be expired. It only expires once the duration defined in expires_in is completed.

class User < ActiveRecord::Base
  validates :name, presence: true

  generates_token_for :name_confirmation, expires_in: 40.seconds
end

user = User.create(name: 'John Doe')
token = user.generate_token_for(:name_confirmation)
User.find_by_token_for(:name_confirmation, token) # => user

user.update!(name: "Sam Smith")
User.find_by_token_for(:name_confirmation, token) # => user

# 40 seconds later...
User.find_by_token_for(:name_confirmation, token) # => nil
Generate token with block

By default, the generate token do not have any expiry and if the value passed in the block gets updated then token will expiry.

class User < ActiveRecord::Base
  validates :name, presence: true

  generates_token_for :name_confirmation do
    name
  end
end

user = User.create(name: 'John Doe')
token = user.generate_token_for(:name_confirmation)
User.find_by_token_for(:name_confirmation, token) # => user

user.update!(name: "Sam Smith")
User.find_by_token_for(:name_confirmation, token) # => nil
Generate token with no expiry and no block

By default, the generated token do not have any expiry and without passing any block the token won’t be expired even after updating the attributes. This is useful when we need token with no expiry like auth token or some unique admin tokens.

class User < ActiveRecord::Base
  validates :name, presence: true

  generates_token_for :auth_token
end

user = User.create(name: 'John Doe')
token = user.generate_token_for(:auth_token)
User.find_by_token_for(:auth_token, token) # => user

user.update!(name: "Sam Smith")
User.find_by_token_for(:auth_token, token) # => user

Need help on your Ruby on Rails or React project?

Join Our Newsletter