rodauth 1.19.1 → 1.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +72 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +100 -7
  5. data/doc/base.rdoc +25 -0
  6. data/doc/email_auth.rdoc +1 -1
  7. data/doc/email_base.rdoc +5 -1
  8. data/doc/internals.rdoc +2 -2
  9. data/doc/jwt_refresh.rdoc +35 -0
  10. data/doc/lockout.rdoc +3 -0
  11. data/doc/login_password_requirements_base.rdoc +4 -1
  12. data/doc/otp.rdoc +22 -39
  13. data/doc/recovery_codes.rdoc +15 -28
  14. data/doc/release_notes/1.20.0.txt +175 -0
  15. data/doc/remember.rdoc +3 -0
  16. data/doc/reset_password.rdoc +2 -1
  17. data/doc/single_session.rdoc +3 -0
  18. data/doc/verify_account.rdoc +4 -3
  19. data/doc/verify_login_change.rdoc +1 -1
  20. data/lib/rodauth.rb +33 -4
  21. data/lib/rodauth/features/base.rb +93 -10
  22. data/lib/rodauth/features/change_login.rb +1 -1
  23. data/lib/rodauth/features/confirm_password.rb +1 -1
  24. data/lib/rodauth/features/create_account.rb +2 -2
  25. data/lib/rodauth/features/disallow_password_reuse.rb +5 -3
  26. data/lib/rodauth/features/email_auth.rb +4 -2
  27. data/lib/rodauth/features/email_base.rb +12 -6
  28. data/lib/rodauth/features/jwt.rb +9 -0
  29. data/lib/rodauth/features/jwt_refresh.rb +142 -0
  30. data/lib/rodauth/features/lockout.rb +8 -4
  31. data/lib/rodauth/features/login_password_requirements_base.rb +1 -0
  32. data/lib/rodauth/features/otp.rb +63 -6
  33. data/lib/rodauth/features/recovery_codes.rb +1 -0
  34. data/lib/rodauth/features/remember.rb +20 -2
  35. data/lib/rodauth/features/reset_password.rb +5 -2
  36. data/lib/rodauth/features/single_session.rb +15 -2
  37. data/lib/rodauth/features/verify_account.rb +11 -6
  38. data/lib/rodauth/features/verify_login_change.rb +5 -3
  39. data/lib/rodauth/version.rb +2 -2
  40. data/spec/disallow_password_reuse_spec.rb +115 -28
  41. data/spec/email_auth_spec.rb +2 -2
  42. data/spec/jwt_refresh_spec.rb +256 -0
  43. data/spec/lockout_spec.rb +4 -4
  44. data/spec/login_spec.rb +52 -11
  45. data/spec/migrate/001_tables.rb +10 -0
  46. data/spec/migrate_travis/001_tables.rb +8 -0
  47. data/spec/remember_spec.rb +27 -0
  48. data/spec/reset_password_spec.rb +2 -2
  49. data/spec/rodauth_spec.rb +25 -1
  50. data/spec/single_session_spec.rb +20 -0
  51. data/spec/spec_helper.rb +29 -0
  52. data/spec/two_factor_spec.rb +57 -3
  53. data/spec/verify_account_spec.rb +18 -1
  54. data/spec/verify_login_change_spec.rb +2 -2
  55. data/templates/add-recovery-codes.str +1 -1
  56. data/templates/change-password.str +2 -2
  57. data/templates/login-confirm-field.str +2 -2
  58. data/templates/login-field.str +2 -2
  59. data/templates/otp-auth-code-field.str +2 -2
  60. data/templates/otp-setup.str +4 -3
  61. data/templates/password-confirm-field.str +2 -2
  62. data/templates/password-field.str +2 -2
  63. data/templates/recovery-auth.str +2 -2
  64. data/templates/reset-password-request.str +1 -1
  65. data/templates/sms-code-field.str +2 -2
  66. data/templates/sms-setup.str +2 -2
  67. data/templates/unlock-account-request.str +1 -1
  68. data/templates/unlock-account.str +1 -1
  69. data/templates/verify-account-resend.str +1 -1
  70. metadata +15 -5
@@ -3,10 +3,14 @@ require File.expand_path("spec_helper", File.dirname(__FILE__))
3
3
  describe 'Rodauth verify_account feature' do
4
4
  it "should support verifying accounts" do
5
5
  last_sent_column = nil
6
+ secret = nil
7
+ allow_raw_token = false
6
8
  rodauth do
7
9
  enable :login, :create_account, :verify_account
8
10
  verify_account_autologin? false
9
11
  verify_account_email_last_sent_column{last_sent_column}
12
+ hmac_secret{secret}
13
+ allow_raw_email_token?{allow_raw_token}
10
14
  end
11
15
  roda do |r|
12
16
  r.rodauth
@@ -73,8 +77,13 @@ describe 'Rodauth verify_account feature' do
73
77
 
74
78
  link = email_link(/(\/verify-account\?key=.+)$/, 'foo@example2.com')
75
79
  visit link[0...-1]
76
- page.find('#error_flash').text.must_equal "invalid verify account key"
80
+ page.find('#error_flash').text.must_equal "There was an error verifying your account: invalid verify account key"
77
81
 
82
+ secret = SecureRandom.random_bytes(32)
83
+ visit link
84
+ page.find('#error_flash').text.must_equal "There was an error verifying your account: invalid verify account key"
85
+
86
+ allow_raw_token = true
78
87
  visit link
79
88
  click_button 'Verify Account'
80
89
  page.find('#notice_flash').text.must_equal "Your account has been verified"
@@ -87,11 +96,13 @@ describe 'Rodauth verify_account feature' do
87
96
 
88
97
  [false, true].each do |ph|
89
98
  it "should support setting passwords when verifying accounts #{'with account_password_hash_column' if ph}" do
99
+ initial_secret = secret = SecureRandom.random_bytes(32)
90
100
  rodauth do
91
101
  enable :login, :create_account, :verify_account
92
102
  account_password_hash_column :ph if ph
93
103
  verify_account_autologin? false
94
104
  verify_account_set_password? true
105
+ hmac_secret{secret}
95
106
  end
96
107
  roda do |r|
97
108
  r.rodauth
@@ -105,6 +116,12 @@ describe 'Rodauth verify_account feature' do
105
116
  page.find('#notice_flash').text.must_equal "An email has been sent to you with a link to verify your account"
106
117
 
107
118
  link = email_link(/(\/verify-account\?key=.+)$/, 'foo@example2.com')
119
+
120
+ secret = SecureRandom.random_bytes(32)
121
+ visit link
122
+ page.find('#error_flash').text.must_equal "There was an error verifying your account: invalid verify account key"
123
+
124
+ secret = initial_secret
108
125
  visit link
109
126
  fill_in 'Password', :with=>'0123456789'
110
127
  fill_in 'Confirm Password', :with=>'012345678'
@@ -36,7 +36,7 @@ describe 'Rodauth verify_login_change feature' do
36
36
  logout
37
37
 
38
38
  visit link
39
- page.find('#error_flash').text.must_equal "invalid verify login change key"
39
+ page.find('#error_flash').text.must_equal "There was an error verifying your login change: invalid verify login change key"
40
40
 
41
41
  visit new_link
42
42
  page.title.must_equal 'Verify Login Change'
@@ -128,7 +128,7 @@ describe 'Rodauth verify_login_change feature' do
128
128
  page.current_path.must_equal '/login'
129
129
 
130
130
  visit link
131
- page.find('#error_flash').text.must_equal "invalid verify login change key"
131
+ page.find('#error_flash').text.must_equal "There was an error verifying your login change: invalid verify login change key"
132
132
  end
133
133
 
134
134
  it "should handle uniqueness errors raised when inserting verify login change entry" do
@@ -1,2 +1,2 @@
1
1
  <pre id="recovery-codes">#{rodauth.recovery_codes.map{|s| h s}.join("\n\n")}</pre>
2
- #{"<h2>Add Additional Recovery Codes</h2>#{rodauth.render('recovery-codes')}" if rodauth.can_add_recovery_codes?}
2
+ #{"#{rodauth.add_recovery_codes_heading}#{rodauth.render('recovery-codes')}" if rodauth.can_add_recovery_codes?}
@@ -3,9 +3,9 @@
3
3
  #{rodauth.csrf_tag}
4
4
  #{rodauth.render('password-field') if rodauth.change_password_requires_password?}
5
5
  <div class="form-group">
6
- <label class="col-sm-2 control-label" for="new-password">#{rodauth.new_password_label}</label>
6
+ <label class="col-sm-2 control-label" for="new-password">#{rodauth.new_password_label}#{rodauth.input_field_label_suffix}</label>
7
7
  <div class="col-sm-10">
8
- <input type="password" class="form-control#{' error' if rodauth.field_error(rodauth.new_password_param)}" name="#{rodauth.new_password_param}" id="new-password"/> #{rodauth.field_error(rodauth.new_password_param)}
8
+ #{rodauth.input_field_string(rodauth.new_password_param, 'new-password', :type => 'password')}
9
9
  </div>
10
10
  </div>
11
11
  #{rodauth.render('password-confirm-field') if rodauth.require_password_confirmation?}
@@ -1,6 +1,6 @@
1
1
  <div class="form-group">
2
- <label class="col-sm-2 control-label" for="login-confirm">#{rodauth.login_confirm_label}</label>
2
+ <label class="col-sm-2 control-label" for="login-confirm">#{rodauth.login_confirm_label}#{rodauth.input_field_label_suffix}</label>
3
3
  <div class="col-sm-10">
4
- <input type="text" class="form-control" name="login-confirm" id="#{rodauth.login_confirm_param}" value="#{h rodauth.param(rodauth.login_confirm_param)}"/>
4
+ #{rodauth.input_field_string(rodauth.login_confirm_param, 'login-confirm', :type=>rodauth.login_input_type)}
5
5
  </div>
6
6
  </div>
@@ -1,6 +1,6 @@
1
1
  <div class="form-group">
2
- <label class="col-sm-2 control-label" for="login">#{rodauth.login_label}</label>
2
+ <label class="col-sm-2 control-label" for="login">#{rodauth.login_label}#{rodauth.input_field_label_suffix}</label>
3
3
  <div class="col-sm-10">
4
- <input type="text" class="form-control#{' error' if rodauth.field_error(rodauth.login_param)}" name="#{rodauth.login_param}" id="login" value="#{h rodauth.param(rodauth.login_param)}"/> #{rodauth.field_error(rodauth.login_param)}
4
+ #{rodauth.input_field_string(rodauth.login_param, 'login', :type=>rodauth.login_input_type)}
5
5
  </div>
6
6
  </div>
@@ -1,6 +1,6 @@
1
1
  <div class="form-group">
2
- <label class="col-sm-4 control-label" for="otp-auth-code">#{rodauth.otp_auth_label}</label>
2
+ <label class="col-sm-4 control-label" for="otp-auth-code">#{rodauth.otp_auth_label}#{rodauth.input_field_label_suffix}</label>
3
3
  <div class="col-sm-3">
4
- <input type="text" class="form-control#{' error' if rodauth.field_error(rodauth.otp_auth_param)}" name="#{rodauth.otp_auth_param}" id="otp-auth-code" value=""/> #{rodauth.field_error(rodauth.otp_auth_param)}
4
+ #{rodauth.input_field_string(rodauth.otp_auth_param, 'otp-auth-code', :value=>'', :attr=>'autocomplete="off"' )}
5
5
  </div>
6
6
  </div>
@@ -1,10 +1,11 @@
1
1
  <form method="post" class="rodauth form-horizontal" role="form" id="otp-setup-form">
2
2
  #{rodauth.otp_setup_additional_form_tags}
3
- <input type="hidden" id="otp-key" name="#{rodauth.otp_setup_param}" value="#{rodauth.otp_key}" />
3
+ <input type="hidden" id="otp-key" name="#{rodauth.otp_setup_param}" value="#{rodauth.otp_user_key}" />
4
+ #{"<input type=\"hidden\" id=\"otp-hmac-secret\" name=\"#{rodauth.otp_setup_raw_param}\" value=\"#{rodauth.otp_key}\" />" if rodauth.otp_keys_use_hmac?}
4
5
  #{rodauth.csrf_tag}
5
6
  <div class="form-group">
6
- <p>Secret: #{rodauth.otp_key}</p>
7
- <p>Provisioning URL: #{rodauth.otp_provisioning_uri}</p>
7
+ <p>#{rodauth.otp_secret_label}: #{rodauth.otp_user_key}</p>
8
+ <p>#{rodauth.otp_provisioning_uri_label}: #{rodauth.otp_provisioning_uri}</p>
8
9
  </div>
9
10
 
10
11
  <div class="row">
@@ -1,6 +1,6 @@
1
1
  <div class="form-group">
2
- <label class="col-sm-2 control-label" for="password-confirm">#{rodauth.password_confirm_label}</label>
2
+ <label class="col-sm-2 control-label" for="password-confirm">#{rodauth.password_confirm_label}#{rodauth.input_field_label_suffix}</label>
3
3
  <div class="col-sm-10">
4
- <input type="password" class="form-control" name="#{rodauth.password_confirm_param}" id="password-confirm"/>
4
+ #{rodauth.input_field_string(rodauth.password_confirm_param, 'password-confirm', :type => 'password')}
5
5
  </div>
6
6
  </div>
@@ -1,6 +1,6 @@
1
1
  <div class="form-group">
2
- <label class="col-sm-2 control-label" for="password">#{rodauth.password_label}</label>
2
+ <label class="col-sm-2 control-label" for="password">#{rodauth.password_label}#{rodauth.input_field_label_suffix}</label>
3
3
  <div class="col-sm-10">
4
- <input type="password" class="form-control#{' error' if rodauth.field_error(rodauth.password_param)}" name="#{rodauth.password_param}" id="password"/> #{rodauth.field_error(rodauth.password_param)}
4
+ #{rodauth.input_field_string(rodauth.password_param, 'password', :type => 'password')}
5
5
  </div>
6
6
  </div>
@@ -2,9 +2,9 @@
2
2
  #{rodauth.recovery_auth_additional_form_tags}
3
3
  #{rodauth.csrf_tag}
4
4
  <div class="form-group">
5
- <label class="col-sm-2 control-label" for="recovery_code">#{rodauth.recovery_codes_label}</label>
5
+ <label class="col-sm-2 control-label" for="recovery_code">#{rodauth.recovery_codes_label}#{rodauth.input_field_label_suffix}</label>
6
6
  <div class="col-sm-10">
7
- <input type="text" class="form-control#{' error' if rodauth.field_error(rodauth.recovery_codes_param)}" name="#{rodauth.recovery_codes_param}" id="recovery_code" value=""/> #{rodauth.field_error(rodauth.recovery_codes_param)}
7
+ #{rodauth.input_field_string(rodauth.recovery_codes_param, 'recovery_code', :value => '')}
8
8
  </div>
9
9
  </div>
10
10
  #{rodauth.button(rodauth.recovery_auth_button)}
@@ -1,7 +1,7 @@
1
1
  <form action="#{rodauth.prefix}/#{rodauth.reset_password_request_route}" method="post" class="rodauth form-horizontal" role="form" id="reset-password-request-form">
2
2
  #{rodauth.reset_password_request_additional_form_tags}
3
3
  #{rodauth.csrf_tag("#{rodauth.prefix}/#{rodauth.reset_password_request_route}")}
4
- <p>If you have forgotten your password, you can request a password reset: </p>
4
+ #{rodauth.reset_password_explanatory_text}
5
5
  #{(login = rodauth.param_or_nil(rodauth.login_param)) ? "<input type=\"hidden\" name=\"#{rodauth.login_param}\" value=\"#{h login}\"/>" : rodauth.render('login-field')}
6
6
  #{rodauth.button(rodauth.reset_password_request_button)}
7
7
  </form>
@@ -1,6 +1,6 @@
1
1
  <div class="form-group">
2
- <label class="col-sm-3 control-label" for="sms-code">#{rodauth.sms_code_label}</label>
2
+ <label class="col-sm-3 control-label" for="sms-code">#{rodauth.sms_code_label}#{rodauth.input_field_label_suffix}</label>
3
3
  <div class="col-sm-3">
4
- <input type="text" class="form-control#{' error' if rodauth.field_error(rodauth.sms_code_param)}" name="#{rodauth.sms_code_param}" id="sms-code" value=""/> #{rodauth.field_error(rodauth.sms_code_param)}
4
+ #{rodauth.input_field_string(rodauth.sms_code_param, 'sms-code', :value => '')}
5
5
  </div>
6
6
  </div>
@@ -3,9 +3,9 @@
3
3
  #{rodauth.csrf_tag}
4
4
  #{rodauth.render('password-field') if rodauth.two_factor_modifications_require_password?}
5
5
  <div class="form-group">
6
- <label class="col-sm-2 control-label" for="sms-phone">#{rodauth.sms_phone_label}</label>
6
+ <label class="col-sm-2 control-label" for="sms-phone">#{rodauth.sms_phone_label}#{rodauth.input_field_label_suffix}</label>
7
7
  <div class="col-sm-3">
8
- <input type="text" class="form-control#{' error' if rodauth.field_error(rodauth.sms_phone_param)}" name="#{rodauth.sms_phone_param}" id="sms-phone" value="#{h rodauth.param(rodauth.sms_phone_param)}"/> #{rodauth.field_error(rodauth.sms_phone_param)}
8
+ #{rodauth.input_field_string(rodauth.sms_phone_param, 'sms-phone')}
9
9
  </div>
10
10
  </div>
11
11
  #{rodauth.button(rodauth.sms_setup_button)}
@@ -2,6 +2,6 @@
2
2
  #{rodauth.unlock_account_request_additional_form_tags}
3
3
  #{rodauth.csrf_tag("#{rodauth.prefix}/#{rodauth.unlock_account_request_route}")}
4
4
  <input type="hidden" name="#{rodauth.login_param}" value="#{h rodauth.param(rodauth.login_param)}"/>
5
- This account is currently locked out. You can request that the account be unlocked:
5
+ #{rodauth.unlock_account_request_explanatory_text}
6
6
  <input type="submit" class="btn btn-primary inline" value="#{rodauth.unlock_account_request_button}"/>
7
7
  </form>
@@ -1,7 +1,7 @@
1
1
  <form method="post" class="rodauth form-horizontal" role="form" id="unlock-account-form">
2
2
  #{rodauth.unlock_account_additional_form_tags}
3
3
  #{rodauth.csrf_tag}
4
- <p>This account is currently locked out. You can unlock the account.</p>
4
+ #{rodauth.unlock_account_explanatory_text}
5
5
  #{rodauth.render('password-field') if rodauth.unlock_account_requires_password?}
6
6
  #{rodauth.button(rodauth.unlock_account_button)}
7
7
  </form>
@@ -1,7 +1,7 @@
1
1
  <form action="#{rodauth.prefix}/#{rodauth.verify_account_resend_route}" method="post" class="rodauth form-horizontal" role="form" id="verify-account-resend-form">
2
2
  #{rodauth.verify_account_resend_additional_form_tags}
3
3
  #{rodauth.csrf_tag("#{rodauth.prefix}/#{rodauth.verify_account_resend_route}")}
4
- <p>If you no longer have the email to verify the account, you can request that it be resent to you:</p>
4
+ #{rodauth.verify_account_resend_explanatory_text}
5
5
  #{(login = rodauth.param_or_nil(rodauth.login_param)) ? "<input type=\"hidden\" name=\"#{rodauth.login_param}\" value=\"#{h login}\"/>" : rodauth.render('login-field')}
6
6
  #{rodauth.button(rodauth.verify_account_resend_button)}
7
7
  </form>
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: 1.19.1
4
+ version: 1.20.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: 2018-11-16 00:00:00.000000000 Z
11
+ date: 2019-06-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sequel
@@ -229,6 +229,7 @@ extra_rdoc_files:
229
229
  - doc/update_password_hash.rdoc
230
230
  - doc/verify_account.rdoc
231
231
  - doc/email_auth.rdoc
232
+ - doc/jwt_refresh.rdoc
232
233
  - doc/verify_account_grace_period.rdoc
233
234
  - doc/verify_login_change.rdoc
234
235
  - doc/release_notes/1.17.0.txt
@@ -251,6 +252,7 @@ extra_rdoc_files:
251
252
  - doc/release_notes/1.9.0.txt
252
253
  - doc/release_notes/1.18.0.txt
253
254
  - doc/release_notes/1.19.0.txt
255
+ - doc/release_notes/1.20.0.txt
254
256
  files:
255
257
  - CHANGELOG
256
258
  - MIT-LICENSE
@@ -272,6 +274,7 @@ files:
272
274
  - doc/http_basic_auth.rdoc
273
275
  - doc/internals.rdoc
274
276
  - doc/jwt.rdoc
277
+ - doc/jwt_refresh.rdoc
275
278
  - doc/lockout.rdoc
276
279
  - doc/login.rdoc
277
280
  - doc/login_password_requirements_base.rdoc
@@ -294,6 +297,7 @@ files:
294
297
  - doc/release_notes/1.18.0.txt
295
298
  - doc/release_notes/1.19.0.txt
296
299
  - doc/release_notes/1.2.0.txt
300
+ - doc/release_notes/1.20.0.txt
297
301
  - doc/release_notes/1.3.0.txt
298
302
  - doc/release_notes/1.4.0.txt
299
303
  - doc/release_notes/1.5.0.txt
@@ -328,6 +332,7 @@ files:
328
332
  - lib/rodauth/features/email_base.rb
329
333
  - lib/rodauth/features/http_basic_auth.rb
330
334
  - lib/rodauth/features/jwt.rb
335
+ - lib/rodauth/features/jwt_refresh.rb
331
336
  - lib/rodauth/features/lockout.rb
332
337
  - lib/rodauth/features/login.rb
333
338
  - lib/rodauth/features/login_password_requirements_base.rb
@@ -362,6 +367,7 @@ files:
362
367
  - spec/disallow_password_reuse_spec.rb
363
368
  - spec/email_auth_spec.rb
364
369
  - spec/http_basic_auth_spec.rb
370
+ - spec/jwt_refresh_spec.rb
365
371
  - spec/jwt_spec.rb
366
372
  - spec/lockout_spec.rb
367
373
  - spec/login_spec.rb
@@ -432,7 +438,12 @@ files:
432
438
  homepage: https://github.com/jeremyevans/rodauth
433
439
  licenses:
434
440
  - MIT
435
- metadata: {}
441
+ metadata:
442
+ bug_tracker_uri: https://github.com/jeremyevans/rodauth/issues
443
+ changelog_uri: http://rodauth.jeremyevans.net/rdoc/files/CHANGELOG.html
444
+ documentation_uri: http://rodauth.jeremyevans.net/documentation.html
445
+ mailing_list_uri: https://groups.google.com/forum/#!forum/rodauth
446
+ source_code_uri: https://github.com/jeremyevans/rodauth
436
447
  post_install_message:
437
448
  rdoc_options:
438
449
  - "--quiet"
@@ -455,8 +466,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
455
466
  - !ruby/object:Gem::Version
456
467
  version: '0'
457
468
  requirements: []
458
- rubyforge_project:
459
- rubygems_version: 2.7.6
469
+ rubygems_version: 3.0.3
460
470
  signing_key:
461
471
  specification_version: 4
462
472
  summary: Authentication and Account Management Framework for Rack Applications