rodauth-rails 1.6.3 → 1.7.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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -0
  3. data/README.md +57 -68
  4. data/lib/generators/rodauth/templates/INSTRUCTIONS +4 -2
  5. data/lib/generators/rodauth/templates/app/mailers/rodauth_mailer.rb +30 -34
  6. data/lib/generators/rodauth/templates/app/misc/rodauth_main.rb +3 -0
  7. data/lib/generators/rodauth/templates/app/views/rodauth/logout.html.erb +1 -1
  8. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/_email_auth_request_form.html.erb +5 -0
  9. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/_login_form.html.erb +24 -0
  10. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/_login_form_footer.html.erb +7 -0
  11. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/_login_form_header.html.erb +3 -0
  12. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/add_recovery_codes.html.erb +16 -0
  13. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/change_login.html.erb +25 -0
  14. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/change_password.html.erb +25 -0
  15. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/close_account.html.erb +11 -0
  16. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/confirm_password.html.erb +19 -0
  17. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/create_account.html.erb +33 -0
  18. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/email_auth.html.erb +3 -0
  19. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/login.html.erb +5 -0
  20. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/logout.html.erb +10 -0
  21. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/multi_phase_login.html.erb +5 -0
  22. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/otp_auth.html.erb +9 -0
  23. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/otp_disable.html.erb +11 -0
  24. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/otp_setup.html.erb +32 -0
  25. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/recovery_auth.html.erb +9 -0
  26. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/recovery_codes.html.erb +11 -0
  27. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/remember.html.erb +18 -0
  28. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/reset_password.html.erb +17 -0
  29. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/reset_password_request.html.erb +17 -0
  30. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/sms_auth.html.erb +9 -0
  31. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/sms_confirm.html.erb +9 -0
  32. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/sms_disable.html.erb +11 -0
  33. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/sms_request.html.erb +3 -0
  34. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/sms_setup.html.erb +17 -0
  35. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/two_factor_auth.html.erb +5 -0
  36. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/two_factor_disable.html.erb +11 -0
  37. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/two_factor_manage.html.erb +26 -0
  38. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/unlock_account.html.erb +15 -0
  39. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/unlock_account_request.html.erb +9 -0
  40. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/verify_account.html.erb +19 -0
  41. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/verify_account_resend.html.erb +17 -0
  42. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/verify_login_change.html.erb +3 -0
  43. data/lib/generators/rodauth/templates/app/views/rodauth_mailer/email_auth.text.erb +1 -1
  44. data/lib/generators/rodauth/templates/app/views/rodauth_mailer/reset_password.text.erb +1 -1
  45. data/lib/generators/rodauth/templates/app/views/rodauth_mailer/reset_password_notify.text.erb +2 -0
  46. data/lib/generators/rodauth/templates/app/views/rodauth_mailer/unlock_account.text.erb +2 -2
  47. data/lib/generators/rodauth/templates/app/views/rodauth_mailer/verify_account.text.erb +1 -1
  48. data/lib/generators/rodauth/templates/app/views/rodauth_mailer/verify_login_change.text.erb +1 -1
  49. data/lib/generators/rodauth/views_generator.rb +18 -1
  50. data/lib/rodauth/rails/feature/base.rb +1 -1
  51. data/lib/rodauth/rails/version.rb +1 -1
  52. metadata +38 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e133150815312f4fec4d4c03685b7a762b285860f72cd4594c5d034b58c8d37f
4
- data.tar.gz: 10623324c6d20a69973f48faf950563d76ddffa1eb70f39cf82872b1318042ee
3
+ metadata.gz: '09013a1bf694e549fa97b25df4490342da804f04a4b390e3c443130b2ae6d39c'
4
+ data.tar.gz: 9c8a721619b16280d7892fe35ec6d0368d3bef2dd9eb0a26edddd5f65f87f5cb
5
5
  SHA512:
6
- metadata.gz: a8b8d22356e108f7e7a6a4025958639546e2fd957a1115f5faf3faf187c136072c7aac1ab130e04a925637a77c31b99326c92d6e43cbc363b6bba3a89188718d
7
- data.tar.gz: a5b40c767d34b94f8485d61cb0bd45021108ce927ee16892e72c2095c31a7091c75229bfd971f029227e5a24355de4f090cb75f5d974be9b7c267a5032e2e7c8
6
+ metadata.gz: 3710f7d71bff102f6a4564542575a75798df2063df3217a01574b04a541def22fb1ca04cf529fdfb1709e5f6c334f3ddda7027dce639f7b1d47ba4f71ba421df
7
+ data.tar.gz: b36c22fdd2e297058ac576d5c064ccb3a982370c0d0d70d2651a1b271ec2e0be4af7a8844dec098bfb839de90d5f3149d88987ed58c1728a4a7ecb039e874a4e
data/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## 1.7.0 (2022-12-21)
2
+
3
+ * Add Tailwind CSS templates to `rodauth:views` generator via the `--css=tailwind` option (@benkoshy, @janko)
4
+
5
+ ## 1.6.4 (2022-11-24)
6
+
7
+ * Make `#rails_account` work on directly allocated Rodauth object with `@account` set (@janko)
8
+
9
+ * Add commented out email configuration for `password_reset_notify` feature (@janko)
10
+
11
+ * Design generated mailer in a way that exposes the Rodauth object (@janko)
12
+
13
+ * Fix generated logout page always logging out globally when using active sessions feature (@janko)
14
+
1
15
  ## 1.6.3 (2022-11-15)
2
16
 
3
17
  * Suggest passing an integer to `verify_account_grace_period` instead of `ActiveSupport::Duration` (@vlado)
data/README.md CHANGED
@@ -22,6 +22,8 @@ Provides Rails integration for the [Rodauth] authentication framework.
22
22
  * [Rails Authentication with Rodauth](https://janko.io/adding-authentication-in-rails-with-rodauth/)
23
23
  * [Multifactor Authentication in Rails with Rodauth](https://janko.io/adding-multifactor-authentication-in-rails-with-rodauth/)
24
24
  * [How to build an OIDC provider using rodauth-oauth on Rails](https://honeyryderchuck.gitlab.io/httpx/2021/03/15/oidc-provider-on-rails-using-rodauth-oauth.html)
25
+ * [What It Took to Build a Rails Integration for Rodauth](https://janko.io/what-it-took-to-build-a-rails-integration-for-rodauth/)
26
+ * [Social Login in Rails with Rodauth](https://janko.io/social-login-in-rails-with-rodauth/)
25
27
 
26
28
  ## Why Rodauth?
27
29
 
@@ -40,11 +42,19 @@ of the advantages that stand out for me:
40
42
  * consistent before/after hooks around everything
41
43
  * dedicated object encapsulating all authentication logic
42
44
 
43
- One common concern is the fact that, unlike most other authentication
44
- frameworks for Rails, Rodauth uses [Sequel] for database interaction instead of
45
- Active Record. There are good reasons for this, and to make Rodauth work
46
- smoothly alongside Active Record, rodauth-rails configures Sequel to [reuse
47
- Active Record's database connection][sequel-activerecord_connection].
45
+ ### Sequel
46
+
47
+ One common concern for people coming from other Rails authentication frameworks
48
+ is the fact that Rodauth uses [Sequel] for database interaction instead of
49
+ Active Record. Sequel has powerful APIs for building advanced queries,
50
+ supporting complex SQL expressions, database-agnostic date arithmetic, SQL
51
+ function calls and more, all without having to drop down to raw SQL.
52
+
53
+ For Rails apps using Active Record, rodauth-rails configures Sequel to [reuse
54
+ Active Record's database connection][sequel-activerecord_connection]. This
55
+ makes it run smoothly alongside Active Record, even allowing calling Active
56
+ Record code from within Rodauth configuration. So, for all intents and
57
+ purposes, Sequel can be treated just as an implementation detail of Rodauth.
48
58
 
49
59
  ## Installation
50
60
 
@@ -85,18 +95,27 @@ default URL options in each environment. Here is a possible configuration for
85
95
  `config/environments/development.rb`:
86
96
 
87
97
  ```rb
88
- config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
98
+ config.action_mailer.default_url_options = { host: "localhost", port: 3000 }
89
99
  ```
90
100
 
91
101
  ## Usage
92
102
 
93
- ### Routes
103
+ The Rodauth app will be called for each request before it reaches the Rails
104
+ router. It handles requests to Rodauth endpoints, and allows you to call
105
+ additional code before your main routes.
94
106
 
95
- Because requests to Rodauth endpoints are handled by a Rack middleware (and not
96
- a Rails controller), Rodauth routes will not show in `rails routes`.
107
+ ```sh
108
+ $ rails middleware
109
+ # ...
110
+ # use Rodauth::Rails::Middleware (calls your Rodauth app)
111
+ # run YourApp::Application.routes
112
+ ```
97
113
 
98
- Use the `rodauth:routes` rake task to view the list of endpoints based on
99
- currently loaded features:
114
+ ### Routes
115
+
116
+ Because requests to Rodauth endpoints are handled by Roda, Rodauth routes will
117
+ not show in `rails routes`. You can use the `rodauth:routes` rake task to view
118
+ the list of endpoints based on currently loaded features:
100
119
 
101
120
  ```sh
102
121
  $ rails rodauth:routes
@@ -295,7 +314,9 @@ you'll want to start editing the markup. You can run the following command to
295
314
  copy Rodauth templates into your Rails app:
296
315
 
297
316
  ```sh
298
- $ rails generate rodauth:views
317
+ $ rails generate rodauth:views # bootstrap views
318
+ # or
319
+ $ rails generate rodauth:views --css=tailwind # tailwind views (requires @tailwindcss/forms plugin)
299
320
  ```
300
321
 
301
322
  This will generate views for Rodauth features you have currently enabled into
@@ -594,9 +615,9 @@ Then in your application you can reference the secondary Rodauth instance:
594
615
  rodauth(:admin).login_path #=> "/admin/login"
595
616
  ```
596
617
 
597
- You'll likely want to save the information of which account belongs to which
618
+ *NOTE: You'll likely want to save the information of which account belongs to which
598
619
  configuration to the database. See [this guide][account types] on how you can do
599
- that.
620
+ that.*
600
621
 
601
622
  ### Sharing configuration
602
623
 
@@ -720,10 +741,9 @@ Rodauth::Rails.rodauth(:admin, params: { "param" => "value" })
720
741
  ## Testing
721
742
 
722
743
  For system and integration tests, which run the whole middleware stack,
723
- authentication can be exercised normally via HTTP endpoints. See [this wiki
724
- page](https://github.com/janko/rodauth-rails/wiki/Testing) for some examples.
744
+ authentication can be exercised normally via HTTP endpoints. For example, given
745
+ a controller
725
746
 
726
- For controller tests, you can log in accounts by modifying the session:
727
747
 
728
748
  ```rb
729
749
  # app/controllers/articles_controller.rb
@@ -735,9 +755,23 @@ class ArticlesController < ApplicationController
735
755
  end
736
756
  end
737
757
  ```
758
+
759
+ One can write `ActionDispatch::IntegrationTest` test helpers for `login` and
760
+ `logout` by making requests to the rodauth endpoints
761
+
738
762
  ```rb
739
763
  # test/controllers/articles_controller_test.rb
740
- class ArticlesControllerTest < ActionController::TestCase
764
+ class ArticlesControllerTest < ActionDispatch::IntegrationTest
765
+ def login(login, password)
766
+ post "/login", params: { login: login, password: password }
767
+ assert_redirected_to "/"
768
+ end
769
+
770
+ def logout
771
+ post "/logout"
772
+ assert_redirected_to "/"
773
+ end
774
+
741
775
  test "required authentication" do
742
776
  get :index
743
777
 
@@ -746,7 +780,7 @@ class ArticlesControllerTest < ActionController::TestCase
746
780
  assert_equal "Please login to continue", flash[:alert]
747
781
 
748
782
  account = Account.create!(email: "user@example.com", password: "secret123", status: "verified")
749
- login(account)
783
+ login(account.email, "secret123")
750
784
 
751
785
  get :index
752
786
  assert_response 200
@@ -757,45 +791,11 @@ class ArticlesControllerTest < ActionController::TestCase
757
791
  assert_response 302
758
792
  assert_equal "Please login to continue", flash[:alert]
759
793
  end
760
-
761
- private
762
-
763
- # Manually modify the session into what Rodauth expects.
764
- def login(account)
765
- session[:account_id] = account.id
766
- session[:authenticated_by] = ["password"] # or ["password", "totp"] for MFA
767
- end
768
-
769
- def logout
770
- session.clear
771
- end
772
794
  end
773
795
  ```
774
796
 
775
- If you're using multiple configurations with different session prefixes, you'll need
776
- to make sure to use those in controller tests as well:
777
-
778
- ```rb
779
- class RodauthAdmin < Rodauth::Rails::Auth
780
- configure do
781
- session_key_prefix "admin_"
782
- end
783
- end
784
- ```
785
- ```rb
786
- # in a controller test:
787
- session[:admin_account_id] = account.id
788
- session[:admin_authenticated_by] = ["password"]
789
- ```
790
-
791
- If you want to access the Rodauth instance in controller tests, you can do so
792
- through the controller instance:
793
-
794
- ```rb
795
- # in a controller test:
796
- @controller.rodauth #=> #<RodauthMain ...>
797
- @controller.rodauth(:admin) #=> #<RodauthAdmin ...>
798
- ```
797
+ For more examples and information about testing with rodauth, see
798
+ [this wiki page about testing](https://github.com/janko/rodauth-rails/wiki/Testing).
799
799
 
800
800
  ## Configuring
801
801
 
@@ -859,6 +859,8 @@ helpers through `#rails_routes`:
859
859
  class RodauthMain < Rodauth::Rails::Auth
860
860
  configure do
861
861
  login_redirect { rails_routes.activity_path }
862
+ change_password_redirect { rails_routes.profile_path }
863
+ change_login_redirect { rails_routes.profile_path }
862
864
  end
863
865
  end
864
866
  ```
@@ -1069,19 +1071,6 @@ end
1069
1071
  <% rodauth(:admin) #=> #<RodauthAdmin> (if using multiple configurations) %>
1070
1072
  ```
1071
1073
 
1072
- ### Sequel
1073
-
1074
- Rodauth uses the [Sequel] library for database interaction, which offers
1075
- powerful APIs for building advanced queries (it supports SQL expressions,
1076
- database-agnostic date arithmetic, SQL function calls).
1077
-
1078
- If you're using Active Record in your application, the `rodauth:install`
1079
- generator automatically configures Sequel to reuse ActiveRecord's database
1080
- connection, using the [sequel-activerecord_connection] gem.
1081
-
1082
- This means that, from the usage perspective, Sequel can be considered just
1083
- as an implementation detail of Rodauth.
1084
-
1085
1074
  ## Rodauth defaults
1086
1075
 
1087
1076
  rodauth-rails changes some of the default Rodauth settings for easier setup:
@@ -43,8 +43,10 @@ Depending on your application's configuration some manual setup may be required:
43
43
 
44
44
  5. You can copy Rodauth views (for customization) to your app by running:
45
45
 
46
- rails g rodauth:views
46
+ rails g rodauth:views # default bootstrap views
47
47
 
48
- * Not required *
48
+ rails g rodauth:views --css=tailwind # tailwind views (requires @tailwindcss/forms plugin)
49
+
50
+ * Not required *
49
51
 
50
52
  ===============================================================================
@@ -1,64 +1,60 @@
1
1
  class RodauthMailer < ApplicationMailer
2
2
  def verify_account(name, account_id, key)
3
- @email_link = email_link(name, :verify_account, account_id, key)
4
- @account = find_account(name, account_id)
3
+ @rodauth = rodauth(name, account_id) { @verify_account_key_value = key }
4
+ @account = @rodauth.rails_account
5
5
 
6
- mail to: @account.email, subject: rodauth(name).verify_account_email_subject
6
+ mail to: @account.email, subject: @rodauth.verify_account_email_subject
7
7
  end
8
8
 
9
9
  def reset_password(name, account_id, key)
10
- @email_link = email_link(name, :reset_password, account_id, key)
11
- @account = find_account(name, account_id)
10
+ @rodauth = rodauth(name, account_id) { @reset_password_key_value = key }
11
+ @account = @rodauth.rails_account
12
12
 
13
- mail to: @account.email, subject: rodauth(name).reset_password_email_subject
13
+ mail to: @account.email, subject: @rodauth.reset_password_email_subject
14
14
  end
15
15
 
16
16
  def verify_login_change(name, account_id, key)
17
- @email_link = email_link(name, :verify_login_change, account_id, key)
18
- @account = find_account(name, account_id)
17
+ @rodauth = rodauth(name, account_id) { @verify_login_change_key_value = key }
18
+ @account = @rodauth.rails_account
19
19
  @new_email = @account.login_change_key.login
20
20
 
21
- mail to: @new_email, subject: rodauth(name).verify_login_change_email_subject
21
+ mail to: @new_email, subject: @rodauth.verify_login_change_email_subject
22
22
  end
23
23
 
24
24
  def password_changed(name, account_id)
25
- @account = find_account(name, account_id)
25
+ @rodauth = rodauth(name, account_id)
26
+ @account = @rodauth.rails_account
26
27
 
27
- mail to: @account.email, subject: rodauth(name).password_changed_email_subject
28
+ mail to: @account.email, subject: @rodauth.password_changed_email_subject
28
29
  end
29
30
 
31
+ # def reset_password_notify(name, account_id)
32
+ # @rodauth = rodauth(name, account_id)
33
+ # @account = @rodauth.rails_account
34
+
35
+ # mail to: @account.email, subject: @rodauth.reset_password_notify_email_subject
36
+ # end
37
+
30
38
  # def email_auth(name, account_id, key)
31
- # @email_link = email_link(name, :email_auth, account_id, key)
32
- # @account = find_account(name, account_id)
39
+ # @rodauth = rodauth(name, account_id) { @email_auth_key_value = key }
40
+ # @account = @rodauth.rails_account
33
41
 
34
- # mail to: @account.email, subject: rodauth(name).email_auth_email_subject
42
+ # mail to: @account.email, subject: @rodauth.email_auth_email_subject
35
43
  # end
36
44
 
37
45
  # def unlock_account(name, account_id, key)
38
- # @email_link = email_link(name, :unlock_account, account_id, key)
39
- # @account = find_account(name, account_id)
46
+ # @rodauth = rodauth(name, account_id) { @unlock_account_key_value = key }
47
+ # @account = @rodauth.rails_account
40
48
 
41
- # mail to: @account.email, subject: rodauth(name).unlock_account_email_subject
49
+ # mail to: @account.email, subject: @rodauth.unlock_account_email_subject
42
50
  # end
43
51
 
44
52
  private
45
53
 
46
- def find_account(_name, account_id)
47
- <% if defined?(ActiveRecord::Railtie) -%>
48
- Account.find(account_id)
49
- <% else -%>
50
- Account.with_pk!(account_id)
51
- <% end -%>
52
- end
53
-
54
- def email_link(name, action, account_id, key)
55
- instance = rodauth(name)
56
- instance.instance_variable_set(:@account, { id: account_id })
57
- instance.instance_variable_set(:"@#{action}_key_value", key)
58
- instance.public_send(:"#{action}_email_link")
59
- end
60
-
61
- def rodauth(name)
62
- RodauthApp.rodauth(name).allocate
54
+ def rodauth(name, account_id, &block)
55
+ instance = RodauthApp.rodauth(name).allocate
56
+ instance.instance_eval { @account = account_ds(account_id).first! }
57
+ instance.instance_eval(&block) if block
58
+ instance
63
59
  end
64
60
  end
@@ -76,6 +76,9 @@ class RodauthMain < Rodauth::Rails::Auth
76
76
  create_password_changed_email do
77
77
  RodauthMailer.password_changed(self.class.configuration_name, account_id)
78
78
  end
79
+ # create_reset_password_notify_email do
80
+ # RodauthMailer.reset_password_notify(self.class.configuration_name, account_id)
81
+ # end
79
82
  # create_email_auth_email do
80
83
  # RodauthMailer.email_auth(self.class.configuration_name, account_id, email_auth_key_value)
81
84
  # end
@@ -2,7 +2,7 @@
2
2
  <% if rodauth.features.include?(:active_sessions) %>
3
3
  <div class="form-group mb-3">
4
4
  <div class="form-check">
5
- <%= form.check_box rodauth.global_logout_param, id: "global-logout", class: "form-check-input" %>
5
+ <%= form.check_box rodauth.global_logout_param, id: "global-logout", class: "form-check-input", include_hidden: false %>
6
6
  <%= form.label "global-logout", rodauth.global_logout_label, class: "form-check-label" %>
7
7
  </div>
8
8
  </div>
@@ -0,0 +1,5 @@
1
+ <%= form_with url: rodauth.email_auth_request_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2
+ <%= form.hidden_field rodauth.login_param, value: params[rodauth.login_param] %>
3
+
4
+ <%= form.submit rodauth.email_auth_request_button, class: "w-full px-8 py-3 cursor-pointer font-semibold text-sm rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-600 dark:bg-emerald-400 dark:hover:bg-emerald-500 dark:text-gray-900 dark:focus:ring-emerald-400 dark:focus:ring-offset-current" %>
5
+ <% end %>
@@ -0,0 +1,24 @@
1
+ <%= form_with url: rodauth.login_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2
+ <% if rodauth.skip_login_field_on_login? %>
3
+ <div class="mb-6">
4
+ <%= form.label "login", rodauth.login_label, class: "block text-sm font-semibold" %>
5
+ <%= form.email_field rodauth.login_param, value: params[rodauth.login_param], id: "login", readonly: true, class: "mt-2 text-sm w-full py-2 px-0 bg-inherit border-transparent focus:ring-0 focus:border-transparent" %>
6
+ </div>
7
+ <% else %>
8
+ <div class="mb-6">
9
+ <%= form.label "login", rodauth.login_label, class: "block text-sm font-semibold" %>
10
+ <%= form.email_field rodauth.login_param, value: params[rodauth.login_param], id: "login", autocomplete: "email", required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.login_param) ? "border-red-600 focus:ring-red-600 focus:border-red-600 dark:border-red-400 dark:focus:ring-red-400" : "border-gray-300 dark:border-gray-700 dark:focus:border-emerald-400 dark:focus:ring-emerald-400" }", aria: ({ invalid: true, describedby: "login_error_message" } if rodauth.field_error(rodauth.login_param)) %>
11
+ <%= content_tag(:span, rodauth.field_error(rodauth.login_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "login_error_message") if rodauth.field_error(rodauth.login_param) %>
12
+ </div>
13
+ <% end %>
14
+
15
+ <% unless rodauth.skip_password_field_on_login? %>
16
+ <div class="mb-6">
17
+ <%= form.label "password", rodauth.password_label, class: "block text-sm font-semibold" %>
18
+ <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.password_param) ? "border-red-600 focus:ring-red-600 focus:border-red-600 dark:border-red-400 dark:focus:ring-red-400" : "border-gray-300 dark:border-gray-700 dark:focus:border-emerald-400 dark:focus:ring-emerald-400" }", aria: ({ invalid: true, describedby: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
19
+ <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
20
+ </div>
21
+ <% end %>
22
+
23
+ <%= form.submit rodauth.login_button, class: "w-full px-8 py-3 cursor-pointer font-semibold text-sm rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-600 dark:bg-emerald-400 dark:hover:bg-emerald-500 dark:text-gray-900 dark:focus:ring-emerald-400 dark:focus:ring-offset-current" %>
24
+ <% end %>
@@ -0,0 +1,7 @@
1
+ <% unless rodauth.login_form_footer_links.empty? %>
2
+ <ul>
3
+ <% rodauth.login_form_footer_links.sort.each do |_, link, text| %>
4
+ <li><%= link_to text, link, class: "text-blue-600 rounded-sm text-sm hover:underline hover:text-blue-800 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-600 dark:text-blue-300 dark:hover:text-blue-400 dark:focus-visible:ring-2 dark:focus-visible:ring-blue-300" %></li>
5
+ <% end %>
6
+ </ul>
7
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <% if rodauth.field_error(rodauth.password_param) && rodauth.features.include?(:reset_password) %>
2
+ <%= render template: "rodauth/reset_password_request", layout: false %>
3
+ <% end %>
@@ -0,0 +1,16 @@
1
+ <div>
2
+ <% if rodauth.recovery_codes.any? %>
3
+ <div class="text-sm px-4 py-3 bg-white rounded-lg border shadow-lg grid grid-cols-2 gap-y-2 gap-x-4 dark:bg-gray-900 dark:text-gray-200">
4
+ <% rodauth.recovery_codes.each do |code| %>
5
+ <div class="font-mono"><%= code %></div>
6
+ <% end %>
7
+ </div>
8
+ <% end %>
9
+
10
+ <% if rodauth.can_add_recovery_codes? %>
11
+ <div class="text-2xl font-medium mt-6 mb-4">
12
+ <%== rodauth.add_recovery_codes_heading %>
13
+ </div>
14
+ <%= render template: "rodauth/recovery_codes", layout: false %>
15
+ <% end %>
16
+ </div>
@@ -0,0 +1,25 @@
1
+ <%= form_with url: rodauth.change_login_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2
+ <div class="mb-6">
3
+ <%= form.label "login", rodauth.login_label, class: "block text-sm font-semibold" %>
4
+ <%= form.email_field rodauth.login_param, value: params[rodauth.login_param], id: "login", autocomplete: "email", required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.login_param) ? "border-red-600 focus:ring-red-600 focus:border-red-600 dark:border-red-400 dark:focus:ring-red-400" : "border-gray-300 dark:border-gray-700 dark:focus:border-emerald-400 dark:focus:ring-emerald-400" }", aria: ({ invalid: true, describedby: "login_error_message" } if rodauth.field_error(rodauth.login_param)) %>
5
+ <%= content_tag(:span, rodauth.field_error(rodauth.login_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "login_error_message") if rodauth.field_error(rodauth.login_param) %>
6
+ </div>
7
+
8
+ <% if rodauth.require_login_confirmation? %>
9
+ <div class="mb-6">
10
+ <%= form.label "login-confirm", rodauth.login_confirm_label, class: "block text-sm font-semibold" %>
11
+ <%= form.email_field rodauth.login_confirm_param, value: params[rodauth.login_confirm_param], id: "login-confirm", autocomplete: "email", required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.login_confirm_param) ? "border-red-600 focus:ring-red-600 focus:border-red-600 dark:border-red-400 dark:focus:ring-red-400" : "border-gray-300 dark:border-gray-700 dark:focus:border-emerald-400 dark:focus:ring-emerald-400" }", aria: ({ invalid: true, describedby: "login-confirm_error_message" } if rodauth.field_error(rodauth.login_confirm_param)) %>
12
+ <%= content_tag(:span, rodauth.field_error(rodauth.login_confirm_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "login-confirm_error_message") if rodauth.field_error(rodauth.login_confirm_param) %>
13
+ </div>
14
+ <% end %>
15
+
16
+ <% if rodauth.change_login_requires_password? %>
17
+ <div class="mb-6">
18
+ <%= form.label "password", rodauth.password_label, class: "block text-sm font-semibold" %>
19
+ <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.password_param) ? "border-red-600 focus:ring-red-600 focus:border-red-600 dark:border-red-400 dark:focus:ring-red-400" : "border-gray-300 dark:border-gray-700 dark:focus:border-emerald-400 dark:focus:ring-emerald-400" }", aria: ({ invalid: true, describedby: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
20
+ <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
21
+ </div>
22
+ <% end %>
23
+
24
+ <%= form.submit rodauth.change_login_button, class: "w-full px-8 py-3 cursor-pointer font-semibold text-sm rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-600 dark:bg-emerald-400 dark:hover:bg-emerald-500 dark:text-gray-900 dark:focus:ring-emerald-400 dark:focus:ring-offset-current" %>
25
+ <% end %>
@@ -0,0 +1,25 @@
1
+ <%= form_with url: rodauth.change_password_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2
+ <% if rodauth.change_password_requires_password? %>
3
+ <div class="mb-6">
4
+ <%= form.label "password", rodauth.password_label, class: "block text-sm font-semibold" %>
5
+ <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.password_param) ? "border-red-600 focus:ring-red-600 focus:border-red-600 dark:border-red-400 dark:focus:ring-red-400" : "border-gray-300 dark:border-gray-700 dark:focus:border-emerald-400 dark:focus:ring-emerald-400" }", aria: ({ invalid: true, describedby: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
6
+ <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
7
+ </div>
8
+ <% end %>
9
+
10
+ <div class="mb-6">
11
+ <%= form.label "new-password", rodauth.new_password_label, class: "block text-sm font-semibold" %>
12
+ <%= form.password_field rodauth.new_password_param, value: "", id: "new-password", autocomplete: "new-password", required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.new_password_param) ? "border-red-600 focus:ring-red-600 focus:border-red-600 dark:border-red-400 dark:focus:ring-red-400" : "border-gray-300 dark:border-gray-700 dark:focus:border-emerald-400 dark:focus:ring-emerald-400" }", aria: ({ invalid: true, describedby: "new-password_error_message" } if rodauth.field_error(rodauth.new_password_param)) %>
13
+ <%= content_tag(:span, rodauth.field_error(rodauth.new_password_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "new-password_error_message") if rodauth.field_error(rodauth.new_password_param) %>
14
+ </div>
15
+
16
+ <% if rodauth.require_password_confirmation? %>
17
+ <div class="mb-6">
18
+ <%= form.label "password-confirm", rodauth.password_confirm_label, class: "block text-sm font-semibold" %>
19
+ <%= form.password_field rodauth.password_confirm_param, value: "", id: "password-confirm", autocomplete: "new-password", required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.password_confirm_param) ? "border-red-600 focus:ring-red-600 focus:border-red-600 dark:border-red-400 dark:focus:ring-red-400" : "border-gray-300 dark:border-gray-700 dark:focus:border-emerald-400 dark:focus:ring-emerald-400" }", aria: ({ invalid: true, describedby: "password-confirm_error_message" } if rodauth.field_error(rodauth.password_confirm_param)) %>
20
+ <%= content_tag(:span, rodauth.field_error(rodauth.password_confirm_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "password-confirm_error_message") if rodauth.field_error(rodauth.password_confirm_param) %>
21
+ </div>
22
+ <% end %>
23
+
24
+ <%= form.submit rodauth.change_password_button, class: "w-full px-8 py-3 cursor-pointer font-semibold text-sm rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-600 dark:bg-emerald-400 dark:hover:bg-emerald-500 dark:text-gray-900 dark:focus:ring-emerald-400 dark:focus:ring-offset-current" %>
25
+ <% end %>
@@ -0,0 +1,11 @@
1
+ <%= form_with url: rodauth.close_account_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2
+ <% if rodauth.close_account_requires_password? %>
3
+ <div class="mb-6">
4
+ <%= form.label "password", rodauth.password_label, class: "block text-sm font-semibold" %>
5
+ <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.password_param) ? "border-red-600 focus:ring-red-600 focus:border-red-600 dark:border-red-400 dark:focus:ring-red-400" : "border-gray-300 dark:border-gray-700 dark:focus:border-emerald-400 dark:focus:ring-emerald-400" }", aria: ({ invalid: true, describedby: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
6
+ <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
7
+ </div>
8
+ <% end %>
9
+
10
+ <%= form.submit rodauth.close_account_button, class: "w-full px-8 py-3 cursor-pointer font-semibold text-sm rounded-md text-white bg-red-600 hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-600 dark:bg-red-400 dark:hover:bg-red-500 dark:text-gray-900 dark:focus:ring-red-400 dark:focus:ring-offset-current" %>
11
+ <% end %>
@@ -0,0 +1,19 @@
1
+ <%= form_with url: rodauth.confirm_password_path, method: :post, data: { turbo: false } do |form| %>
2
+ <div class="dark:bg-gray-900 flex items-center justify-center h-screen">
3
+ <div class="max-w-sm">
4
+ <h2 class="mb-3 text-3xl font-semibold text-center dark:text-gray-100">
5
+ <%= rodauth.confirm_password_page_title %>
6
+ </h2>
7
+ <div class="mx-4">
8
+ <div class="py-2">
9
+ <%= form.label "password", rodauth.password_label, class: "text-sm text-gray-400" %>
10
+ <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "w-full px-3 py-2 border rounded-md border-gray-400 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100 focus:dark:border-emerald-400 focus:dark:bg-gray-700 invalid:border-red-500 invalid:text-red-600 focus:invalid:border-red-500 focus:invalid:ring-red-500 valid:border-green-500 #{"border rounded-md border-red-500" if rodauth.field_error(rodauth.password_param)}", aria: ({ invalid: true, describedby: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
11
+ <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "text-red-800 text-xs mt-1 ml-1", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
12
+ </div>
13
+ </div>
14
+ <div class="py-2">
15
+ <%= form.submit rodauth.confirm_password_button, class: "w-full px-8 py-2 font-semibold rounded-md w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:bg-emerald-400 dark:text-gray-900" %>
16
+ </div>
17
+ </div>
18
+ </div>
19
+ <% end %>
@@ -0,0 +1,33 @@
1
+ <%= form_with url: rodauth.create_account_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2
+ <div class="mb-6">
3
+ <%= form.label "login", rodauth.login_label, class: "block text-sm font-semibold" %>
4
+ <%= form.email_field rodauth.login_param, value: params[rodauth.login_param], id: "login", autocomplete: "email", required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.login_param) ? "border-red-600 focus:ring-red-600 focus:border-red-600 dark:border-red-400 dark:focus:ring-red-400" : "border-gray-300 dark:border-gray-700 dark:focus:border-emerald-400 dark:focus:ring-emerald-400" }", aria: ({ invalid: true, describedby: "login_error_message" } if rodauth.field_error(rodauth.login_param)) %>
5
+ <%= content_tag(:span, rodauth.field_error(rodauth.login_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "login_error_message") if rodauth.field_error(rodauth.login_param) %>
6
+ </div>
7
+
8
+ <% if rodauth.require_login_confirmation? %>
9
+ <div class="mb-6">
10
+ <%= form.label "login-confirm", rodauth.login_confirm_label, class: "block text-sm font-semibold" %>
11
+ <%= form.email_field rodauth.login_confirm_param, value: params[rodauth.login_confirm_param], id: "login-confirm", autocomplete: "email", required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.login_confirm_param) ? "border-red-600 focus:ring-red-600 focus:border-red-600 dark:border-red-400 dark:focus:ring-red-400" : "border-gray-300 dark:border-gray-700 dark:focus:border-emerald-400 dark:focus:ring-emerald-400" }", aria: ({ invalid: true, describedby: "login-confirm_error_message" } if rodauth.field_error(rodauth.login_confirm_param)) %>
12
+ <%= content_tag(:span, rodauth.field_error(rodauth.login_confirm_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "login-confirm_error_message") if rodauth.field_error(rodauth.login_confirm_param) %>
13
+ </div>
14
+ <% end %>
15
+
16
+ <% if rodauth.create_account_set_password? %>
17
+ <div class="mb-6">
18
+ <%= form.label "password", rodauth.password_label, class: "block text-sm font-semibold" %>
19
+ <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.password_param) ? "border-red-600 focus:ring-red-600 focus:border-red-600 dark:border-red-400 dark:focus:ring-red-400" : "border-gray-300 dark:border-gray-700 dark:focus:border-emerald-400 dark:focus:ring-emerald-400" }", aria: ({ invalid: true, describedby: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
20
+ <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
21
+ </div>
22
+
23
+ <% if rodauth.require_password_confirmation? %>
24
+ <div class="mb-6">
25
+ <%= form.label "password-confirm", rodauth.password_confirm_label, class: "block text-sm font-semibold" %>
26
+ <%= form.password_field rodauth.password_confirm_param, value: "", id: "password-confirm", autocomplete: "new-password", required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.password_confirm_param) ? "border-red-600 focus:ring-red-600 focus:border-red-600 dark:border-red-400 dark:focus:ring-red-400" : "border-gray-300 dark:border-gray-700 dark:focus:border-emerald-400 dark:focus:ring-emerald-400" }", aria: ({ invalid: true, describedby: "password-confirm_error_message" } if rodauth.field_error(rodauth.password_confirm_param)) %>
27
+ <%= content_tag(:span, rodauth.field_error(rodauth.password_confirm_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "password-confirm_error_message") if rodauth.field_error(rodauth.password_confirm_param) %>
28
+ </div>
29
+ <% end %>
30
+ <% end %>
31
+
32
+ <%= form.submit rodauth.create_account_button, class: "w-full px-8 py-3 cursor-pointer font-semibold text-sm rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-600 dark:bg-emerald-400 dark:hover:bg-emerald-500 dark:text-gray-900 dark:focus:ring-emerald-400 dark:focus:ring-offset-current" %>
33
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <%= form_with url: rodauth.email_auth_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2
+ <%= form.submit rodauth.login_button, class: "w-full px-8 py-3 cursor-pointer font-semibold text-sm rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-600 dark:bg-emerald-400 dark:hover:bg-emerald-500 dark:text-gray-900 dark:focus:ring-emerald-400 dark:focus:ring-offset-current" %>
3
+ <% end %>
@@ -0,0 +1,5 @@
1
+ <div class="flex-1 space-y-4">
2
+ <%= render "login_form_header" %>
3
+ <%= render "login_form" %>
4
+ <%= render "login_form_footer" %>
5
+ </div>
@@ -0,0 +1,10 @@
1
+ <%= form_with url: rodauth.logout_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2
+ <% if rodauth.features.include?(:active_sessions) %>
3
+ <div class="mb-6 flex items-center space-x-2">
4
+ <%= form.check_box rodauth.global_logout_param, id: "global-logout", class: "rounded dark:bg-gray-900 dark:border-gray-600 dark:checked:bg-current dark:checked:border-current dark:checked:text-emerald-400 dark:focus:ring-emerald-400 dark:focus:ring-offset-gray-900", include_hidden: false %>
5
+ <%= form.label "global-logout", rodauth.global_logout_label, class: "text-sm" %>
6
+ </div>
7
+ <% end %>
8
+
9
+ <%= form.submit rodauth.logout_button, class: "w-full px-8 py-3 cursor-pointer font-semibold text-sm rounded-md bg-yellow-300 hover:bg-yellow-400 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-yellow-300 dark:bg-violet-400 dark:hover:bg-violet-500 dark:text-gray-900 dark:focus:ring-violet-400 dark:focus:ring-offset-current" %>
10
+ <% end %>
@@ -0,0 +1,5 @@
1
+ <div class="flex-1 space-y-4">
2
+ <%= render "login_form_header" %>
3
+ <%== rodauth.render_multi_phase_login_forms %>
4
+ <%= render "login_form_footer" %>
5
+ </div>
@@ -0,0 +1,9 @@
1
+ <%= form_with url: rodauth.otp_auth_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2
+ <div class="mb-6">
3
+ <%= form.label "otp-auth-code", rodauth.otp_auth_label, class: "block text-sm font-semibold" %>
4
+ <%= form.text_field rodauth.otp_auth_param, value: "", id: "otp-auth-code", autocomplete: "off", inputmode: "numeric", required: true, class: "mt-2 text-sm w-1/2 px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.otp_auth_param) ? "border-red-600 focus:ring-red-600 focus:border-red-600 dark:border-red-400 dark:focus:ring-red-400" : "border-gray-300 dark:border-gray-700 dark:focus:border-emerald-400 dark:focus:ring-emerald-400" }", aria: ({ invalid: true, describedby: "otp-auth-code_error_message" } if rodauth.field_error(rodauth.otp_auth_param)) %>
5
+ <%= content_tag(:span, rodauth.field_error(rodauth.otp_auth_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "otp-auth-code_error_message") if rodauth.field_error(rodauth.otp_auth_param) %>
6
+ </div>
7
+
8
+ <%= form.submit rodauth.otp_auth_button, class: "w-full px-8 py-3 cursor-pointer font-semibold text-sm rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-600 dark:bg-emerald-400 dark:hover:bg-emerald-500 dark:text-gray-900 dark:focus:ring-emerald-400 dark:focus:ring-offset-current" %>
9
+ <% end %>
@@ -0,0 +1,11 @@
1
+ <%= form_with url: rodauth.otp_disable_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2
+ <% if rodauth.two_factor_modifications_require_password? %>
3
+ <div class="mb-6">
4
+ <%= form.label "password", rodauth.password_label, class: "block text-sm font-semibold" %>
5
+ <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.password_param) ? "border-red-600 focus:ring-red-600 focus:border-red-600 dark:border-red-400 dark:focus:ring-red-400" : "border-gray-300 dark:border-gray-700 dark:focus:border-emerald-400 dark:focus:ring-emerald-400" }", aria: ({ invalid: true, describedby: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
6
+ <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
7
+ </div>
8
+ <% end %>
9
+
10
+ <%= form.submit rodauth.otp_disable_button, class: "w-full px-8 py-3 cursor-pointer font-semibold text-sm rounded-md bg-yellow-300 hover:bg-yellow-400 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-yellow-300 dark:bg-violet-400 dark:hover:bg-violet-500 dark:text-gray-900 dark:focus:ring-violet-400 dark:focus:ring-offset-current" %>
11
+ <% end %>
@@ -0,0 +1,32 @@
1
+ <%= form_with url: rodauth.otp_setup_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2
+ <%= form.hidden_field rodauth.otp_setup_param, value: rodauth.otp_user_key, id: "otp-key" %>
3
+ <%= form.hidden_field rodauth.otp_setup_raw_param, value: rodauth.otp_key, id: "otp-hmac-secret" if rodauth.otp_keys_use_hmac? %>
4
+
5
+ <div class="mb-6 w-48">
6
+ <%== rodauth.otp_qr_code %>
7
+ </div>
8
+
9
+ <dl class="mb-6 text-sm">
10
+ <dt class="font-semibold"><%= rodauth.otp_secret_label %>:</dt>
11
+ <dd><%= rodauth.otp_user_key %></dd>
12
+
13
+ <dt class="font-semibold mt-2"><%= rodauth.otp_provisioning_uri_label %>:</dt>
14
+ <dd><%= rodauth.otp_provisioning_uri %></dd>
15
+ </dl>
16
+
17
+ <% if rodauth.two_factor_modifications_require_password? %>
18
+ <div class="mb-6">
19
+ <%= form.label "password", rodauth.password_label, class: "block text-sm font-semibold" %>
20
+ <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.password_param) ? "border-red-600 focus:ring-red-600 focus:border-red-600 dark:border-red-400 dark:focus:ring-red-400" : "border-gray-300 dark:border-gray-700 dark:focus:border-emerald-400 dark:focus:ring-emerald-400" }", aria: ({ invalid: true, describedby: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
21
+ <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
22
+ </div>
23
+ <% end %>
24
+
25
+ <div class="mb-6">
26
+ <%= form.label "otp-auth-code", rodauth.otp_auth_label, class: "block text-sm font-semibold" %>
27
+ <%= form.text_field rodauth.otp_auth_param, value: "", id: "otp-auth-code", autocomplete: "off", inputmode: "numeric", required: true, class: "mt-2 text-sm w-1/2 px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.otp_auth_param) ? "border-red-600 focus:ring-red-600 focus:border-red-600 dark:border-red-400 dark:focus:ring-red-400" : "border-gray-300 dark:border-gray-700 dark:focus:border-emerald-400 dark:focus:ring-emerald-400" }", aria: ({ invalid: true, describedby: "otp-auth-code_error_message" } if rodauth.field_error(rodauth.otp_auth_param)) %>
28
+ <%= content_tag(:span, rodauth.field_error(rodauth.otp_auth_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "otp-auth-code_error_message") if rodauth.field_error(rodauth.otp_auth_param) %>
29
+ </div>
30
+
31
+ <%= form.submit rodauth.otp_setup_button, class: "w-full px-8 py-3 cursor-pointer font-semibold text-sm rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-600 dark:bg-emerald-400 dark:hover:bg-emerald-500 dark:text-gray-900 dark:focus:ring-emerald-400 dark:focus:ring-offset-current" %>
32
+ <% end %>
@@ -0,0 +1,9 @@
1
+ <%= form_with url: rodauth.recovery_auth_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2
+ <div class="mb-6">
3
+ <%= form.label "recovery-code", rodauth.recovery_codes_label, class: "block text-sm font-semibold" %>
4
+ <%= form.text_field rodauth.recovery_codes_param, value: "", id: "recovery-code", autocomplete: "off", required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.recovery_codes_param) ? "border-red-600 focus:ring-red-600 focus:border-red-600 dark:border-red-400 dark:focus:ring-red-400" : "border-gray-300 dark:border-gray-700 dark:focus:border-emerald-400 dark:focus:ring-emerald-400" }", aria: ({ invalid: true, describedby: "recovery-code_error_message" } if rodauth.field_error(rodauth.recovery_codes_param)) %>
5
+ <%= content_tag(:span, rodauth.field_error(rodauth.recovery_codes_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "recovery-code_error_message") if rodauth.field_error(rodauth.recovery_codes_param) %>
6
+ </div>
7
+
8
+ <%= form.submit rodauth.recovery_auth_button, class: "w-full px-8 py-3 cursor-pointer font-semibold text-sm rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-600 dark:bg-emerald-400 dark:hover:bg-emerald-500 dark:text-gray-900 dark:focus:ring-emerald-400 dark:focus:ring-offset-current" %>
9
+ <% end %>
@@ -0,0 +1,11 @@
1
+ <%= form_with url: rodauth.recovery_codes_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2
+ <% if rodauth.two_factor_modifications_require_password? %>
3
+ <div class="mb-6">
4
+ <%= form.label "password", rodauth.password_label, class: "block text-sm font-semibold" %>
5
+ <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.password_param) ? "border-red-600 focus:ring-red-600 focus:border-red-600 dark:border-red-400 dark:focus:ring-red-400" : "border-gray-300 dark:border-gray-700 dark:focus:border-emerald-400 dark:focus:ring-emerald-400" }", aria: ({ invalid: true, describedby: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
6
+ <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
7
+ </div>
8
+ <% end %>
9
+
10
+ <%= form.submit rodauth.recovery_codes_button || rodauth.view_recovery_codes_button, name: (rodauth.add_recovery_codes_param if rodauth.recovery_codes_button), class: "w-full px-8 py-3 cursor-pointer font-semibold text-sm rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-600 dark:bg-emerald-400 dark:hover:bg-emerald-500 dark:text-gray-900 dark:focus:ring-emerald-400 dark:focus:ring-offset-current" %>
11
+ <% end %>
@@ -0,0 +1,18 @@
1
+ <%= form_with url: rodauth.remember_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2
+ <fieldset class="mb-6 space-y-2">
3
+ <div class="flex items-center space-x-2">
4
+ <%= form.radio_button rodauth.remember_param, rodauth.remember_remember_param_value, id: "remember-remember", class: "dark:bg-gray-900 dark:border-gray-600 dark:checked:bg-current dark:checked:border-current dark:checked:text-emerald-400 dark:focus:ring-emerald-400 dark:focus:ring-offset-gray-900" %>
5
+ <%= form.label "remember-remember", rodauth.remember_remember_label, class: "text-sm" %>
6
+ </div>
7
+ <div class="flex items-center space-x-2">
8
+ <%= form.radio_button rodauth.remember_param, rodauth.remember_forget_param_value, id: "remember-forget", class: "dark:bg-gray-900 dark:border-gray-600 dark:checked:bg-current dark:checked:border-current dark:checked:text-emerald-400 dark:focus:ring-emerald-400 dark:focus:ring-offset-gray-900" %>
9
+ <%= form.label "remember-forget", rodauth.remember_forget_label, class: "text-sm" %>
10
+ </div>
11
+ <div class="flex items-center space-x-2">
12
+ <%= form.radio_button rodauth.remember_param, rodauth.remember_disable_param_value, id: "remember-disable", class: "dark:bg-gray-900 dark:border-gray-600 dark:checked:bg-current dark:checked:border-current dark:checked:text-emerald-400 dark:focus:ring-emerald-400 dark:focus:ring-offset-gray-900" %>
13
+ <%= form.label "remember-disable", rodauth.remember_disable_label, class: "text-sm" %>
14
+ </div>
15
+ </fieldset>
16
+
17
+ <%= form.submit rodauth.remember_button, class: "w-full px-8 py-3 cursor-pointer font-semibold text-sm rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-600 dark:bg-emerald-400 dark:hover:bg-emerald-500 dark:text-gray-900 dark:focus:ring-emerald-400 dark:focus:ring-offset-current" %>
18
+ <% end %>
@@ -0,0 +1,17 @@
1
+ <%= form_with url: rodauth.reset_password_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2
+ <div class="mb-6">
3
+ <%= form.label "password", rodauth.password_label, class: "block text-sm font-semibold" %>
4
+ <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.password_param) ? "border-red-600 focus:ring-red-600 focus:border-red-600 dark:border-red-400 dark:focus:ring-red-400" : "border-gray-300 dark:border-gray-700 dark:focus:border-emerald-400 dark:focus:ring-emerald-400" }", aria: ({ invalid: true, describedby: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
5
+ <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
6
+ </div>
7
+
8
+ <% if rodauth.require_password_confirmation? %>
9
+ <div class="mb-6">
10
+ <%= form.label "password-confirm", rodauth.password_confirm_label, class: "block text-sm font-semibold" %>
11
+ <%= form.password_field rodauth.password_confirm_param, value: "", id: "password-confirm", autocomplete: "new-password", required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.password_confirm_param) ? "border-red-600 focus:ring-red-600 focus:border-red-600 dark:border-red-400 dark:focus:ring-red-400" : "border-gray-300 dark:border-gray-700 dark:focus:border-emerald-400 dark:focus:ring-emerald-400" }", aria: ({ invalid: true, describedby: "password-confirm_error_message" } if rodauth.field_error(rodauth.password_confirm_param)) %>
12
+ <%= content_tag(:span, rodauth.field_error(rodauth.password_confirm_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "password-confirm_error_message") if rodauth.field_error(rodauth.password_confirm_param) %>
13
+ </div>
14
+ <% end %>
15
+
16
+ <%= form.submit rodauth.reset_password_button, class: "w-full px-8 py-3 cursor-pointer font-semibold text-sm rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-600 dark:bg-emerald-400 dark:hover:bg-emerald-500 dark:text-gray-900 dark:focus:ring-emerald-400 dark:focus:ring-offset-current" %>
17
+ <% end %>
@@ -0,0 +1,17 @@
1
+ <%= form_with url: rodauth.reset_password_request_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2
+ <div class="mb-4 text-sm">
3
+ <%== rodauth.reset_password_explanatory_text %>
4
+ </div>
5
+
6
+ <% if params[rodauth.login_param] && !rodauth.field_error(rodauth.login_param) %>
7
+ <%= form.hidden_field rodauth.login_param, value: params[rodauth.login_param] %>
8
+ <% else %>
9
+ <div class="mb-6">
10
+ <%= form.label "login", rodauth.login_label, class: "block text-sm font-semibold" %>
11
+ <%= form.email_field rodauth.login_param, value: params[rodauth.login_param], id: "login", autocomplete: "email", required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.login_param) ? "border-red-600 focus:ring-red-600 focus:border-red-600 dark:border-red-400 dark:focus:ring-red-400" : "border-gray-300 dark:border-gray-700 dark:focus:border-emerald-400 dark:focus:ring-emerald-400" }", aria: ({ invalid: true, describedby: "login_error_message" } if rodauth.field_error(rodauth.login_param)) %>
12
+ <%= content_tag(:span, rodauth.field_error(rodauth.login_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "login_error_message") if rodauth.field_error(rodauth.login_param) %>
13
+ </div>
14
+ <% end %>
15
+
16
+ <%= form.submit rodauth.reset_password_request_button, class: "w-full px-8 py-3 cursor-pointer font-semibold text-sm rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-600 dark:bg-emerald-400 dark:hover:bg-emerald-500 dark:text-gray-900 dark:focus:ring-emerald-400 dark:focus:ring-offset-current" %>
17
+ <% end %>
@@ -0,0 +1,9 @@
1
+ <%= form_with url: rodauth.sms_auth_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2
+ <div class="mb-6">
3
+ <%= form.label "sms-code", rodauth.sms_code_label, class: "block text-sm font-semibold" %>
4
+ <%= form.text_field rodauth.sms_code_param, value: "", id: "sms-code", autocomplete: "one-time-code", inputmode: "numeric", required: true, class: "mt-2 text-sm w-1/2 px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.sms_code_param) ? "border-red-600 focus:ring-red-600 focus:border-red-600 dark:border-red-400 dark:focus:ring-red-400" : "border-gray-300 dark:border-gray-700 dark:focus:border-emerald-400 dark:focus:ring-emerald-400" }", aria: ({ invalid: true, describedby: "sms-code_error_message" } if rodauth.field_error(rodauth.sms_code_param)) %>
5
+ <%= content_tag(:span, rodauth.field_error(rodauth.sms_code_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "sms-code_error_message") if rodauth.field_error(rodauth.sms_code_param) %>
6
+ </div>
7
+
8
+ <%= form.submit rodauth.sms_auth_button, class: "w-full px-8 py-3 cursor-pointer font-semibold text-sm rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-600 dark:bg-emerald-400 dark:hover:bg-emerald-500 dark:text-gray-900 dark:focus:ring-emerald-400 dark:focus:ring-offset-current" %>
9
+ <% end %>
@@ -0,0 +1,9 @@
1
+ <%= form_with url: rodauth.sms_confirm_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2
+ <div class="mb-6">
3
+ <%= form.label "sms-code", rodauth.sms_code_label, class: "block text-sm font-semibold" %>
4
+ <%= form.text_field rodauth.sms_code_param, value: "", id: "sms-code", autocomplete: "one-time-code", inputmode: "numeric", required: true, class: "mt-2 text-sm w-1/2 px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.sms_code_param) ? "border-red-600 focus:ring-red-600 focus:border-red-600 dark:border-red-400 dark:focus:ring-red-400" : "border-gray-300 dark:border-gray-700 dark:focus:border-emerald-400 dark:focus:ring-emerald-400" }", aria: ({ invalid: true, describedby: "sms-code_error_message" } if rodauth.field_error(rodauth.sms_code_param)) %>
5
+ <%= content_tag(:span, rodauth.field_error(rodauth.sms_code_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "sms-code_error_message") if rodauth.field_error(rodauth.sms_code_param) %>
6
+ </div>
7
+
8
+ <%= form.submit rodauth.sms_confirm_button, class: "w-full px-8 py-3 cursor-pointer font-semibold text-sm rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-600 dark:bg-emerald-400 dark:hover:bg-emerald-500 dark:text-gray-900 dark:focus:ring-emerald-400 dark:focus:ring-offset-current" %>
9
+ <% end %>
@@ -0,0 +1,11 @@
1
+ <%= form_with url: rodauth.sms_disable_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2
+ <% if rodauth.two_factor_modifications_require_password? %>
3
+ <div class="mb-6">
4
+ <%= form.label "password", rodauth.password_label, class: "block text-sm font-semibold" %>
5
+ <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.password_param) ? "border-red-600 focus:ring-red-600 focus:border-red-600 dark:border-red-400 dark:focus:ring-red-400" : "border-gray-300 dark:border-gray-700 dark:focus:border-emerald-400 dark:focus:ring-emerald-400" }", aria: ({ invalid: true, describedby: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
6
+ <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
7
+ </div>
8
+ <% end %>
9
+
10
+ <%= form.submit rodauth.sms_disable_button, class: "w-full px-8 py-3 cursor-pointer font-semibold text-sm rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-600 dark:bg-emerald-400 dark:hover:bg-emerald-500 dark:text-gray-900 dark:focus:ring-emerald-400 dark:focus:ring-offset-current" %>
11
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <%= form_with url: rodauth.sms_request_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2
+ <%= form.submit rodauth.sms_request_button, class: "w-full px-8 py-3 cursor-pointer font-semibold text-sm rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-600 dark:bg-emerald-400 dark:hover:bg-emerald-500 dark:text-gray-900 dark:focus:ring-emerald-400 dark:focus:ring-offset-current" %>
3
+ <% end %>
@@ -0,0 +1,17 @@
1
+ <%= form_with url: rodauth.sms_setup_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2
+ <% if rodauth.two_factor_modifications_require_password? %>
3
+ <div class="mb-6">
4
+ <%= form.label "password", rodauth.password_label, class: "block text-sm font-semibold" %>
5
+ <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.password_param) ? "border-red-600 focus:ring-red-600 focus:border-red-600 dark:border-red-400 dark:focus:ring-red-400" : "border-gray-300 dark:border-gray-700 dark:focus:border-emerald-400 dark:focus:ring-emerald-400" }", aria: ({ invalid: true, describedby: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
6
+ <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
7
+ </div>
8
+ <% end %>
9
+
10
+ <div class="mb-6">
11
+ <%= form.label "sms-phone", rodauth.sms_phone_label, class: "block text-sm font-semibold" %>
12
+ <%= form.telephone_field rodauth.sms_phone_param, value: "", id: "sms-phone", autocomplete: "tel", required: true, class: "mt-2 text-sm w-1/2 px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.sms_phone_param) ? "border-red-600 focus:ring-red-600 focus:border-red-600 dark:border-red-400 dark:focus:ring-red-400" : "border-gray-300 dark:border-gray-700 dark:focus:border-emerald-400 dark:focus:ring-emerald-400" }", aria: ({ invalid: true, describedby: "sms-phone_error_message" } if rodauth.field_error(rodauth.sms_phone_param)) %>
13
+ <%= content_tag(:span, rodauth.field_error(rodauth.sms_phone_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "sms-phone_error_message") if rodauth.field_error(rodauth.sms_phone_param) %>
14
+ </div>
15
+
16
+ <%= form.submit rodauth.sms_setup_button, class: "w-full px-8 py-3 cursor-pointer font-semibold text-sm rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-600 dark:bg-emerald-400 dark:hover:bg-emerald-500 dark:text-gray-900 dark:focus:ring-emerald-400 dark:focus:ring-offset-current" %>
17
+ <% end %>
@@ -0,0 +1,5 @@
1
+ <ul>
2
+ <% rodauth.two_factor_auth_links.sort.each do |_, link, text| %>
3
+ <li><%= link_to text, link, class: "text-blue-600 rounded-sm text-sm hover:underline hover:text-blue-800 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-600 dark:text-blue-300 dark:hover:text-blue-400 dark:focus-visible:ring-2 dark:focus-visible:ring-blue-300" %></li>
4
+ <% end %>
5
+ </ul>
@@ -0,0 +1,11 @@
1
+ <%= form_with url: rodauth.two_factor_disable_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2
+ <% if rodauth.two_factor_modifications_require_password? %>
3
+ <div class="mb-6">
4
+ <%= form.label "password", rodauth.password_label, class: "block text-sm font-semibold" %>
5
+ <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.password_param) ? "border-red-600 focus:ring-red-600 focus:border-red-600 dark:border-red-400 dark:focus:ring-red-400" : "border-gray-300 dark:border-gray-700 dark:focus:border-emerald-400 dark:focus:ring-emerald-400" }", aria: ({ invalid: true, describedby: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
6
+ <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
7
+ </div>
8
+ <% end %>
9
+
10
+ <%= form.submit rodauth.two_factor_disable_button, class: "w-full px-8 py-3 cursor-pointer font-semibold text-sm rounded-md bg-yellow-300 hover:bg-yellow-400 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-yellow-300 dark:bg-violet-400 dark:hover:bg-violet-500 dark:text-gray-900 dark:focus:ring-violet-400 dark:focus:ring-offset-current" %>
11
+ <% end %>
@@ -0,0 +1,26 @@
1
+ <div>
2
+ <% if rodauth.two_factor_setup_links.any? %>
3
+ <div class="text-2xl font-medium">
4
+ <%== rodauth.two_factor_setup_heading %>
5
+ </div>
6
+ <ul class="mt-1">
7
+ <% rodauth.two_factor_setup_links.sort.each do |_, link, text| %>
8
+ <li><%= link_to text, link, class: "text-blue-600 rounded-sm text-sm hover:underline hover:text-blue-800 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-600 dark:text-blue-300 dark:hover:text-blue-400 dark:focus-visible:ring-2 dark:focus-visible:ring-blue-300" %></li>
9
+ <% end %>
10
+ </ul>
11
+ <% end %>
12
+
13
+ <% if rodauth.two_factor_remove_links.any? %>
14
+ <div class="text-2xl font-medium mt-4">
15
+ <%== rodauth.two_factor_remove_heading %>
16
+ </div>
17
+ <ul class="mt-1">
18
+ <% rodauth.two_factor_remove_links.sort.each do |_, link, text| %>
19
+ <li><%= link_to text, link, class: "text-blue-600 rounded-sm text-sm hover:underline hover:text-blue-800 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-600 dark:text-blue-300 dark:hover:text-blue-400 dark:focus-visible:ring-2 dark:focus-visible:ring-blue-300" %></li>
20
+ <% end %>
21
+ <% if rodauth.two_factor_remove_links.length > 1 %>
22
+ <li><%= link_to rodauth.two_factor_disable_link_text, rodauth.two_factor_disable_path, class: "text-blue-600 rounded-sm text-sm hover:underline hover:text-blue-800 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-600 dark:text-blue-300 dark:hover:text-blue-400 dark:focus-visible:ring-2 dark:focus-visible:ring-blue-300" %></li>
23
+ <% end %>
24
+ </ul>
25
+ <% end %>
26
+ </div>
@@ -0,0 +1,15 @@
1
+ <%= form_with url: rodauth.unlock_account_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2
+ <div class="mb-4 text-sm">
3
+ <%== rodauth.unlock_account_explanatory_text %>
4
+ </div>
5
+
6
+ <% if rodauth.unlock_account_requires_password? %>
7
+ <div class="mb-6">
8
+ <%= form.label "password", rodauth.password_label, class: "block text-sm font-semibold" %>
9
+ <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.password_param) ? "border-red-600 focus:ring-red-600 focus:border-red-600 dark:border-red-400 dark:focus:ring-red-400" : "border-gray-300 dark:border-gray-700 dark:focus:border-emerald-400 dark:focus:ring-emerald-400" }", aria: ({ invalid: true, describedby: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
10
+ <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
11
+ </div>
12
+ <% end %>
13
+
14
+ <%= form.submit rodauth.unlock_account_button, class: "w-full px-8 py-3 cursor-pointer font-semibold text-sm rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-600 dark:bg-emerald-400 dark:hover:bg-emerald-500 dark:text-gray-900 dark:focus:ring-emerald-400 dark:focus:ring-offset-current" %>
15
+ <% end %>
@@ -0,0 +1,9 @@
1
+ <%= form_with url: rodauth.unlock_account_request_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2
+ <div class="mb-4 text-sm">
3
+ <%== rodauth.unlock_account_request_explanatory_text %>
4
+ </div>
5
+
6
+ <%= form.hidden_field rodauth.login_param, value: params[rodauth.login_param] %>
7
+
8
+ <%= form.submit rodauth.unlock_account_request_button, class: "w-full px-8 py-3 cursor-pointer font-semibold text-sm rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-600 dark:bg-emerald-400 dark:hover:bg-emerald-500 dark:text-gray-900 dark:focus:ring-emerald-400 dark:focus:ring-offset-current" %>
9
+ <% end %>
@@ -0,0 +1,19 @@
1
+ <%= form_with url: rodauth.verify_account_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2
+ <% if rodauth.verify_account_set_password? %>
3
+ <div class="mb-6">
4
+ <%= form.label "password", rodauth.password_label, class: "block text-sm font-semibold" %>
5
+ <%= form.password_field rodauth.password_param, value: "", id: "password", autocomplete: rodauth.password_field_autocomplete_value, required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.password_param) ? "border-red-600 focus:ring-red-600 focus:border-red-600 dark:border-red-400 dark:focus:ring-red-400" : "border-gray-300 dark:border-gray-700 dark:focus:border-emerald-400 dark:focus:ring-emerald-400" }", aria: ({ invalid: true, describedby: "password_error_message" } if rodauth.field_error(rodauth.password_param)) %>
6
+ <%= content_tag(:span, rodauth.field_error(rodauth.password_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "password_error_message") if rodauth.field_error(rodauth.password_param) %>
7
+ </div>
8
+
9
+ <% if rodauth.require_password_confirmation? %>
10
+ <div class="mb-6">
11
+ <%= form.label "password-confirm", rodauth.password_confirm_label, class: "block text-sm font-semibold" %>
12
+ <%= form.password_field rodauth.password_confirm_param, value: "", id: "password-confirm", autocomplete: "new-password", required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.password_confirm_param) ? "border-red-600 focus:ring-red-600 focus:border-red-600 dark:border-red-400 dark:focus:ring-red-400" : "border-gray-300 dark:border-gray-700 dark:focus:border-emerald-400 dark:focus:ring-emerald-400" }", aria: ({ invalid: true, describedby: "password-confirm_error_message" } if rodauth.field_error(rodauth.password_confirm_param)) %>
13
+ <%= content_tag(:span, rodauth.field_error(rodauth.password_confirm_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "password-confirm_error_message") if rodauth.field_error(rodauth.password_confirm_param) %>
14
+ </div>
15
+ <% end %>
16
+ <% end %>
17
+
18
+ <%= form.submit rodauth.verify_account_button, class: "w-full px-8 py-3 cursor-pointer font-semibold text-sm rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-600 dark:bg-emerald-400 dark:hover:bg-emerald-500 dark:text-gray-900 dark:focus:ring-emerald-400 dark:focus:ring-offset-current" %>
19
+ <% end %>
@@ -0,0 +1,17 @@
1
+ <%= form_with url: rodauth.verify_account_resend_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2
+ <div class="mb-4 text-sm">
3
+ <%== rodauth.verify_account_resend_explanatory_text %>
4
+ </div>
5
+
6
+ <% if params[rodauth.login_param] %>
7
+ <%= form.hidden_field rodauth.login_param, value: params[rodauth.login_param] %>
8
+ <% else %>
9
+ <div class="mb-6">
10
+ <%= form.label "login", rodauth.login_label, class: "block text-sm font-semibold" %>
11
+ <%= form.email_field rodauth.login_param, value: params[rodauth.login_param], id: "login", autocomplete: "email", required: true, class: "mt-2 text-sm w-full px-3 py-2 border rounded-md dark:bg-gray-900 dark:text-gray-100 dark:focus:bg-gray-800 #{rodauth.field_error(rodauth.login_param) ? "border-red-600 focus:ring-red-600 focus:border-red-600 dark:border-red-400 dark:focus:ring-red-400" : "border-gray-300 dark:border-gray-700 dark:focus:border-emerald-400 dark:focus:ring-emerald-400" }", aria: ({ invalid: true, describedby: "login_error_message" } if rodauth.field_error(rodauth.login_param)) %>
12
+ <%= content_tag(:span, rodauth.field_error(rodauth.login_param), class: "block mt-1 text-red-600 text-xs dark:text-red-400", id: "login_error_message") if rodauth.field_error(rodauth.login_param) %>
13
+ </div>
14
+ <% end %>
15
+
16
+ <%= form.submit rodauth.verify_account_resend_button, class: "w-full px-8 py-3 cursor-pointer font-semibold text-sm rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-600 dark:bg-emerald-400 dark:hover:bg-emerald-500 dark:text-gray-900 dark:focus:ring-emerald-400 dark:focus:ring-offset-current" %>
17
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <%= form_with url: rodauth.verify_login_change_path, method: :post, data: { turbo: false }, class: "w-full max-w-sm" do |form| %>
2
+ <%= form.submit rodauth.verify_login_change_button, class: "w-full px-8 py-3 cursor-pointer font-semibold text-sm rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-600 dark:bg-emerald-400 dark:hover:bg-emerald-500 dark:text-gray-900 dark:focus:ring-emerald-400 dark:focus:ring-offset-current" %>
3
+ <% end %>
@@ -1,5 +1,5 @@
1
1
  Someone has requested a login link for the account with this email
2
2
  address. If you did not request a login link, please ignore this
3
3
  message. If you requested a login link, please go to
4
- <%= @email_link %>
4
+ <%= @rodauth.email_auth_email_link %>
5
5
  to login to this account.
@@ -1,5 +1,5 @@
1
1
  Someone has requested a password reset for the account with this email
2
2
  address. If you did not request a password reset, please ignore this
3
3
  message. If you requested a password reset, please go to
4
- <%= @email_link %>
4
+ <%= @rodauth.reset_password_email_link %>
5
5
  to reset the password for the account.
@@ -0,0 +1,2 @@
1
+ Someone (hopefully you) has reset the password for the account
2
+ associated to this email address.
@@ -1,5 +1,5 @@
1
- Someone has requested that the account with this email be unlocked.
1
+ Someone has requested a that the account with this email be unlocked.
2
2
  If you did not request the unlocking of this account, please ignore this
3
3
  message. If you requested the unlocking of this account, please go to
4
- <%= @email_link %>
4
+ <%= @rodauth.unlock_account_email_link %>
5
5
  to unlock this account.
@@ -1,4 +1,4 @@
1
1
  Someone has created an account with this email address. If you did not create
2
2
  this account, please ignore this message. If you created this account, please go to
3
- <%= @email_link %>
3
+ <%= @rodauth.verify_account_email_link %>
4
4
  to verify the account.
@@ -6,5 +6,5 @@ New email: <%= @new_email %>
6
6
 
7
7
  If you did not request this login change, please ignore this message. If you
8
8
  requested this login change, please go to
9
- <%= @email_link %>
9
+ <%= @rodauth.verify_login_change_email_link %>
10
10
  to verify the login change.
@@ -14,6 +14,10 @@ module Rodauth
14
14
  desc: "Generates views for all Rodauth features",
15
15
  default: false
16
16
 
17
+ class_option :css, type: :string, enum: %w[bootstrap tailwind tailwindcss],
18
+ desc: "CSS framework to generate views for",
19
+ default: "bootstrap"
20
+
17
21
  class_option :name, aliases: "-n", type: :string,
18
22
  desc: "The configuration name for which to generate views",
19
23
  default: nil
@@ -49,7 +53,7 @@ module Rodauth
49
53
  validate_features or return
50
54
 
51
55
  views.each do |view|
52
- copy_file "app/views/rodauth/#{view}.html.erb", "app/views/#{directory}/#{view}.html.erb" do |content|
56
+ copy_file view_location(view), "app/views/#{directory}/#{view}.html.erb" do |content|
53
57
  content = content.gsub("rodauth.", "rodauth(:#{configuration_name}).") if configuration_name
54
58
  content = content.gsub("rodauth/", "#{directory}/")
55
59
  content = form_helpers_compatibility(content) if ActionView.version < Gem::Version.new("5.1")
@@ -115,6 +119,19 @@ module Rodauth
115
119
  .gsub(/form\.radio_button (\S+), (\S+),/, 'radio_button_tag \1, \2, false,')
116
120
  .gsub(/form\.check_box (\S+), (.+) /, 'check_box_tag \1, "t", false, \2 ')
117
121
  end
122
+
123
+ def view_location(view)
124
+ if tailwind?
125
+ "app/views/rodauth/tailwind/#{view}.html.erb"
126
+ else
127
+ "app/views/rodauth/#{view}.html.erb"
128
+ end
129
+ end
130
+
131
+ def tailwind?
132
+ ::Rails.application.config.generators.options[:rails][:template_engine] == :tailwindcss ||
133
+ options[:css]&.downcase&.start_with?("tailwind")
134
+ end
118
135
  end
119
136
  end
120
137
  end
@@ -13,7 +13,7 @@ module Rodauth
13
13
  end
14
14
 
15
15
  def rails_account
16
- return unless logged_in?
16
+ return unless account || logged_in?
17
17
 
18
18
  account_from_session unless account
19
19
 
@@ -1,5 +1,5 @@
1
1
  module Rodauth
2
2
  module Rails
3
- VERSION = "1.6.3"
3
+ VERSION = "1.7.0"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rodauth-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.3
4
+ version: 1.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Janko Marohnić
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-11-15 00:00:00.000000000 Z
11
+ date: 2022-12-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
@@ -251,6 +251,41 @@ files:
251
251
  - lib/generators/rodauth/templates/app/views/rodauth/sms_disable.html.erb
252
252
  - lib/generators/rodauth/templates/app/views/rodauth/sms_request.html.erb
253
253
  - lib/generators/rodauth/templates/app/views/rodauth/sms_setup.html.erb
254
+ - lib/generators/rodauth/templates/app/views/rodauth/tailwind/_email_auth_request_form.html.erb
255
+ - lib/generators/rodauth/templates/app/views/rodauth/tailwind/_login_form.html.erb
256
+ - lib/generators/rodauth/templates/app/views/rodauth/tailwind/_login_form_footer.html.erb
257
+ - lib/generators/rodauth/templates/app/views/rodauth/tailwind/_login_form_header.html.erb
258
+ - lib/generators/rodauth/templates/app/views/rodauth/tailwind/add_recovery_codes.html.erb
259
+ - lib/generators/rodauth/templates/app/views/rodauth/tailwind/change_login.html.erb
260
+ - lib/generators/rodauth/templates/app/views/rodauth/tailwind/change_password.html.erb
261
+ - lib/generators/rodauth/templates/app/views/rodauth/tailwind/close_account.html.erb
262
+ - lib/generators/rodauth/templates/app/views/rodauth/tailwind/confirm_password.html.erb
263
+ - lib/generators/rodauth/templates/app/views/rodauth/tailwind/create_account.html.erb
264
+ - lib/generators/rodauth/templates/app/views/rodauth/tailwind/email_auth.html.erb
265
+ - lib/generators/rodauth/templates/app/views/rodauth/tailwind/login.html.erb
266
+ - lib/generators/rodauth/templates/app/views/rodauth/tailwind/logout.html.erb
267
+ - lib/generators/rodauth/templates/app/views/rodauth/tailwind/multi_phase_login.html.erb
268
+ - lib/generators/rodauth/templates/app/views/rodauth/tailwind/otp_auth.html.erb
269
+ - lib/generators/rodauth/templates/app/views/rodauth/tailwind/otp_disable.html.erb
270
+ - lib/generators/rodauth/templates/app/views/rodauth/tailwind/otp_setup.html.erb
271
+ - lib/generators/rodauth/templates/app/views/rodauth/tailwind/recovery_auth.html.erb
272
+ - lib/generators/rodauth/templates/app/views/rodauth/tailwind/recovery_codes.html.erb
273
+ - lib/generators/rodauth/templates/app/views/rodauth/tailwind/remember.html.erb
274
+ - lib/generators/rodauth/templates/app/views/rodauth/tailwind/reset_password.html.erb
275
+ - lib/generators/rodauth/templates/app/views/rodauth/tailwind/reset_password_request.html.erb
276
+ - lib/generators/rodauth/templates/app/views/rodauth/tailwind/sms_auth.html.erb
277
+ - lib/generators/rodauth/templates/app/views/rodauth/tailwind/sms_confirm.html.erb
278
+ - lib/generators/rodauth/templates/app/views/rodauth/tailwind/sms_disable.html.erb
279
+ - lib/generators/rodauth/templates/app/views/rodauth/tailwind/sms_request.html.erb
280
+ - lib/generators/rodauth/templates/app/views/rodauth/tailwind/sms_setup.html.erb
281
+ - lib/generators/rodauth/templates/app/views/rodauth/tailwind/two_factor_auth.html.erb
282
+ - lib/generators/rodauth/templates/app/views/rodauth/tailwind/two_factor_disable.html.erb
283
+ - lib/generators/rodauth/templates/app/views/rodauth/tailwind/two_factor_manage.html.erb
284
+ - lib/generators/rodauth/templates/app/views/rodauth/tailwind/unlock_account.html.erb
285
+ - lib/generators/rodauth/templates/app/views/rodauth/tailwind/unlock_account_request.html.erb
286
+ - lib/generators/rodauth/templates/app/views/rodauth/tailwind/verify_account.html.erb
287
+ - lib/generators/rodauth/templates/app/views/rodauth/tailwind/verify_account_resend.html.erb
288
+ - lib/generators/rodauth/templates/app/views/rodauth/tailwind/verify_login_change.html.erb
254
289
  - lib/generators/rodauth/templates/app/views/rodauth/two_factor_auth.html.erb
255
290
  - lib/generators/rodauth/templates/app/views/rodauth/two_factor_disable.html.erb
256
291
  - lib/generators/rodauth/templates/app/views/rodauth/two_factor_manage.html.erb
@@ -265,6 +300,7 @@ files:
265
300
  - lib/generators/rodauth/templates/app/views/rodauth_mailer/email_auth.text.erb
266
301
  - lib/generators/rodauth/templates/app/views/rodauth_mailer/password_changed.text.erb
267
302
  - lib/generators/rodauth/templates/app/views/rodauth_mailer/reset_password.text.erb
303
+ - lib/generators/rodauth/templates/app/views/rodauth_mailer/reset_password_notify.text.erb
268
304
  - lib/generators/rodauth/templates/app/views/rodauth_mailer/unlock_account.text.erb
269
305
  - lib/generators/rodauth/templates/app/views/rodauth_mailer/verify_account.text.erb
270
306
  - lib/generators/rodauth/templates/app/views/rodauth_mailer/verify_login_change.text.erb