Generating special tokens that are unique, tamper proof and that can store information like the purpose of the token and the token's expiry can be very useful in certain scenarios.
You can create a unique token for specific purposes like email_verification or password_reset, attach them to your application URL endpoint and send it to the user via email.

Up until now, you might have used the ActiveRecord::SignedId API that allows you to create expirable tokens. And you can query ActiveRecord to find the record using the signed id.

Consider a scenario where we want to send a password reset link to the user. If you want to generate a unique token that expires after say 1 day.

Using ActiveRecord::SignedId :

Loading development environment (Rails 7.0.3)
user = User.first

signed_id = user.signed_id(expires_in: 1.day, purpose: :password_reset)
=> "eyJfcmFpbHMiOnsibWVzc2FnZSI6Ik16Yz0iLCJleHAiOm51bGwsInB1ciI6InVzZXIifX0=--a6aab77fee1ccc945e3fc19426799de062063e3db9b0c631a98693475a5e4b11"

User.find_signed(signed_id, purpose: :password_reset)
=> <User id: 1 ...>

user.update!(password: "new password")

User.find_signed(signed_id, purpose: :password_reset)
=> <User id: 1 ...> # Token is still valid even after its purpose is served

Even though the purpose of the token has been served, it will stay valid until it expires.

Using ActiveRecord::Base::generates_token_for :

class User < ActiveRecord::Base
  has_secure_password

  generates_token_for :password_reset, expires_in: 1.day do
    BCrypt::Password.new(password_digest).salt[-10..]
  end
end

Added in Rails 7.1.0, generates_token_for works similarly to signed_id. While signed_id uses the model ID as the payload, generates_token_for uses a combination of the model ID and whatever is returned by the block given to it. The block should return an attribute associated with the purpose of the token. Once the attribute is updated, the payload is updated as well thus invalidating the old token.

In the above example, the payload combination will be the model's ID and the last 10 characters of the password digest.

Loading development environment (Rails 7.1.0-alpha)
user = User.first

token = user.generate_token_for(:password_reset)
=> "eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaGJCMmtxU1NJUFMzbEpjMFZ4UmtKb0xnWTZCa1ZVIiwiZXhwIjpudWxsLCJwdXIiOiJVc2VyXG5wYXNzd29yZF9yZXNldFxuIn19--845ed7d939cbe28bb565334352f6ac7884ddc8d0"

User.find_by_token_for(:password_reset, token)
=> <User id: 1 ...>

user.update!(password: "new password")

User.find_by_token_for(:password_reset, token)
=> nil

The BCrypt salt changes when the password is updated and hence the token is invalidated on the first update of the password. This makes the token available for one-time use.

References