rodauth 1.19.1 → 1.20.0

Sign up to get free protection for your applications and to get access to all the features.
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