Back to Blog

has_secure_token for unique random token in Rails 5

on March 23, 2016
This blog is part of our Rails 5 series.

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

1class User < ActiveRecord::Base
2
3  before_create :set_access_token
4
5  private
6
7  def set_access_token
8    self.access_token = generate_token
9  end
10
11  def generate_token
12    loop do
13      token = SecureRandom.hex(10)
14      break token unless User.where(access_token: token).exists?
15    end
16  end
17end

has_secure_token in Rails 5

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

1
2class User < ApplicationRecord
3  has_secure_token
4end
5

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.

1
2class User < ApplicationRecord
3  has_secure_token :password_reset_token
4end
5

The above code assumes that we already have password_reset_token attribute in our model.

1
2>> user = User.new
3>> user.save
4=> true
5
6>> user.password_reset_token
7=> 'qjCbex522DfVEVd5ysUWppWQ'
8

The generated tokens are URL safe and are of fixed length strings.

Migration helper for generating token

We can also generate migration for token similar to other data types.

1$ rails g migration add_auth_token_to_user auth_token:token
1class AddAuthTokenToUser < ActiveRecord::Migration[5.0]
2  def change
3    add_column :users, :auth_token, :string
4    add_index :users, :auth_token, unique: true
5  end
6end

Notice that migration automatically adds index on the generated column with unique constraint.

We can also generate a model with the token attribute.

1$ rails g model Product access_token:token
1class CreateProducts < ActiveRecord::Migration[5.0]
2  def change
3    create_table :products do |t|
4      t.string :access_token
5
6      t.timestamps
7    end
8    add_index :products, :access_token, unique: true
9  end
10end
11

Model generator also adds has_secure_token method to the model.

1
2class Product < ApplicationRecord
3  has_secure_token :access_token
4end
5

Regenerating tokens

Sometimes we need to regenerate the tokens based on some expiration criteria.

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

1
2>> user = User.first
3=> <User id: 11, name: 'John', email: 'john@example.com',
4         token: "jRMcN645BQyDr67yHR3qjsJF",
5         password_reset_token: "qjCbex522DfVEVd5ysUWppWQ">
6
7>> user.password_reset_token
8=> "qjCbex522DfVEVd5ysUWppWQ"
9
10>> user.regenerate_password_reset_token
11=> true
12
13>> user.password_reset_token
14=> "tYYVjnCEd1LAXvmLCyyQFzbm"
15

Beware of race condition

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.


You might also like

If you liked this blog post, check out similar ones from BigBinary