1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Allow token length configuration for has_secure_token method

The minimum token length is set to 24 due to security constraints. We
can now specify a longer length through the length: parameter. This is
especially useful for cases when your data storage is case-insensitive
and you want to increase your entropy.
This commit is contained in:
Bernardo Araujo 2019-04-09 15:40:16 -04:00
parent dd58d040b2
commit 51443e22f3
4 changed files with 47 additions and 9 deletions

View file

@ -1,3 +1,22 @@
* Allow length configuration for `has_secure_token` method. The minimum length
is set at 24 characters.
Before:
```ruby
has_secure_token :auth_token
```
After:
```ruby
has_secure_token :default_token # 24 characters
has_secure_token :auth_token, length: 36 # 36 characters
has_secure_token :invalid_token, length: 12 # => ActiveRecord::SecureToken::MinimumLengthError
```
*Bernardo de Araujo*
* Raise `ArgumentError` for invalid `:limit` and `:precision` like as other options.
Before:

View file

@ -2,6 +2,10 @@
module ActiveRecord
module SecureToken
class MinimumLengthError < StandardError; end
MINIMUM_TOKEN_LENGTH = 24
extend ActiveSupport::Concern
module ClassMethods
@ -10,30 +14,32 @@ module ActiveRecord
# # Schema: User(token:string, auth_token:string)
# class User < ActiveRecord::Base
# has_secure_token
# has_secure_token :auth_token
# has_secure_token :auth_token, length: 36
# end
#
# user = User.new
# user.save
# user.token # => "pX27zsMN2ViQKta1bGfLmVJE"
# user.auth_token # => "77TMHrHJFvFDwodq8w7Ev2m7"
# user.auth_token # => "tU9bLuZseefXQ4yQxQo8wjtBvsAfPc78os6R"
# user.regenerate_token # => true
# user.regenerate_auth_token # => true
#
# <tt>SecureRandom::base58</tt> is used to generate the 24-character unique token, so collisions are highly unlikely.
# <tt>SecureRandom::base58</tt> is used to generate at minimum a 24-character unique token, so collisions are highly unlikely.
#
# Note that it's still possible to generate a race condition in the database in the same way that
# {validates_uniqueness_of}[rdoc-ref:Validations::ClassMethods#validates_uniqueness_of] can.
# You're encouraged to add a unique index in the database to deal with this even more unlikely scenario.
def has_secure_token(attribute = :token)
def has_secure_token(attribute = :token, length: MINIMUM_TOKEN_LENGTH)
raise MinimumLengthError, "Token requires a minimum length of 24 characters." if length < MINIMUM_TOKEN_LENGTH
# Load securerandom only when has_secure_token is used.
require "active_support/core_ext/securerandom"
define_method("regenerate_#{attribute}") { update! attribute => self.class.generate_unique_secure_token }
before_create { send("#{attribute}=", self.class.generate_unique_secure_token) unless send("#{attribute}?") }
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
SecureRandom.base58(24)
def generate_unique_secure_token(length: MINIMUM_TOKEN_LENGTH)
SecureRandom.base58(length)
end
end
end

View file

@ -12,6 +12,8 @@ class SecureTokenTest < ActiveRecord::TestCase
@user.save
assert_not_nil @user.token
assert_not_nil @user.auth_token
assert_equal 24, @user.token.size
assert_equal 36, @user.auth_token.size
end
def test_regenerating_the_secure_token
@ -23,6 +25,9 @@ class SecureTokenTest < ActiveRecord::TestCase
assert_not_equal @user.token, old_token
assert_not_equal @user.auth_token, old_auth_token
assert_equal 24, @user.token.size
assert_equal 36, @user.auth_token.size
end
def test_token_value_not_overwritten_when_present
@ -31,4 +36,12 @@ class SecureTokenTest < ActiveRecord::TestCase
assert_equal "custom-secure-token", @user.token
end
def test_token_length_cannot_be_less_than_24_characters
assert_raises(ActiveRecord::SecureToken::MinimumLengthError) do
@user.class_eval do
has_secure_token :not_valid_token, length: 12
end
end
end
end

View file

@ -4,7 +4,7 @@ require "models/job"
class User < ActiveRecord::Base
has_secure_token
has_secure_token :auth_token
has_secure_token :auth_token, length: 36
has_and_belongs_to_many :jobs_pool,
class_name: "Job",