sly
May 2, 2024, 3:53am
1
before_validation :generate_token, on: :create, if: :token_blank?
private
def generate_token
self.token = SecureRandom.hex(32)
end
There’s a unique index on the database field itself (using migration) - but on the model this is all I have.
Anything I should add to make this more robust?
abc
May 2, 2024, 2:35pm
2
If you are using Rails 7.1, rails adds the method generates_token_for
.
Your model could look like this:
class User < ActiveRecord::Base
generates_token_for :invitation_token
end
You can get new token by calling:
token = User.first.generate_token_for(:invitation_token)
You can find a user by this token:
user = User.find_by_token_for(:invitation_token, token)
Note that you don’t have to create additional database field. The best part
Check: ActiveRecord::TokenFor::ClassMethods - Ruby on Rails API
2 Likes
sly
May 2, 2024, 6:10pm
3
Thank you, that worked beautifully! Bonus: no need for another database field!
Kitchens
(Kitchens)
May 8, 2024, 2:27pm
4
You can use a GUID, it’s even a good idea. Just make sure your GUID generator is using a cryptographically secure random number generator.
BenKoshy
(Ben Koshy)
February 11, 2025, 10:36am
5
Note that you don’t have to create additional database field. The best part
You don’t have create a database field?
How would you assert / compare that the token is correct or valid not? I would be very interested to know more.
Update: you are right. thanks for the pointer. It saves me from keeping an uneeded database field/
@BenKoshy I have a complete explanation of how this feature works in another thread in the here:
We deploy rails edge to production, so are constantly reading PRs that were merged. I just stumbled on a cool one and though it would be nice share it and maybe other future PRs
Link
What does it do?
Generate a single use token without needing an extra database field to track it.
Sample Code:
class User < ActiveRecord::Base
has_secure_password
generates_token_for :password_reset do
# BCrypt salt changes when password is updated
BCrypt::Password.new(password_digest).salt[-10..…
The basic idea is that the method uses the record id and the name you pass it to generate an encrypted value, that it can then decrypt and compare back with the id. You can even pass it a block with some additional data for verification. This is useful if you want the token to stop working after something changes in the record.
1 Like