rodauth 1.22.0 → 2.3.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 (198) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +190 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +210 -80
  5. data/doc/account_expiration.rdoc +12 -26
  6. data/doc/active_sessions.rdoc +49 -0
  7. data/doc/audit_logging.rdoc +44 -0
  8. data/doc/base.rdoc +75 -128
  9. data/doc/change_login.rdoc +7 -14
  10. data/doc/change_password.rdoc +9 -13
  11. data/doc/change_password_notify.rdoc +2 -2
  12. data/doc/close_account.rdoc +9 -16
  13. data/doc/confirm_password.rdoc +12 -5
  14. data/doc/create_account.rdoc +11 -22
  15. data/doc/disallow_password_reuse.rdoc +6 -13
  16. data/doc/email_auth.rdoc +15 -14
  17. data/doc/email_base.rdoc +6 -15
  18. data/doc/guides/admin_activation.rdoc +46 -0
  19. data/doc/guides/already_authenticated.rdoc +10 -0
  20. data/doc/guides/alternative_login.rdoc +46 -0
  21. data/doc/guides/create_account_programmatically.rdoc +38 -0
  22. data/doc/guides/delay_password.rdoc +25 -0
  23. data/doc/guides/email_only.rdoc +16 -0
  24. data/doc/guides/i18n.rdoc +26 -0
  25. data/doc/{internals.rdoc → guides/internals.rdoc} +0 -0
  26. data/doc/guides/links.rdoc +12 -0
  27. data/doc/guides/login_return.rdoc +37 -0
  28. data/doc/guides/password_column.rdoc +25 -0
  29. data/doc/guides/password_confirmation.rdoc +37 -0
  30. data/doc/guides/password_requirements.rdoc +30 -0
  31. data/doc/guides/paths.rdoc +36 -0
  32. data/doc/guides/query_params.rdoc +9 -0
  33. data/doc/guides/redirects.rdoc +17 -0
  34. data/doc/guides/registration_field.rdoc +68 -0
  35. data/doc/guides/require_mfa.rdoc +30 -0
  36. data/doc/guides/reset_password_autologin.rdoc +21 -0
  37. data/doc/guides/status_column.rdoc +28 -0
  38. data/doc/guides/totp_or_recovery.rdoc +16 -0
  39. data/doc/http_basic_auth.rdoc +10 -1
  40. data/doc/jwt.rdoc +22 -22
  41. data/doc/jwt_cors.rdoc +2 -3
  42. data/doc/jwt_refresh.rdoc +23 -8
  43. data/doc/lockout.rdoc +17 -15
  44. data/doc/login.rdoc +17 -2
  45. data/doc/login_password_requirements_base.rdoc +18 -37
  46. data/doc/logout.rdoc +2 -2
  47. data/doc/otp.rdoc +25 -19
  48. data/doc/password_complexity.rdoc +10 -26
  49. data/doc/password_expiration.rdoc +11 -25
  50. data/doc/password_grace_period.rdoc +16 -2
  51. data/doc/recovery_codes.rdoc +18 -12
  52. data/doc/release_notes/1.23.0.txt +32 -0
  53. data/doc/release_notes/2.0.0.txt +361 -0
  54. data/doc/release_notes/2.1.0.txt +31 -0
  55. data/doc/release_notes/2.2.0.txt +39 -0
  56. data/doc/release_notes/2.3.0.txt +37 -0
  57. data/doc/remember.rdoc +40 -64
  58. data/doc/reset_password.rdoc +12 -9
  59. data/doc/session_expiration.rdoc +1 -0
  60. data/doc/single_session.rdoc +16 -25
  61. data/doc/sms_codes.rdoc +24 -14
  62. data/doc/two_factor_base.rdoc +60 -22
  63. data/doc/verify_account.rdoc +14 -12
  64. data/doc/verify_account_grace_period.rdoc +6 -2
  65. data/doc/verify_login_change.rdoc +9 -8
  66. data/doc/webauthn.rdoc +115 -0
  67. data/doc/webauthn_login.rdoc +15 -0
  68. data/doc/webauthn_verify_account.rdoc +9 -0
  69. data/javascript/webauthn_auth.js +45 -0
  70. data/javascript/webauthn_setup.js +35 -0
  71. data/lib/roda/plugins/rodauth.rb +1 -1
  72. data/lib/rodauth.rb +36 -28
  73. data/lib/rodauth/features/account_expiration.rb +5 -5
  74. data/lib/rodauth/features/active_sessions.rb +158 -0
  75. data/lib/rodauth/features/audit_logging.rb +98 -0
  76. data/lib/rodauth/features/base.rb +144 -43
  77. data/lib/rodauth/features/change_password_notify.rb +2 -2
  78. data/lib/rodauth/features/close_account.rb +8 -6
  79. data/lib/rodauth/features/confirm_password.rb +40 -2
  80. data/lib/rodauth/features/create_account.rb +8 -13
  81. data/lib/rodauth/features/disallow_common_passwords.rb +1 -1
  82. data/lib/rodauth/features/disallow_password_reuse.rb +1 -1
  83. data/lib/rodauth/features/email_auth.rb +31 -30
  84. data/lib/rodauth/features/email_base.rb +9 -4
  85. data/lib/rodauth/features/http_basic_auth.rb +55 -35
  86. data/lib/rodauth/features/jwt.rb +63 -16
  87. data/lib/rodauth/features/jwt_cors.rb +15 -15
  88. data/lib/rodauth/features/jwt_refresh.rb +42 -13
  89. data/lib/rodauth/features/lockout.rb +12 -14
  90. data/lib/rodauth/features/login.rb +64 -15
  91. data/lib/rodauth/features/login_password_requirements_base.rb +13 -8
  92. data/lib/rodauth/features/otp.rb +77 -80
  93. data/lib/rodauth/features/password_complexity.rb +8 -13
  94. data/lib/rodauth/features/password_expiration.rb +2 -2
  95. data/lib/rodauth/features/password_grace_period.rb +17 -10
  96. data/lib/rodauth/features/recovery_codes.rb +49 -53
  97. data/lib/rodauth/features/remember.rb +11 -27
  98. data/lib/rodauth/features/reset_password.rb +26 -26
  99. data/lib/rodauth/features/session_expiration.rb +7 -10
  100. data/lib/rodauth/features/single_session.rb +8 -6
  101. data/lib/rodauth/features/sms_codes.rb +62 -72
  102. data/lib/rodauth/features/two_factor_base.rb +134 -30
  103. data/lib/rodauth/features/verify_account.rb +29 -21
  104. data/lib/rodauth/features/verify_account_grace_period.rb +18 -9
  105. data/lib/rodauth/features/verify_login_change.rb +12 -11
  106. data/lib/rodauth/features/webauthn.rb +505 -0
  107. data/lib/rodauth/features/webauthn_login.rb +70 -0
  108. data/lib/rodauth/features/webauthn_verify_account.rb +46 -0
  109. data/lib/rodauth/migrations.rb +16 -5
  110. data/lib/rodauth/version.rb +2 -2
  111. data/templates/button.str +1 -3
  112. data/templates/change-login.str +1 -2
  113. data/templates/change-password.str +3 -5
  114. data/templates/close-account.str +2 -2
  115. data/templates/confirm-password.str +1 -1
  116. data/templates/create-account.str +1 -1
  117. data/templates/email-auth-request-form.str +2 -3
  118. data/templates/email-auth.str +1 -1
  119. data/templates/global-logout-field.str +6 -0
  120. data/templates/login-confirm-field.str +2 -4
  121. data/templates/login-display.str +3 -2
  122. data/templates/login-field.str +2 -4
  123. data/templates/login-form-footer.str +6 -0
  124. data/templates/login-form.str +7 -0
  125. data/templates/login.str +1 -9
  126. data/templates/logout.str +1 -1
  127. data/templates/multi-phase-login.str +3 -0
  128. data/templates/otp-auth-code-field.str +5 -3
  129. data/templates/otp-auth.str +1 -1
  130. data/templates/otp-disable.str +1 -1
  131. data/templates/otp-setup.str +3 -3
  132. data/templates/password-confirm-field.str +2 -4
  133. data/templates/password-field.str +2 -4
  134. data/templates/recovery-auth.str +3 -6
  135. data/templates/recovery-codes.str +1 -1
  136. data/templates/remember.str +15 -20
  137. data/templates/reset-password-request.str +3 -3
  138. data/templates/reset-password.str +1 -2
  139. data/templates/sms-auth.str +1 -1
  140. data/templates/sms-code-field.str +5 -3
  141. data/templates/sms-confirm.str +1 -2
  142. data/templates/sms-disable.str +1 -2
  143. data/templates/sms-request.str +1 -1
  144. data/templates/sms-setup.str +6 -4
  145. data/templates/two-factor-auth.str +5 -0
  146. data/templates/two-factor-disable.str +6 -0
  147. data/templates/two-factor-manage.str +16 -0
  148. data/templates/unlock-account-request.str +4 -4
  149. data/templates/unlock-account.str +1 -1
  150. data/templates/verify-account-resend.str +3 -3
  151. data/templates/verify-account.str +1 -2
  152. data/templates/verify-login-change.str +1 -1
  153. data/templates/webauthn-auth.str +11 -0
  154. data/templates/webauthn-remove.str +14 -0
  155. data/templates/webauthn-setup.str +12 -0
  156. metadata +94 -54
  157. data/Rakefile +0 -179
  158. data/doc/verify_change_login.rdoc +0 -11
  159. data/lib/rodauth/features/verify_change_login.rb +0 -20
  160. data/spec/account_expiration_spec.rb +0 -225
  161. data/spec/all.rb +0 -1
  162. data/spec/change_login_spec.rb +0 -156
  163. data/spec/change_password_notify_spec.rb +0 -33
  164. data/spec/change_password_spec.rb +0 -202
  165. data/spec/close_account_spec.rb +0 -162
  166. data/spec/confirm_password_spec.rb +0 -70
  167. data/spec/create_account_spec.rb +0 -127
  168. data/spec/disallow_common_passwords_spec.rb +0 -93
  169. data/spec/disallow_password_reuse_spec.rb +0 -179
  170. data/spec/email_auth_spec.rb +0 -285
  171. data/spec/http_basic_auth_spec.rb +0 -143
  172. data/spec/jwt_cors_spec.rb +0 -57
  173. data/spec/jwt_refresh_spec.rb +0 -256
  174. data/spec/jwt_spec.rb +0 -235
  175. data/spec/lockout_spec.rb +0 -250
  176. data/spec/login_spec.rb +0 -328
  177. data/spec/migrate/001_tables.rb +0 -184
  178. data/spec/migrate/002_account_password_hash_column.rb +0 -11
  179. data/spec/migrate_password/001_tables.rb +0 -73
  180. data/spec/migrate_travis/001_tables.rb +0 -141
  181. data/spec/password_complexity_spec.rb +0 -109
  182. data/spec/password_expiration_spec.rb +0 -244
  183. data/spec/password_grace_period_spec.rb +0 -93
  184. data/spec/remember_spec.rb +0 -451
  185. data/spec/reset_password_spec.rb +0 -229
  186. data/spec/rodauth_spec.rb +0 -343
  187. data/spec/session_expiration_spec.rb +0 -58
  188. data/spec/single_session_spec.rb +0 -127
  189. data/spec/spec_helper.rb +0 -327
  190. data/spec/two_factor_spec.rb +0 -1462
  191. data/spec/update_password_hash_spec.rb +0 -40
  192. data/spec/verify_account_grace_period_spec.rb +0 -171
  193. data/spec/verify_account_spec.rb +0 -240
  194. data/spec/verify_change_login_spec.rb +0 -46
  195. data/spec/verify_login_change_spec.rb +0 -232
  196. data/spec/views/layout-other.str +0 -11
  197. data/spec/views/layout.str +0 -11
  198. data/spec/views/login.str +0 -21
@@ -0,0 +1,70 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Rodauth
4
+ Feature.define(:webauthn_login, :WebauthnLogin) do
5
+ depends :login, :webauthn
6
+
7
+ before
8
+
9
+ redirect(:webauthn_login_failure){require_login_redirect}
10
+
11
+ error_flash "There was an error authenticating via WebAuthn"
12
+
13
+ route(:webauthn_login) do |r|
14
+ check_already_logged_in
15
+ before_webauthn_login_route
16
+
17
+ r.post do
18
+ catch_error do
19
+ unless account_from_login(param(login_param)) && open_account?
20
+ throw_error_status(no_matching_login_error_status, login_param, no_matching_login_message)
21
+ end
22
+
23
+ webauthn_credential = webauthn_auth_credential_from_form_submission
24
+ before_webauthn_login
25
+ login('webauthn') do
26
+ webauthn_update_session(webauthn_credential.id)
27
+ end
28
+ end
29
+
30
+ set_redirect_error_flash webauthn_login_error_flash
31
+ redirect webauthn_login_failure_redirect
32
+ end
33
+ end
34
+
35
+ def webauthn_auth_additional_form_tags
36
+ if @webauthn_login
37
+ super.to_s + login_hidden_field
38
+ else
39
+ super
40
+ end
41
+ end
42
+
43
+ def webauthn_auth_form_path
44
+ if @webauthn_login
45
+ webauthn_login_path
46
+ else
47
+ super
48
+ end
49
+ end
50
+
51
+ def use_multi_phase_login?
52
+ true
53
+ end
54
+
55
+ private
56
+
57
+ def _multi_phase_login_forms
58
+ forms = super
59
+ if valid_login_entered? && webauthn_setup?
60
+ @webauthn_login = true
61
+ forms << [20, render('webauthn-auth'), nil]
62
+ end
63
+ forms
64
+ end
65
+
66
+ def webauthn_account_id
67
+ super || account_id
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,46 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Rodauth
4
+ Feature.define(:webauthn_verify_account, :WebauthnVerifyAccount) do
5
+ depends :verify_account, :webauthn
6
+
7
+ def verify_account_view
8
+ webauthn_setup_view
9
+ end
10
+
11
+ def create_account_set_password?
12
+ false
13
+ end
14
+
15
+ def verify_account_set_password?
16
+ false
17
+ end
18
+
19
+ def autologin_session(autologin_type)
20
+ super
21
+ if autologin_type == 'verify_account'
22
+ set_session_value(authenticated_by_session_key, ['webauthn'])
23
+ remove_session_value(autologin_type_session_key)
24
+ webauthn_update_session(@webauthn_credential.id)
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def before_verify_account
31
+ super
32
+ if features.include?(:jwt) && use_jwt? && !param_or_nil(webauthn_setup_param)
33
+ cred = new_webauthn_credential
34
+ json_response[webauthn_setup_param] = cred.as_json
35
+ json_response[webauthn_setup_challenge_param] = cred.challenge
36
+ json_response[webauthn_setup_challenge_hmac_param] = compute_hmac(cred.challenge)
37
+ end
38
+ @webauthn_credential = webauthn_setup_credential_from_form_submission
39
+ add_webauthn_credential(@webauthn_credential)
40
+ end
41
+
42
+ def webauthn_account_id
43
+ super || account_id
44
+ end
45
+ end
46
+ end
@@ -9,9 +9,14 @@ module Rodauth
9
9
  case db.database_type
10
10
  when :postgres
11
11
  search_path = opts[:search_path] || 'public, pg_temp'
12
+ primary_key_type =
13
+ case db.schema(table_name).find { |row| row.first == :id }[1][:db_type]
14
+ when 'uuid' then :uuid
15
+ else :int8
16
+ end
12
17
 
13
18
  db.run <<END
14
- CREATE OR REPLACE FUNCTION #{get_salt_name}(acct_id int8) RETURNS text AS $$
19
+ CREATE OR REPLACE FUNCTION #{get_salt_name}(acct_id #{primary_key_type}) RETURNS text AS $$
15
20
  DECLARE salt text;
16
21
  BEGIN
17
22
  SELECT substr(password_hash, 0, 30) INTO salt
@@ -25,7 +30,7 @@ SET search_path = #{search_path};
25
30
  END
26
31
 
27
32
  db.run <<END
28
- CREATE OR REPLACE FUNCTION #{valid_hash_name}(acct_id int8, hash text) RETURNS boolean AS $$
33
+ CREATE OR REPLACE FUNCTION #{valid_hash_name}(acct_id #{primary_key_type}, hash text) RETURNS boolean AS $$
29
34
  DECLARE valid boolean;
30
35
  BEGIN
31
36
  SELECT password_hash = hash INTO valid
@@ -100,13 +105,19 @@ END
100
105
  end
101
106
 
102
107
  def self.drop_database_authentication_functions(db, opts={})
108
+ table_name = opts[:table_name] || :account_password_hashes
103
109
  get_salt_name = opts[:get_salt_name] || :rodauth_get_salt
104
110
  valid_hash_name = opts[:valid_hash_name] || :rodauth_valid_password_hash
105
111
 
106
112
  case db.database_type
107
113
  when :postgres
108
- db.run "DROP FUNCTION #{get_salt_name}(int8)"
109
- db.run "DROP FUNCTION #{valid_hash_name}(int8, text)"
114
+ primary_key_type =
115
+ case db.schema(table_name).find { |row| row.first == :id }[1][:db_type]
116
+ when 'uuid' then :uuid
117
+ else :int8
118
+ end
119
+ db.run "DROP FUNCTION #{get_salt_name}(#{primary_key_type})"
120
+ db.run "DROP FUNCTION #{valid_hash_name}(#{primary_key_type}, text)"
110
121
  when :mysql, :mssql
111
122
  db.run "DROP FUNCTION #{get_salt_name}"
112
123
  db.run "DROP FUNCTION #{valid_hash_name}"
@@ -118,6 +129,6 @@ END
118
129
  end
119
130
 
120
131
  def self.drop_database_previous_password_check_functions(db, opts={})
121
- drop_database_authentication_functions(db, {:get_salt_name=>:rodauth_get_previous_salt, :valid_hash_name=>:rodauth_previous_password_hash_match}.merge(opts))
132
+ drop_database_authentication_functions(db, {:table_name=>:account_previous_password_hashes, :get_salt_name=>:rodauth_get_previous_salt, :valid_hash_name=>:rodauth_previous_password_hash_match}.merge(opts))
122
133
  end
123
134
  end
@@ -3,10 +3,10 @@
3
3
  module Rodauth
4
4
  # The major version of Rodauth, updated only for major changes that are
5
5
  # likely to require modification to apps using Rodauth.
6
- MAJOR = 1
6
+ MAJOR = 2
7
7
 
8
8
  # The minor version of Rodauth, updated for new feature releases of Rodauth.
9
- MINOR = 22
9
+ MINOR = 3
10
10
 
11
11
  # The patch version of Rodauth, updated only for bug fixes from the last
12
12
  # feature release.
@@ -1,5 +1,3 @@
1
1
  <div class="form-group">
2
- <div class="col-sm-offset-2 col-sm-10">
3
- <input type="submit" #{"name=\"#{h opts[:name]}\"" if opts[:name]} class="#{h(opts[:class] || 'btn btn-primary')}" value="#{h value}"/>
4
- </div>
2
+ <input type="submit" #{"name=\"#{h opts[:name]}\"" if opts[:name]} class="#{h(opts[:class] || 'btn btn-primary')}" value="#{h value}"/>
5
3
  </div>
@@ -1,4 +1,4 @@
1
- <form method="post" class="rodauth form-horizontal" role="form" id="change-login-form">
1
+ <form method="post" class="rodauth" role="form" id="change-login-form">
2
2
  #{rodauth.change_login_additional_form_tags}
3
3
  #{rodauth.csrf_tag}
4
4
  #{rodauth.render('login-field')}
@@ -6,4 +6,3 @@
6
6
  #{rodauth.render('password-field') if rodauth.change_login_requires_password?}
7
7
  #{rodauth.button(rodauth.change_login_button)}
8
8
  </form>
9
-
@@ -1,12 +1,10 @@
1
- <form method="post" class="rodauth form-horizontal" role="form" id="change-password-form">
1
+ <form method="post" class="rodauth" role="form" id="change-password-form">
2
2
  #{rodauth.change_password_additional_form_tags}
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}#{rodauth.input_field_label_suffix}</label>
7
- <div class="col-sm-10">
8
- #{rodauth.input_field_string(rodauth.new_password_param, 'new-password', :type => 'password')}
9
- </div>
6
+ <label for="new-password">#{rodauth.new_password_label}#{rodauth.input_field_label_suffix}</label>
7
+ #{rodauth.input_field_string(rodauth.new_password_param, 'new-password', :type => 'password', :autocomplete=>"new-password")}
10
8
  </div>
11
9
  #{rodauth.render('password-confirm-field') if rodauth.require_password_confirmation?}
12
10
  #{rodauth.button(rodauth.change_password_button)}
@@ -1,6 +1,6 @@
1
- <form method="post" class="rodauth form-horizontal" role="form" id="close-account-form">
1
+ <form method="post" class="rodauth" role="form" id="close-account-form">
2
2
  #{rodauth.close_account_additional_form_tags}
3
3
  #{rodauth.csrf_tag}
4
4
  #{rodauth.render('password-field') if rodauth.close_account_requires_password?}
5
- #{rodauth.button(rodauth.close_account_button, :class=>'btn btn-warning')}
5
+ #{rodauth.button(rodauth.close_account_button, :class=>'btn btn-danger')}
6
6
  </form>
@@ -1,4 +1,4 @@
1
- <form method="post" class="rodauth form-horizontal" role="form" id="confirm-password-form">
1
+ <form method="post" class="rodauth" role="form" id="confirm-password-form">
2
2
  #{rodauth.confirm_password_additional_form_tags}
3
3
  #{rodauth.csrf_tag}
4
4
  #{rodauth.render('password-field')}
@@ -1,4 +1,4 @@
1
- <form method="post" class="rodauth form-horizontal" role="form" id="create-account-form">
1
+ <form method="post" class="rodauth" role="form" id="create-account-form">
2
2
  #{rodauth.create_account_additional_form_tags}
3
3
  #{rodauth.csrf_tag}
4
4
  #{rodauth.render('login-field')}
@@ -1,7 +1,6 @@
1
- <form action="#{rodauth.prefix}/#{rodauth.email_auth_request_route}" method="post" class="rodauth form-horizontal" role="form" id="email-auth-request-form">
1
+ <form action="#{rodauth.email_auth_request_path}" method="post" class="rodauth" role="form" id="email-auth-request-form">
2
2
  #{rodauth.email_auth_request_additional_form_tags}
3
- #{rodauth.csrf_tag("#{rodauth.prefix}/#{rodauth.email_auth_request_route}")}
3
+ #{rodauth.csrf_tag(rodauth.email_auth_request_path)}
4
4
  #{rodauth.login_hidden_field}
5
5
  #{rodauth.button(rodauth.email_auth_request_button)}
6
6
  </form>
7
-
@@ -1,4 +1,4 @@
1
- <form method="post" class="rodauth form-horizontal" role="form" id="email-auth-form">
1
+ <form method="post" class="rodauth" role="form" id="email-auth-form">
2
2
  #{rodauth.email_auth_additional_form_tags}
3
3
  #{rodauth.csrf_tag}
4
4
  #{rodauth.button(rodauth.login_button)}
@@ -0,0 +1,6 @@
1
+ <div class="form-group">
2
+ <div class="form-check checkbox">
3
+ <input type="checkbox" name="#{rodauth.global_logout_param}" class="form-check-input" id="global-logout" value="t"/>
4
+ <label class="rodauth-global-logout-label form-check-label" for="global-logout">#{rodauth.global_logout_label}</label>
5
+ </div>
6
+ </div>
@@ -1,6 +1,4 @@
1
1
  <div class="form-group">
2
- <label class="col-sm-2 control-label" for="login-confirm">#{rodauth.login_confirm_label}#{rodauth.input_field_label_suffix}</label>
3
- <div class="col-sm-10">
4
- #{rodauth.input_field_string(rodauth.login_confirm_param, 'login-confirm', :type=>rodauth.login_input_type)}
5
- </div>
2
+ <label for="login-confirm">#{rodauth.login_confirm_label}#{rodauth.input_field_label_suffix}</label>
3
+ #{rodauth.input_field_string(rodauth.login_confirm_param, 'login-confirm', :type=>rodauth.login_input_type, :autocomplete=>rodauth.login_uses_email? ? "email" : "on")}
6
4
  </div>
@@ -1,4 +1,5 @@
1
1
  <div class="form-group">
2
- <label class="col-sm-2 control-label">#{rodauth.login_label}</label>
3
- <div class="col-sm-10">#{rodauth.login_hidden_field}#{h rodauth.param(rodauth.login_param)}</div>
2
+ #{rodauth.login_hidden_field}
3
+ <label for="login">#{rodauth.login_label}</label>
4
+ <div class="form-control-plaintext form-control-static">#{h rodauth.param(rodauth.login_param)}</div>
4
5
  </div>
@@ -1,6 +1,4 @@
1
1
  <div class="form-group">
2
- <label class="col-sm-2 control-label" for="login">#{rodauth.login_label}#{rodauth.input_field_label_suffix}</label>
3
- <div class="col-sm-10">
4
- #{rodauth.input_field_string(rodauth.login_param, 'login', :type=>rodauth.login_input_type)}
5
- </div>
2
+ <label for="login">#{rodauth.login_label}#{rodauth.input_field_label_suffix}</label>
3
+ #{rodauth.input_field_string(rodauth.login_param, 'login', :type=>rodauth.login_input_type, :autocomplete=>rodauth.login_uses_email? ? "email" : "on")}
6
4
  </div>
@@ -0,0 +1,6 @@
1
+ #{rodauth.login_form_footer_links_heading}
2
+ <ul class="rodauth-links rodauth-login-footer-links">
3
+ #{rodauth.login_form_footer_links.sort.map do |_, link, text|
4
+ "<li><a href=\"#{h link}\">#{h text}</a></li>"
5
+ end.join("\n")}
6
+ </ul>
@@ -0,0 +1,7 @@
1
+ <form method="post" class="rodauth" role="form" id="login-form">
2
+ #{rodauth.login_additional_form_tags}
3
+ #{rodauth.csrf_tag}
4
+ #{rodauth.skip_login_field_on_login? ? rodauth.render('login-display') : rodauth.render('login-field')}
5
+ #{rodauth.render('password-field') unless rodauth.skip_password_field_on_login?}
6
+ #{rodauth.button(rodauth.login_button)}
7
+ </form>
@@ -1,11 +1,3 @@
1
1
  #{rodauth.login_form_header}
2
-
3
- <form method="post" class="rodauth form-horizontal" role="form" id="login-form">
4
- #{rodauth.login_additional_form_tags}
5
- #{rodauth.csrf_tag}
6
- #{rodauth.skip_login_field_on_login? ? rodauth.render('login-display') : rodauth.render('login-field')}
7
- #{rodauth.render('password-field') unless rodauth.skip_password_field_on_login?}
8
- #{rodauth.button(rodauth.login_button)}
9
- </form>
10
-
2
+ #{rodauth.render('login-form')}
11
3
  #{rodauth.login_form_footer}
@@ -1,4 +1,4 @@
1
- <form method="post" class="rodauth form-horizontal" role="form" id="logout-form">
1
+ <form method="post" class="rodauth" role="form" id="logout-form">
2
2
  #{rodauth.logout_additional_form_tags}
3
3
  #{rodauth.csrf_tag}
4
4
  #{rodauth.button(rodauth.logout_button, :class=>'btn btn-warning')}
@@ -0,0 +1,3 @@
1
+ #{rodauth.login_form_header}
2
+ #{rodauth.render_multi_phase_login_forms}
3
+ #{rodauth.login_form_footer}
@@ -1,6 +1,8 @@
1
1
  <div class="form-group">
2
- <label class="col-sm-4 control-label" for="otp-auth-code">#{rodauth.otp_auth_label}#{rodauth.input_field_label_suffix}</label>
3
- <div class="col-sm-3">
4
- #{rodauth.input_field_string(rodauth.otp_auth_param, 'otp-auth-code', :value=>'', :attr=>'autocomplete="off"' )}
2
+ <label for="otp-auth-code">#{rodauth.otp_auth_label}#{rodauth.input_field_label_suffix}</label>
3
+ <div class="row">
4
+ <div class="col-sm-3">
5
+ #{rodauth.input_field_string(rodauth.otp_auth_param, 'otp-auth-code', :value=>'', :autocomplete=>"off", :inputmode=>'numeric')}
6
+ </div>
5
7
  </div>
6
8
  </div>
@@ -1,4 +1,4 @@
1
- <form method="post" class="rodauth form-horizontal" role="form" id="otp-auth-form">
1
+ <form method="post" class="rodauth" role="form" id="otp-auth-form">
2
2
  #{rodauth.otp_auth_additional_form_tags}
3
3
  #{rodauth.csrf_tag}
4
4
  #{rodauth.render('otp-auth-code-field')}
@@ -1,4 +1,4 @@
1
- <form method="post" class="rodauth form-horizontal" role="form" id="otp-disable-form">
1
+ <form method="post" class="rodauth" role="form" id="otp-disable-form">
2
2
  #{rodauth.otp_disable_additional_form_tags}
3
3
  #{rodauth.csrf_tag}
4
4
  #{rodauth.render('password-field') if rodauth.two_factor_modifications_require_password?}
@@ -1,4 +1,4 @@
1
- <form method="post" class="rodauth form-horizontal" role="form" id="otp-setup-form">
1
+ <form method="post" class="rodauth" role="form" id="otp-setup-form">
2
2
  #{rodauth.otp_setup_additional_form_tags}
3
3
  <input type="hidden" id="otp-key" name="#{rodauth.otp_setup_param}" value="#{rodauth.otp_user_key}" />
4
4
  #{"<input type=\"hidden\" id=\"otp-hmac-secret\" name=\"#{rodauth.otp_setup_raw_param}\" value=\"#{rodauth.otp_key}\" />" if rodauth.otp_keys_use_hmac?}
@@ -9,13 +9,13 @@
9
9
  </div>
10
10
 
11
11
  <div class="row">
12
- <div class="col-sm-6 col-sm">
12
+ <div class="col-lg-6 col-lg">
13
13
  <div class="form-group">
14
14
  <p>#{rodauth.otp_qr_code}</p>
15
15
  </div>
16
16
  </div>
17
17
 
18
- <div class="col-sm-6 col-sm">
18
+ <div class="col-lg-6 col-lg">
19
19
  #{rodauth.render('password-field') if rodauth.two_factor_modifications_require_password?}
20
20
  #{rodauth.render('otp-auth-code-field')}
21
21
  #{rodauth.button(rodauth.otp_setup_button)}
@@ -1,6 +1,4 @@
1
1
  <div class="form-group">
2
- <label class="col-sm-2 control-label" for="password-confirm">#{rodauth.password_confirm_label}#{rodauth.input_field_label_suffix}</label>
3
- <div class="col-sm-10">
4
- #{rodauth.input_field_string(rodauth.password_confirm_param, 'password-confirm', :type => 'password')}
5
- </div>
2
+ <label for="password-confirm">#{rodauth.password_confirm_label}#{rodauth.input_field_label_suffix}</label>
3
+ #{rodauth.input_field_string(rodauth.password_confirm_param, 'password-confirm', :type => 'password', :autocomplete=>'new-password')}
6
4
  </div>
@@ -1,6 +1,4 @@
1
1
  <div class="form-group">
2
- <label class="col-sm-2 control-label" for="password">#{rodauth.password_label}#{rodauth.input_field_label_suffix}</label>
3
- <div class="col-sm-10">
4
- #{rodauth.input_field_string(rodauth.password_param, 'password', :type => 'password')}
5
- </div>
2
+ <label for="password">#{rodauth.password_label}#{rodauth.input_field_label_suffix}</label>
3
+ #{rodauth.input_field_string(rodauth.password_param, 'password', :type => 'password', :autocomplete=>rodauth.password_field_autocomplete_value)}
6
4
  </div>
@@ -1,12 +1,9 @@
1
- <form method="post" class="rodauth form-horizontal" role="form" id="recovery-auth-form">
1
+ <form method="post" class="rodauth" role="form" id="recovery-auth-form">
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}#{rodauth.input_field_label_suffix}</label>
6
- <div class="col-sm-10">
7
- #{rodauth.input_field_string(rodauth.recovery_codes_param, 'recovery_code', :value => '')}
8
- </div>
5
+ <label for="recovery-code">#{rodauth.recovery_codes_label}#{rodauth.input_field_label_suffix}</label>
6
+ #{rodauth.input_field_string(rodauth.recovery_codes_param, 'recovery-code', :value => '', :autocomplete=>'off')}
9
7
  </div>
10
8
  #{rodauth.button(rodauth.recovery_auth_button)}
11
9
  </form>
12
-
@@ -1,4 +1,4 @@
1
- <form method="post" class="rodauth form-horizontal" role="form" id="recovery-codes-form">
1
+ <form method="post" class="rodauth" role="form" id="recovery-codes-form">
2
2
  #{rodauth.recovery_codes_additional_form_tags}
3
3
  #{rodauth.csrf_tag}
4
4
  #{rodauth.render('password-field') if rodauth.two_factor_modifications_require_password?}
@@ -1,24 +1,19 @@
1
- <form method="post" class="rodauth form-horizontal" role="form" id="remember-form">
1
+ <form method="post" class="rodauth" role="form" id="remember-form">
2
2
  #{rodauth.remember_additional_form_tags}
3
3
  #{rodauth.csrf_tag}
4
- <div class="radio">
5
- <label>
6
- <input type="radio" name="#{rodauth.remember_param}" id="remember_remember" value="#{rodauth.remember_remember_param_value}"/>
7
- #{rodauth.remember_remember_label}
8
- </label>
9
- </div>
10
- <div class="radio">
11
- <label>
12
- <input type="radio" name="#{rodauth.remember_param}" id="remember_forget" value="#{rodauth.remember_forget_param_value}"/>
13
- #{rodauth.remember_forget_label}
14
- </label>
15
- </div>
16
- <div class="radio">
17
- <label>
18
- <input type="radio" name="#{rodauth.remember_param}" id="remember_disable" value="#{rodauth.remember_disable_param_value}"/>
19
- #{rodauth.remember_disable_label}
20
- </label>
21
- </div>
4
+ <fieldset class="form-group">
5
+ <div class="form-check radio">
6
+ <input type="radio" name="#{rodauth.remember_param}" id="remember-remember" value="#{h rodauth.remember_remember_param_value}" class="form-check-input"/>
7
+ <label class="form-check-label" for="remember-remember">#{rodauth.remember_remember_label}</label>
8
+ </div>
9
+ <div class="form-check radio">
10
+ <input type="radio" name="#{rodauth.remember_param}" id="remember-forget" value="#{h rodauth.remember_forget_param_value}" class="form-check-input"/>
11
+ <label class="form-check-label" for="remember-forget">#{rodauth.remember_forget_label}</label>
12
+ </div>
13
+ <div class="form-check radio">
14
+ <input type="radio" name="#{rodauth.remember_param}" id="remember-disable" value="#{h rodauth.remember_disable_param_value}" class="form-check-input"/>
15
+ <label class="form-check-label" for="remember-disable">#{rodauth.remember_disable_label}</label>
16
+ </div>
17
+ </fieldset>
22
18
  #{rodauth.button(rodauth.remember_button)}
23
19
  </form>
24
-