rodauth 2.9.0 → 2.10.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a51cd006e762abd197f16ec9b8337cd54bf15206fcc2ce3a83cb1c424bb83845
4
- data.tar.gz: 41e21158bee2b7ef8c75a93b035a5ff08c0245e5719aecbf783a818a860329b6
3
+ metadata.gz: 5286a570d72b6bc951ca489e12a696a4c2348d789d09f2782cfbd3521e63ee6d
4
+ data.tar.gz: be5d4e5bef18f6f62978bc84e07da1c4b31db4f03bf104d01abf8510812c5719
5
5
  SHA512:
6
- metadata.gz: fb2c83005f29f8e8f2ea36ae97111c2b2108dc370ce1fee624c6cd27fd96eaa70be8f1471d9b9cb4a8b652b55e2277cce3489514bf56624bceb2c4f52413b580
7
- data.tar.gz: 6bf8b23187ab6935cd33259b4ee546281df100641b7927c9e03e531f591a451ba5d56b50b8621cbc7948ad8807253dbded5da0b85247e70db7aa5db1d2be765a
6
+ metadata.gz: 98a849b965bfee4d80b8b6f6fc286d22f57673bf37da4e2cb338c114275795b665e46b561781f688dbb2ddfdd312e8b393f545d1920ac9f83024e042ff97cee0
7
+ data.tar.gz: 46da808a9d2e38c647339eed1554183b0e175fb3dbf4fd04d49d78015ff523bc0960091b3643bbc3913cfce88b7f78a240bd1857002de889a2d9455f106e4e03
data/CHANGELOG CHANGED
@@ -1,3 +1,9 @@
1
+ === 2.10.0 (2021-02-22)
2
+
3
+ * Add argon2 feature to allow use of the argon2 password hash algorithm instead of bcrypt (AlexeyMatskevich, jeremyevans) (#147)
4
+
5
+ * Avoid unnecessary previous password queries when using disallow_password_reuse feature with create_account or verify_account features (AlexeyMatskevich, jeremyevans) (#148)
6
+
1
7
  === 2.9.0 (2021-01-22)
2
8
 
3
9
  * Split jwt feature into json and jwt features, with the json feature using standard session support (janko, jeremyevans) (#145)
data/README.rdoc CHANGED
@@ -57,6 +57,7 @@ HTML and JSON API for all supported features.
57
57
  * JWT Refresh (Access & Refresh Token)
58
58
  * JWT CORS (Cross-Origin Resource Sharing)
59
59
  * Update Password Hash (when hash cost changes)
60
+ * Argon2
60
61
  * HTTP Basic Auth
61
62
  * Change Password Notify
62
63
 
@@ -80,8 +81,10 @@ rack_csrf :: Used for CSRF support if the :csrf=>:rack_csrf plugin
80
81
  option is given (the default is to use Roda's route_csrf
81
82
  plugin, as that allows for more secure request-specific
82
83
  tokens).
83
- bcrypt :: Used by default for password matching, can be skipped
84
+ bcrypt :: Used by default for password hashing, can be skipped
84
85
  if password_match? is overridden for custom authentication.
86
+ argon2 :: Used by the argon2 feature as alternative to bcrypt for
87
+ password hashing.
85
88
  mail :: Used by default for mailing in the reset password, verify
86
89
  account, verify_login_change, change_password_notify,
87
90
  lockout, and email_auth features.
@@ -106,7 +109,7 @@ correctly without it. There may be cases where you cannot use
106
109
  this feature, such as when using a different database or when you
107
110
  do not have full control over the database you are using.
108
111
 
109
- Passwords are hashed using bcrypt, and the password hashes are
112
+ Passwords are hashed using bcrypt by default, and the password hashes are
110
113
  kept in a separate table from the accounts table, with a foreign key
111
114
  referencing the accounts table. Two database functions are added,
112
115
  one to retrieve the salt for a password, and the other to check
@@ -333,7 +336,7 @@ things for the schema changes:
333
336
  foreign_key :id, Sequel[:${DATABASE_NAME}][:accounts], :primary_key=>true, :type=>:Bignum
334
337
  String :password_hash, :null=>false
335
338
  end
336
- Rodauth.create_database_authentication_functions(self, :table_name=>"${DATABASE_NAME}_password.account_password_hashes")
339
+ Rodauth.create_database_authentication_functions(self, :table_name=>Sequel[:${DATABASE_NAME}_password][:account_password_hashes])
337
340
 
338
341
  # if using the disallow_password_reuse feature:
339
342
  create_table(:account_previous_password_hashes) do
@@ -341,7 +344,7 @@ things for the schema changes:
341
344
  foreign_key :account_id, Sequel[:${DATABASE_NAME}][:accounts], :type=>:Bignum
342
345
  String :password_hash, :null=>false
343
346
  end
344
- Rodauth.create_database_previous_password_check_functions(self, :table_name=>"${DATABASE_NAME}_password.account_previous_password_hashes")
347
+ Rodauth.create_database_previous_password_check_functions(self, :table_name=>Sequel[:${DATABASE_NAME}_password][:account_previous_password_hashes])
345
348
 
346
349
  You'll also need to use the following Rodauth configuration methods so that the
347
350
  app account calls functions in a separate schema:
@@ -863,6 +866,7 @@ view the appropriate file in the doc directory.
863
866
  * {Account Expiration}[rdoc-ref:doc/account_expiration.rdoc]
864
867
  * {Active Sessions}[rdoc-ref:doc/active_sessions.rdoc]
865
868
  * {Audit Logging}[rdoc-ref:doc/audit_logging.rdoc]
869
+ * {Argon2}[rdoc-ref:doc/argon2.rdoc]
866
870
  * {Change Login}[rdoc-ref:doc/change_login.rdoc]
867
871
  * {Change Password}[rdoc-ref:doc/change_password.rdoc]
868
872
  * {Change Password Notify}[rdoc-ref:doc/change_password_notify.rdoc]
data/doc/argon2.rdoc ADDED
@@ -0,0 +1,49 @@
1
+ = Documentation for Argon2 Feature
2
+
3
+ The argon2 feature adds the ability to replace the bcrypt password hash
4
+ algorithm with argon2 (specifically, argon2id). Argon2 is an alternative to
5
+ bcrypt that offers the ability to be memory-hard. However, if you are storing
6
+ password hashes in a table that the database user does not have access to
7
+ (the recommended way to use Rodauth), argon2 does not offer significant
8
+ security advantages over bcrypt.
9
+
10
+ If you are using this feature with Rodauth's database authentication functions,
11
+ you need to make sure that the database authentication functions are configured
12
+ to support argon2 in addition to bcrypt. You can do this by passing the
13
+ +:argon2+ option when calling the method to define the database functions.
14
+ In this example, +DB+ should be your Sequel::Database object:
15
+
16
+ require 'rodauth/migrations'
17
+
18
+ # If the functions are already defined and you are not using PostgreSQL,
19
+ # you need to drop the existing functions.
20
+ Rodauth.drop_database_authentication_functions(DB)
21
+
22
+ # If you are using the disallow_password_reuse feature, also drop the
23
+ # database functions related to that if not using PostgreSQL:
24
+ Rodauth.drop_database_previous_password_check_functions(DB)
25
+
26
+ # Define new functions that support argon2:
27
+ Rodauth.create_database_authentication_functions(DB, argon2: true)
28
+
29
+ # If you are using the disallow_password_reuse feature, also define
30
+ # new functions that support argon2 for that:
31
+ Rodauth.create_database_previous_password_check_functions(DB, argon2: true)
32
+
33
+ The argon2 feature provides the ability to allow for a gradual migration
34
+ from transitioning from bcrypt to argon2 and vice-versa, if you are using the
35
+ update_password_hash.
36
+
37
+ Argon2 is more configurable than bcrypt in terms of password hash cost
38
+ speficiation. Instead of specifying the password_hash_cost value as
39
+ an integer, you must specify the password hash cost as a hash, such as
40
+ (<tt>{t_cost: 2, m_cost: 16}</tt>).
41
+
42
+ If you are using the argon2 feature and if you have no bcrypt passwords in
43
+ your database, you should use <tt>require_bcrypt? false</tt> in your
44
+ Rodauth configuration to prevent loading the bcrypt library, which will save
45
+ memory.
46
+
47
+ == Auth Value Methods
48
+
49
+ use_argon2? :: Whether to use the argon2 password hash algorithm for new passwords (true by default). The only reason to set this to false is if you have existing passwords using argon2 that you want to support, but want to use bcrypt for new passwords.
data/doc/base.rdoc CHANGED
@@ -15,7 +15,7 @@ domain :: The domain to use, required by some other features. It is recommended
15
15
  hmac_secret :: This sets the secret to use for all of Rodauth's HMACs. This is not set by default, in which case Rodauth does not use HMACs for additional security. However, it is highly recommended that you set this, and some features require it.
16
16
  mark_input_fields_as_required? :: Whether input fields should be marked as required, so browsers will not allow submission without filling out the field (default: true).
17
17
  prefix :: The routing prefix used for Rodauth routes. If you are calling in a routing subtree, this should be set to the root path of the subtree. This should include a leading slash if set, but not a trailing slash.
18
- require_bcrypt? :: Set to false to not require bcrypt, useful if using custom authentication.
18
+ require_bcrypt? :: Set to false to not require bcrypt, useful if using custom authentication or when using the argon2 feature without existing bcrypt password hashes.
19
19
  session_key :: The key in the session hash storing the primary key of the logged in account.
20
20
  session_key_prefix :: The string that will be prepended to the default value for all session keys.
21
21
  skip_status_checks? :: Whether status checks should be skipped for accounts. Defaults to true unless enabling the verify_account or close_account features.
@@ -0,0 +1,15 @@
1
+ = Migrate users passwords from bcrypt to argon2 or back
2
+
3
+ If you are currently using the default bcrypt password hash algorithm, and want to
4
+ gradually migrate to the argon2 password hash algorithm, you can use both the argon2
5
+ and update_password_hash features:
6
+
7
+ plugin :rodauth do
8
+ enable :login, :update_password_hash, :argon2
9
+ end
10
+
11
+ When a user with a current bcrypt password hash next successfully uses their
12
+ password, their password hash will be migrated to argon2.
13
+
14
+ If for some reason you want to migrate back from argon2 to bcrypt, you can set
15
+ <tt>use_argon2? false</tt> in your Rodauth configuration.
@@ -19,7 +19,7 @@ logins_do_not_match_message :: The error message to display when login and login
19
19
  password_confirm_label :: The label to use for password confirmations.
20
20
  password_confirm_param :: The parameter name to use for password confirmations.
21
21
  password_does_not_meet_requirements_message :: The error message to display when the password does not meet the requirements you have set.
22
- password_hash_cost :: The bcrypt cost to use for the password hash.
22
+ password_hash_cost :: The cost to use for the password hash algorithm. This should be an integer when using bcrypt (the default), and a hash if using argon2 (supported by the argon2 feature).
23
23
  password_minimum_length :: The minimum length for passwords, 6 by default.
24
24
  password_too_short_message :: The error message fragment to show if the password is too short.
25
25
  passwords_do_not_match_message :: The error message to display when password and password confirmation do not match.
@@ -0,0 +1,47 @@
1
+ = New Features
2
+
3
+ * An argon2 feature has been added that supports using the argon2
4
+ password hashing algorithm instead of the bcrypt password hashing
5
+ algorithm. While argon2 does not provide an advantage over bcrypt
6
+ if the attacker cannot access the password hashes directly (which
7
+ is how Rodauth is recommended to be used), in cases where attackers
8
+ can access the password hashes directly, argon2 is thought to be
9
+ more difficult or expensive to crack due to requiring more memory
10
+ (bcrypt is not a memory-hard password hash algorithm).
11
+
12
+ If you are using this feature with Rodauth's database authentication
13
+ functions, you need to make sure that the database authentication
14
+ functions are configured to support argon2 in addition to bcrypt.
15
+ You can do this by passing the :argon2 option when calling the
16
+ method to define the database functions. In this example, DB should
17
+ be your Sequel::Database object (this could be self if used in a
18
+ Sequel migration):
19
+
20
+ require 'rodauth/migrations'
21
+
22
+ # If the functions are already defined and you are not using PostgreSQL,
23
+ # you need to drop the existing functions.
24
+ Rodauth.drop_database_authentication_functions(DB)
25
+
26
+ # If you are using the disallow_password_reuse feature, also drop the
27
+ # database functions related to that if you are not using PostgreSQL:
28
+ Rodauth.drop_database_previous_password_check_functions(DB)
29
+
30
+ # Define new functions that support argon2:
31
+ Rodauth.create_database_authentication_functions(DB, argon2: true)
32
+
33
+ # If you are using the disallow_password_reuse feature, also define
34
+ # new functions that support argon2 for that:
35
+ Rodauth.create_database_previous_password_check_functions(DB, argon2: true)
36
+
37
+ You can transparently migrate bcrypt password hashes to argon2
38
+ password hashes whenever a user successfully uses their password
39
+ by using the argon2 feature in combination with the
40
+ update_password_hash feature.
41
+
42
+ = Other Improvements
43
+
44
+ * Unnecessary queries to determine whether the new password matches
45
+ a previous password are now skipped when using the create_account
46
+ or verify_account features with the disallow_password_reuse
47
+ feature.
@@ -0,0 +1,69 @@
1
+ # frozen-string-literal: true
2
+
3
+ require 'argon2'
4
+
5
+ # :nocov:
6
+ if !defined?(Argon2::VERSION) || Argon2::VERSION < '2'
7
+ raise LoadError, "argon2 version 1.x not supported as it does not support argon2id hashes"
8
+ end
9
+ # :nocov:
10
+
11
+ module Rodauth
12
+ Feature.define(:argon2, :Argon2) do
13
+ depends :login_password_requirements_base
14
+
15
+ auth_value_method :use_argon2?, true
16
+
17
+ private
18
+
19
+ def password_hash_cost
20
+ return super unless use_argon2?
21
+ argon2_hash_cost
22
+ end
23
+
24
+ def password_hash(password)
25
+ return super unless use_argon2?
26
+ ::Argon2::Password.new(password_hash_cost).create(password)
27
+ end
28
+
29
+ def password_hash_match?(hash, password)
30
+ return super unless argon2_hash_algorithm?(hash)
31
+ argon2_password_hash_match?(hash, password)
32
+ end
33
+
34
+ def password_hash_using_salt(password, salt)
35
+ return super unless argon2_hash_algorithm?(salt)
36
+
37
+ argon2_params = Hash[extract_password_hash_cost(salt)]
38
+ argon2_params[:salt_do_not_supply] = Base64.decode64(salt.split('$').last)
39
+ ::Argon2::Password.new(argon2_params).create(password)
40
+ end
41
+
42
+ def extract_password_hash_cost(hash)
43
+ return super unless argon2_hash_algorithm?(hash )
44
+
45
+ /\A\$argon2id\$v=\d+\$m=(\d+),t=(\d+)/ =~ hash
46
+ { t_cost: $2.to_i, m_cost: Math.log2($1.to_i).to_i }
47
+ end
48
+
49
+ if ENV['RACK_ENV'] == 'test'
50
+ def argon2_hash_cost
51
+ {t_cost: 1, m_cost: 3}
52
+ end
53
+ # :nocov:
54
+ else
55
+ def argon2_hash_cost
56
+ {t_cost: 2, m_cost: 16}
57
+ end
58
+ end
59
+ # :nocov:
60
+
61
+ def argon2_hash_algorithm?(hash)
62
+ hash.start_with?('$argon2id$')
63
+ end
64
+
65
+ def argon2_password_hash_match?(hash, password)
66
+ ::Argon2::Password.verify_password(password, hash)
67
+ end
68
+ end
69
+ end
@@ -465,7 +465,7 @@ module Rodauth
465
465
  end
466
466
 
467
467
  def database_function_password_match?(name, hash_id, password, salt)
468
- db.get(Sequel.function(function_name(name), hash_id, BCrypt::Engine.hash_secret(password, salt)))
468
+ db.get(Sequel.function(function_name(name), hash_id, password_hash_using_salt(password, salt)))
469
469
  end
470
470
 
471
471
  def password_hash_match?(hash, password)
@@ -593,6 +593,10 @@ module Rodauth
593
593
  @has_password = !!get_password_hash
594
594
  end
595
595
 
596
+ def password_hash_using_salt(password, salt)
597
+ BCrypt::Engine.hash_secret(password, salt)
598
+ end
599
+
596
600
  # Get the password hash for the user. When using database authentication functions,
597
601
  # note that only the salt is returned.
598
602
  def get_password_hash
@@ -24,13 +24,16 @@ module Rodauth
24
24
 
25
25
  def add_previous_password_hash(hash)
26
26
  ds = previous_password_ds
27
- keep_before = ds.reverse(previous_password_id_column).
28
- limit(nil, previous_passwords_to_check).
29
- get(previous_password_id_column)
30
27
 
31
- if keep_before
32
- ds.where(Sequel.expr(previous_password_id_column) <= keep_before).
33
- delete
28
+ unless @dont_check_previous_password
29
+ keep_before = ds.reverse(previous_password_id_column).
30
+ limit(nil, previous_passwords_to_check).
31
+ get(previous_password_id_column)
32
+
33
+ if keep_before
34
+ ds.where(Sequel.expr(previous_password_id_column) <= keep_before).
35
+ delete
36
+ end
34
37
  end
35
38
 
36
39
  # This should never raise uniqueness violations, as it uses a serial primary key
@@ -39,7 +42,7 @@ module Rodauth
39
42
 
40
43
  def password_meets_requirements?(password)
41
44
  super &&
42
- password_doesnt_match_previous_password?(password)
45
+ (@dont_check_previous_password || password_doesnt_match_previous_password?(password))
43
46
  end
44
47
 
45
48
  private
@@ -71,6 +74,16 @@ module Rodauth
71
74
  previous_password_ds.delete
72
75
  end
73
76
 
77
+ def before_create_account_route
78
+ super if defined?(super)
79
+ @dont_check_previous_password = true
80
+ end
81
+
82
+ def before_verify_account_route
83
+ super if defined?(super)
84
+ @dont_check_previous_password = true
85
+ end
86
+
74
87
  def after_create_account
75
88
  if account_password_hash_column && !(respond_to?(:verify_account_set_password?) && verify_account_set_password?)
76
89
  add_previous_password_hash(password_hash(param(password_param)))
@@ -140,6 +140,10 @@ module Rodauth
140
140
  # :nocov:
141
141
  end
142
142
 
143
+ def extract_password_hash_cost(hash)
144
+ hash[4, 2].to_i
145
+ end
146
+
143
147
  def password_hash(password)
144
148
  BCrypt::Password.create(password, :cost=>password_hash_cost)
145
149
  end
@@ -20,7 +20,7 @@ module Rodauth
20
20
 
21
21
  def get_password_hash
22
22
  if hash = super
23
- @current_password_hash_cost = hash.split('$')[2].to_i
23
+ @current_password_hash_cost = extract_password_hash_cost(hash)
24
24
  end
25
25
 
26
26
  hash
@@ -4,7 +4,8 @@ module Rodauth
4
4
  def self.create_database_authentication_functions(db, opts={})
5
5
  table_name = opts[:table_name] || :account_password_hashes
6
6
  get_salt_name = opts[:get_salt_name] || :rodauth_get_salt
7
- valid_hash_name = opts[:valid_hash_name] || :rodauth_valid_password_hash
7
+ valid_hash_name = opts[:valid_hash_name] || :rodauth_valid_password_hash
8
+ argon2 = opts[:argon2]
8
9
 
9
10
  case db.database_type
10
11
  when :postgres
@@ -14,12 +15,21 @@ module Rodauth
14
15
  when 'uuid' then :uuid
15
16
  else :int8
16
17
  end
18
+ table_name = db.literal(table_name) unless table_name.is_a?(String)
17
19
 
20
+ argon_sql = <<END
21
+ CASE
22
+ WHEN password_hash ~ '^\\$argon2id'
23
+ THEN substring(password_hash from '\\$argon2id\\$v=\\d+\\$m=\\d+,t=\\d+,p=\\d+\\$.+\\$')
24
+ ELSE substr(password_hash, 0, 30)
25
+ END INTO salt
26
+ END
18
27
  db.run <<END
19
28
  CREATE OR REPLACE FUNCTION #{get_salt_name}(acct_id #{primary_key_type}) RETURNS text AS $$
20
29
  DECLARE salt text;
21
30
  BEGIN
22
- SELECT substr(password_hash, 0, 30) INTO salt
31
+ SELECT
32
+ #{argon2 ? argon_sql : "substr(password_hash, 0, 30) INTO salt"}
23
33
  FROM #{table_name}
24
34
  WHERE acct_id = id;
25
35
  RETURN salt;
@@ -43,12 +53,20 @@ SECURITY DEFINER
43
53
  SET search_path = #{search_path};
44
54
  END
45
55
  when :mysql
56
+ argon_sql = <<END
57
+ CASE
58
+ WHEN password_hash REGEXP '^.argon2id'
59
+ THEN left(password_hash, CHAR_LENGTH(password_hash) - INSTR(REVERSE(password_hash), '$'))
60
+ ELSE substr(password_hash, 1, 30)
61
+ END
62
+ END
46
63
  db.run <<END
47
64
  CREATE FUNCTION #{get_salt_name}(acct_id int8) RETURNS varchar(255)
48
65
  SQL SECURITY DEFINER
49
66
  READS SQL DATA
50
67
  BEGIN
51
- RETURN (SELECT substr(password_hash, 1, 30)
68
+ RETURN (SELECT
69
+ #{argon2 ? argon_sql : "substr(password_hash, 1, 30)"}
52
70
  FROM #{table_name}
53
71
  WHERE acct_id = id);
54
72
  END;
@@ -71,13 +89,21 @@ RETURN valid;
71
89
  END;
72
90
  END
73
91
  when :mssql
92
+ argon_sql = <<END
93
+ CASE
94
+ WHEN password_hash LIKE '[$]argon2id%'
95
+ THEN left(password_hash, len(password_hash) - charindex('$', reverse(password_hash)))
96
+ ELSE substring(password_hash, 0, 30)
97
+ END
98
+ END
74
99
  db.run <<END
75
100
  CREATE FUNCTION #{get_salt_name}(@account_id bigint) RETURNS nvarchar(255)
76
101
  WITH EXECUTE AS OWNER
77
102
  AS
78
103
  BEGIN
79
104
  DECLARE @salt nvarchar(255);
80
- SELECT @salt = substring(password_hash, 0, 30)
105
+ SELECT @salt =
106
+ #{argon2 ? argon_sql : "substring(password_hash, 0, 30)"}
81
107
  FROM #{table_name}
82
108
  WHERE id = @account_id;
83
109
  RETURN @salt;
@@ -107,7 +133,7 @@ END
107
133
  def self.drop_database_authentication_functions(db, opts={})
108
134
  table_name = opts[:table_name] || :account_password_hashes
109
135
  get_salt_name = opts[:get_salt_name] || :rodauth_get_salt
110
- valid_hash_name = opts[:valid_hash_name] || :rodauth_valid_password_hash
136
+ valid_hash_name = opts[:valid_hash_name] || :rodauth_valid_password_hash
111
137
 
112
138
  case db.database_type
113
139
  when :postgres
@@ -6,7 +6,7 @@ module Rodauth
6
6
  MAJOR = 2
7
7
 
8
8
  # The minor version of Rodauth, updated for new feature releases of Rodauth.
9
- MINOR = 9
9
+ MINOR = 10
10
10
 
11
11
  # The patch version of Rodauth, updated only for bug fixes from the last
12
12
  # feature release.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rodauth
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.9.0
4
+ version: 2.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-01-23 00:00:00.000000000 Z
11
+ date: 2021-02-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sequel
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: argon2
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '2'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '2'
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: mail
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -140,14 +154,14 @@ dependencies:
140
154
  name: webauthn
141
155
  requirement: !ruby/object:Gem::Requirement
142
156
  requirements:
143
- - - ">"
157
+ - - ">="
144
158
  - !ruby/object:Gem::Version
145
159
  version: '2'
146
160
  type: :development
147
161
  prerelease: false
148
162
  version_requirements: !ruby/object:Gem::Requirement
149
163
  requirements:
150
- - - ">"
164
+ - - ">="
151
165
  - !ruby/object:Gem::Version
152
166
  version: '2'
153
167
  - !ruby/object:Gem::Dependency
@@ -239,6 +253,7 @@ extra_rdoc_files:
239
253
  - MIT-LICENSE
240
254
  - doc/account_expiration.rdoc
241
255
  - doc/active_sessions.rdoc
256
+ - doc/argon2.rdoc
242
257
  - doc/audit_logging.rdoc
243
258
  - doc/base.rdoc
244
259
  - doc/change_login.rdoc
@@ -305,6 +320,7 @@ extra_rdoc_files:
305
320
  - doc/release_notes/1.9.0.txt
306
321
  - doc/release_notes/2.0.0.txt
307
322
  - doc/release_notes/2.1.0.txt
323
+ - doc/release_notes/2.10.0.txt
308
324
  - doc/release_notes/2.2.0.txt
309
325
  - doc/release_notes/2.3.0.txt
310
326
  - doc/release_notes/2.4.0.txt
@@ -320,6 +336,7 @@ files:
320
336
  - dict/top-10_000-passwords.txt
321
337
  - doc/account_expiration.rdoc
322
338
  - doc/active_sessions.rdoc
339
+ - doc/argon2.rdoc
323
340
  - doc/audit_logging.rdoc
324
341
  - doc/base.rdoc
325
342
  - doc/change_login.rdoc
@@ -342,6 +359,7 @@ files:
342
359
  - doc/guides/internals.rdoc
343
360
  - doc/guides/links.rdoc
344
361
  - doc/guides/login_return.rdoc
362
+ - doc/guides/migrate_password_hash_algorithm.rdoc
345
363
  - doc/guides/password_column.rdoc
346
364
  - doc/guides/password_confirmation.rdoc
347
365
  - doc/guides/password_requirements.rdoc
@@ -394,6 +412,7 @@ files:
394
412
  - doc/release_notes/1.9.0.txt
395
413
  - doc/release_notes/2.0.0.txt
396
414
  - doc/release_notes/2.1.0.txt
415
+ - doc/release_notes/2.10.0.txt
397
416
  - doc/release_notes/2.2.0.txt
398
417
  - doc/release_notes/2.3.0.txt
399
418
  - doc/release_notes/2.4.0.txt
@@ -421,6 +440,7 @@ files:
421
440
  - lib/rodauth.rb
422
441
  - lib/rodauth/features/account_expiration.rb
423
442
  - lib/rodauth/features/active_sessions.rb
443
+ - lib/rodauth/features/argon2.rb
424
444
  - lib/rodauth/features/audit_logging.rb
425
445
  - lib/rodauth/features/base.rb
426
446
  - lib/rodauth/features/change_login.rb