Posted over 5 years ago. Visible to the public.

Generate unique random token.

We sometimes need unique and random tokens in our web apps. Here is how we typically build it.

Copy
class User < ActiveRecord::Base before_create :set_access_token private def set_access_token self.access_token = generate_token end def generate_token loop do token = SecureRandom.hex(10) break token unless User.where(access_token: token).exists? end end end

Rails 5 has added has_secure_token method to generate a random alphanumeric token for a given column.

Copy
class User < ApplicationRecord has_secure_token end

By default, Rails assumes that the attribute name is token. We can provide a different name as a parameter to has_secure_token if the attribute name is not token.

Copy
class User < ApplicationRecord has_secure_token :password_reset_token end >> user = User.new >> user.save => true >> user.password_reset_token => 'qjCbex522DfVEVd5ysUWppWQ'

The generated tokens are URL safe and are of fixed length strings. We can also generate migration for token similar to other data types.

Copy
rails g migration add_auth_token_to_user auth_token:token class AddAuthTokenToUser < ActiveRecord::Migration[5.0] def change add_column :users, :auth_token, :string add_index :users, :auth_token, unique: true end end

Notice that migration automatically adds index on the generated column with unique constraint. We can also generate a model with the token attribute.

Copy
rails g model Product access_token:token class CreateProducts < ActiveRecord::Migration[5.0] def change create_table :products do |t| t.string :access_token t.timestamps end add_index :products, :access_token, unique: true end end

Model generator also adds has_secure_token method to the model.

Copy
class Product < ApplicationRecord has_secure_token :access_token end

In order to regenerate tokens, we can simply call regenerate_#{token_attribute_name} which would regenerate the token and save it to its respective attribute.

Copy
>> user = User.first => <User id: 11, name: 'John', email: 'john@example.com', token: "jRMcN645BQyDr67yHR3qjsJF", password_reset_token: "qjCbex522DfVEVd5ysUWppWQ"> >> user.password_reset_token => "qjCbex522DfVEVd5ysUWppWQ" >> user.regenerate_password_reset_token => true >> user.password_reset_token => "tYYVjnCEd1LAXvmLCyyQFzbm"

It is possible to generate a race condition in the database while generating the tokens. So it is advisable to add a unique index in the database to deal with this unlikely scenario.

Owner of this card:

Avatar
Alexander M
Last edit:
over 5 years ago
by Alexander M
Tags:
Rails-5-0
Posted by Alexander M to Ruby and RoR knowledge base
This website uses short-lived cookies to improve usability.
Accept or learn more