Sometimes, you may want to use token-based authentication to grant your users access to some resource for some time, after which the token expires and needs to be regenerated.
You could manually create a token on your models as follows:
class User < ApplicationRecord
before_create :generate_token
private
def generate_token
self.token = SecureRandom.base58(24)
end
end
However, a better solution is to use the has_secure_token
macro provided by Rails. By default, it will use the name :token
and length 24, but you can provide a different name and length for your token.
class User < ApplicationRecord
has_secure_token :access_code, length: 30
end
Here's a passing test that shows how it works.
test 'user has a secure access code which can be regenerated' do
user = create(:user)
assert user.access_code # rpWRC...
assert_equal 30, user.access_code.length
user.regenerate_access_code # J6K9u...
end
Note: If you're using it for the first time, don't forget to create the backing column for your model.
class AddAccessCodeToUser < ActiveRecord::Migration[7.0]
def change
add_column :users, :access_code, :string
end
end
For the most part, you can be assured that the generated token will be unique. However, consider adding a unique index in the database just in case, to deal with this highly unlikely scenario.
Regenerating Tokens
Sometimes, for security reasons, you may want to expire the user's token or access code after some time. This is useful if they haven't been active on your application for a while and you want to make sure the old token isn't compromised.
You can regenerate the token using the regenerate_{token}
method on your model.
def handle_inactive_member(user)
user.regenerate_access_code
end
Behind the Scenes
If you open the secure_token.rb
file in the Rails codebase, you'll notice that the ActiveRecord::SecureToken
module is a Rails Concern.
Here's a simplified version of the source code.
# activerecord/lib/active_record/secure_token.rb
module ActiveRecord
module SecureToken
extend ActiveSupport::Concern
module ClassMethods
def has_secure_token(attribute = :token, length: MIN_LENGTH)
require "active_support/core_ext/securerandom"
define_method("regenerate_#{attribute}") { update! attribute => self.class.generate_unique_secure_token(length: length) }
before_create { send("#{attribute}=", self.class.generate_unique_secure_token(length: length)) unless send("#{attribute}?") }
end
def generate_unique_secure_token(length: MIN_LENGTH)
SecureRandom.base58(length)
end
end
end
end
The very first thing to note is that Rails will only load the securerandom
library when you actually use this macro. This library is an interface to secure random number generators.
When you call the has_secure_token
macro, two things happen:
- It dynamically creates the
regenerate_token
method using thedefine_method
in Ruby. All it does is update the existing token, setting a new value. - Adds a
before_create
callback that fires before the model is saved. It calls themodel.token=
method, generating and setting a secure and unique token.
Finally, the generate_unique_secure_token
method uses the SecureRandom
class to generate a random base58 string with the given length.
That's it.
I hope you liked this article and you learned something new.
As always, if you have any questions or feedback, didn't understand something, or found a mistake, please leave a comment below or send me an email. I look forward to hearing from you.
If you'd like to receive future articles directly in your email, please subscribe to my blog. If you're already a subscriber, thank you.