rodauth-rails 1.14.1 → 1.15.1

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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +48 -46
  3. data/lib/generators/rodauth/install_generator.rb +7 -23
  4. data/lib/generators/rodauth/mailer/email_auth.erb +6 -0
  5. data/lib/generators/rodauth/mailer/otp_disabled.erb +6 -0
  6. data/lib/generators/rodauth/mailer/otp_locked_out.erb +6 -0
  7. data/lib/generators/rodauth/mailer/otp_setup.erb +6 -0
  8. data/lib/generators/rodauth/mailer/otp_unlock_failed.erb +6 -0
  9. data/lib/generators/rodauth/mailer/otp_unlocked.erb +6 -0
  10. data/lib/generators/rodauth/mailer/password_changed.erb +6 -0
  11. data/lib/generators/rodauth/mailer/reset_password.erb +6 -0
  12. data/lib/generators/rodauth/mailer/reset_password_notify.erb +6 -0
  13. data/lib/generators/rodauth/mailer/unlock_account.erb +6 -0
  14. data/lib/generators/rodauth/mailer/verify_account.erb +6 -0
  15. data/lib/generators/rodauth/mailer/verify_login_change.erb +7 -0
  16. data/lib/generators/rodauth/mailer/webauthn_authenticator_added.erb +6 -0
  17. data/lib/generators/rodauth/mailer/webauthn_authenticator_removed.erb +6 -0
  18. data/lib/generators/rodauth/mailer_generator.rb +126 -0
  19. data/lib/generators/rodauth/migration/active_record/audit_logging.erb +2 -2
  20. data/lib/generators/rodauth/migration/active_record/jwt_refresh.erb +0 -1
  21. data/lib/generators/rodauth/migration/active_record/otp_unlock.erb +7 -0
  22. data/lib/generators/rodauth/migration/sequel/audit_logging.erb +2 -2
  23. data/lib/generators/rodauth/migration/sequel/jwt_refresh.erb +1 -1
  24. data/lib/generators/rodauth/migration/sequel/otp_unlock.erb +6 -0
  25. data/lib/generators/rodauth/migration_generator.rb +4 -3
  26. data/lib/generators/rodauth/templates/INSTRUCTIONS +17 -38
  27. data/lib/generators/rodauth/templates/app/mailers/rodauth_mailer.rb.tt +4 -50
  28. data/lib/generators/rodauth/templates/app/misc/rodauth_main.rb.tt +5 -29
  29. data/lib/generators/rodauth/templates/app/models/account.rb.tt +2 -2
  30. data/lib/generators/rodauth/templates/app/views/rodauth/otp_unlock.html.erb +21 -0
  31. data/lib/generators/rodauth/templates/app/views/rodauth/otp_unlock_not_available.html.erb +5 -0
  32. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/otp_unlock.html.erb +22 -0
  33. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/otp_unlock_not_available.html.erb +14 -0
  34. data/lib/generators/rodauth/templates/app/views/rodauth/tailwind/webauthn_remove.html.erb +1 -0
  35. data/lib/generators/rodauth/templates/app/views/rodauth/webauthn_remove.html.erb +1 -0
  36. data/lib/generators/rodauth/templates/app/views/rodauth_mailer/otp_disabled.text.erb +2 -0
  37. data/lib/generators/rodauth/templates/app/views/rodauth_mailer/otp_locked_out.text.erb +9 -0
  38. data/lib/generators/rodauth/templates/app/views/rodauth_mailer/otp_setup.text.erb +2 -0
  39. data/lib/generators/rodauth/templates/app/views/rodauth_mailer/otp_unlock_failed.text.erb +8 -0
  40. data/lib/generators/rodauth/templates/app/views/rodauth_mailer/otp_unlocked.text.erb +2 -0
  41. data/lib/generators/rodauth/templates/app/views/rodauth_mailer/webauthn_authenticator_added.text.erb +3 -0
  42. data/lib/generators/rodauth/templates/app/views/rodauth_mailer/webauthn_authenticator_removed.text.erb +3 -0
  43. data/lib/generators/rodauth/views_generator.rb +2 -1
  44. data/lib/rodauth/rails/feature/base.rb +2 -1
  45. data/lib/rodauth/rails/feature/instrumentation.rb +23 -7
  46. data/lib/rodauth/rails/feature/internal_request.rb +16 -6
  47. data/lib/rodauth/rails/version.rb +1 -1
  48. data/rodauth-rails.gemspec +4 -4
  49. metadata +35 -8
  50. data/CHANGELOG.md +0 -570
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0c1c699a9d7a18673c641d2fe236bfdb2b9537ade7c6212e9f2f386738308d67
4
- data.tar.gz: b30b4195d46e461f0235caaa1e2e453fb70260bda7d9174cbf60be8f43796f94
3
+ metadata.gz: d6f6be6f7643a5ee8a8c249c4c4a5591df43a63d32c85a2b3ee1427394b7becf
4
+ data.tar.gz: a838f4ddb1eab3b8e39acdeacac4830d5eb4024b4a44a3606c5de4c754c488de
5
5
  SHA512:
6
- metadata.gz: f8d67d9e2a738d66d9ba37bcf1f71f69c546d6a361e91248255966bdd8b49fadfff717ab28e44bd2cd0ad0cad784740ef631a7863b1403e56c267b8434c2d8e3
7
- data.tar.gz: 0b501ebf1306bbf9780ec86fd9267e75b3bcae3ee69f0b7f56f0c19132f1d8a96a8d7cfcae31dbb07b5636b42064833d5d8543ceb55d3d4432d22cb28ae9aa72
6
+ metadata.gz: da215ea65e513b90b41203d1652955b378e1c8b1145c476d4d0a15ef9b0a1f9ebc86f6818a4e35a2405805c663047c85a309e62de41af5c4e0259645f7326596
7
+ data.tar.gz: 11770055b40a703d6e7640c6c80f192a5eccaa0605d6b7c46fe24d9d61d8438224c763571d69a86ba767a222460db90b795407756ad112def5af27aff8f79052
data/README.md CHANGED
@@ -12,11 +12,13 @@ Provides Rails integration for the [Rodauth] authentication framework.
12
12
  * [OmniAuth guide](https://github.com/janko/rodauth-rails/wiki/OmniAuth)
13
13
  * [JSON Request Documentation for Rodauth](https://documenter.getpostman.com/view/26686011/2s9YC7SWn9)
14
14
 
15
- 🎥 Screencasts:
15
+ 🎥 Screencasts / Streams:
16
16
 
17
- * [Rails Authentication with Rodauth](https://www.youtube.com/watch?v=2hDpNikacf0)
18
- * [Multifactor Authentication with Rodauth](https://www.youtube.com/watch?v=9ON-kgXpz2A&list=PLkGQXZLACDTGKsaRWstkHQdm2CUmT3SZ-) ([TOTP](https://youtu.be/9ON-kgXpz2A), [Recovery Codes](https://youtu.be/lkFCcE1Q5-w))
19
- * [Add Admin Accounts](https://www.youtube.com/watch?v=N6z7AtKSpNI)
17
+ * [Rails Authentication with Rodauth](https://www.youtube.com/watch?v=2hDpNikacf0) \[8:23\]
18
+ * [Multifactor Authentication via TOTP with Rodauth](https://youtu.be/9ON-kgXpz2A) \[4:36\]
19
+ * [Multifactor Authentication via Recovery Codes with Rodauth](https://youtu.be/lkFCcE1Q5-w) \[4:24\]
20
+ * [Adding Admin Accounts with Rodauth](https://www.youtube.com/watch?v=N6z7AtKSpNI) \[1:25:55\]
21
+ * [Integrating Passkeys into Rails with Rodauth](https://www.youtube.com/watch?v=kGzgmfCmnmY) \[59:47\]
20
22
 
21
23
  📚 Articles:
22
24
 
@@ -68,7 +70,7 @@ $ rails generate rodauth:install
68
70
 
69
71
  This generator will create a Rodauth app and configuration with common
70
72
  authentication features enabled, a database migration with tables required by
71
- those features, a mailer with default templates, and a few other files.
73
+ those features, and a few other files.
72
74
 
73
75
  Feel free to remove any features you don't need, along with their corresponding
74
76
  tables. Afterwards, run the migration:
@@ -77,14 +79,6 @@ tables. Afterwards, run the migration:
77
79
  $ rails db:migrate
78
80
  ```
79
81
 
80
- For your mailer to be able to generate email links, you'll need to set up
81
- default URL options in each environment. Here is a possible configuration for
82
- `config/environments/development.rb`:
83
-
84
- ```rb
85
- config.action_mailer.default_url_options = { host: "localhost", port: 3000 }
86
- ```
87
-
88
82
  ### Install options
89
83
 
90
84
  The install generator will use the `accounts` table by default. You can specify a different table name:
@@ -316,46 +310,45 @@ $ rails generate rodauth:views webauthn two_factor_base --name admin
316
310
 
317
311
  ## Mailer
318
312
 
319
- The install generator will create `RodauthMailer` with default email templates,
320
- and configure Rodauth features that send emails as part of the authentication
321
- flow to use it.
313
+ When you're ready to modify the default email templates and safely deliver them
314
+ in a background job, you can run the following command to generate the mailer
315
+ integration:
322
316
 
323
- ```rb
324
- # app/mailers/rodauth_mailer.rb
325
- class RodauthMailer < ApplicationMailer
326
- def verify_account(account_id, key) ... end
327
- def reset_password(account_id, key) ... end
328
- def verify_login_change(account_id, key) ... end
329
- def password_changed(account_id) ... end
330
- # def email_auth(account_id, key) ... end
331
- # def unlock_account(account_id, key) ... end
332
- end
317
+ ```sh
318
+ $ rails generate rodauth:mailer
333
319
  ```
320
+
321
+ This will create a `RodauthMailer`, email templates, and necessary Rodauth
322
+ configuration for the features you have enabled. For email links to work, you
323
+ need to have `config.action_mailer.default_url_options` set for each
324
+ environment.
325
+
334
326
  ```rb
335
- # app/misc/rodauth_main.rb
336
- class RodauthMain < Rodauth::Rails::Auth
337
- configure do
338
- create_reset_password_email { RodauthMailer.reset_password(account_id, reset_password_key_value) }
339
- create_verify_account_email { RodauthMailer.verify_account(account_id, verify_account_key_value) }
340
- create_verify_login_change_email { |_login| RodauthMailer.verify_login_change(account_id, verify_login_change_key_value) }
341
- create_password_changed_email { RodauthMailer.password_changed(account_id) }
342
- # create_email_auth_email { RodauthMailer.email_auth(account_id, email_auth_key_value) }
343
- # create_unlock_account_email { RodauthMailer.unlock_account(account_id, unlock_account_key_value) }
344
- send_email do |email|
345
- # queue email delivery on the mailer after the transaction commits
346
- db.after_commit { email.deliver_later }
347
- end
348
- end
349
- end
327
+ # config/environments/development.rb
328
+ config.action_mailer.default_url_options = { host: "localhost", port: 3000 }
329
+ ```
330
+
331
+ The generator accepts various options:
332
+
333
+ ```sh
334
+ # generate mailer integration for specified features
335
+ $ rails generate rodauth:mailer email_auth lockout webauthn_modify_email
336
+
337
+ # generate mailer integration for all Rodauth features
338
+ $ rails generate rodauth:mailer --all
339
+
340
+ # specify different Rodauth configuration to select enabled features
341
+ $ rails generate rodauth:mailer --name admin
350
342
  ```
351
343
 
352
- This configuration calls `#deliver_later`, which uses Active Job to deliver
353
- emails in a background job. If you want to send emails synchronously, you can
354
- modify the configuration to call `#deliver_now` instead.
344
+ Note that the generated Rodauth configuration calls `#deliver_later`, which
345
+ uses Active Job to deliver emails in a background job. If you want to deliver
346
+ emails synchronously, you can modify the configuration to call `#deliver_now`
347
+ instead.
355
348
 
356
349
  If you're using a background processing library without an Active Job adapter,
357
350
  or a 3rd-party service for sending transactional emails, see [this wiki
358
- page][custom mailer worker] on how to set it up.
351
+ page][custom mailer job] on how to set it up.
359
352
 
360
353
  ## Migrations
361
354
 
@@ -535,6 +528,14 @@ Rodauth::Rails.rodauth(session: { two_factor_auth_setup: true })
535
528
  Rodauth::Rails.rodauth(:admin, params: { "param" => "value" })
536
529
  ```
537
530
 
531
+ You can override default URL options ad-hoc by modifying `#rails_url_options`:
532
+
533
+ ```rb
534
+ rodauth.base_url #=> "https://example.com"
535
+ rodauth.rails_url_options[:host] = "subdomain.example.com"
536
+ rodauth.base_url #=> "https://subdomain.example.com"
537
+ ```
538
+
538
539
  ### Using as a library
539
540
 
540
541
  Rodauth offers a [`Rodauth.lib`][library] method for when you want to use it as a library (via [internal requests][internal_request]), as opposed to having it route requests. This gem provides a `Rodauth::Rails.lib` counterpart that does the same but with Rails integration:
@@ -633,6 +634,7 @@ The `rails` feature rodauth-rails loads provides the following configuration met
633
634
  | `rails_controller_instance` | Instance of the controller with the request env context. |
634
635
  | `rails_controller` | Controller class to use for rendering and CSRF protection. |
635
636
  | `rails_account_model` | Model class connected with the accounts table. |
637
+ | `rails_url_options` | Options used for generating URLs outside of a request (defaults to `config.action_mailer.default_url_options`) |
636
638
 
637
639
  ```rb
638
640
  class RodauthMain < Rodauth::Rails::Auth
@@ -786,7 +788,7 @@ conduct](CODE_OF_CONDUCT.md).
786
788
  [internal_request]: http://rodauth.jeremyevans.net/rdoc/files/doc/internal_request_rdoc.html
787
789
  [path_class_methods]: https://rodauth.jeremyevans.net/rdoc/files/doc/path_class_methods_rdoc.html
788
790
  [account types]: https://github.com/janko/rodauth-rails/wiki/Account-Types
789
- [custom mailer worker]: https://github.com/janko/rodauth-rails/wiki/Custom-Mailer-Worker
791
+ [custom mailer job]: https://github.com/janko/rodauth-rails/wiki/Custom-Mailer-Job
790
792
  [Turbo]: https://turbo.hotwired.dev/
791
793
  [rodauth-model]: https://github.com/janko/rodauth-model
792
794
  [JSON API]: https://github.com/janko/rodauth-rails/wiki/JSON-API
@@ -13,15 +13,6 @@ module Rodauth
13
13
  "sqlserver" => RUBY_ENGINE == "jruby" ? "mssql" : "tinytds",
14
14
  }
15
15
 
16
- MAILER_VIEWS = %w[
17
- email_auth
18
- password_changed
19
- reset_password
20
- unlock_account
21
- verify_account
22
- verify_login_change
23
- ]
24
-
25
16
  source_root "#{__dir__}/templates"
26
17
  namespace "rodauth:install"
27
18
 
@@ -55,16 +46,6 @@ module Rodauth
55
46
  template "app/models/account.rb", "app/models/#{table_prefix}.rb"
56
47
  end
57
48
 
58
- def create_mailer
59
- return unless defined?(ActionMailer)
60
-
61
- template "app/mailers/rodauth_mailer.rb"
62
-
63
- MAILER_VIEWS.each do |view|
64
- copy_file "app/views/rodauth_mailer/#{view}.text.erb"
65
- end
66
- end
67
-
68
49
  def create_fixtures
69
50
  generator_options = ::Rails.configuration.generators.options
70
51
  if generator_options[:test_unit][:fixture] && generator_options[:test_unit][:fixture_replacement].nil?
@@ -74,7 +55,7 @@ module Rodauth
74
55
  end
75
56
 
76
57
  def show_instructions
77
- readme "INSTRUCTIONS" if behavior == :invoke
58
+ readme "INSTRUCTIONS" if behavior == :invoke && !api_only?
78
59
  end
79
60
 
80
61
  private
@@ -101,9 +82,12 @@ module Rodauth
101
82
  options[:argon2]
102
83
  end
103
84
 
104
- def sequel_activerecord_integration?
105
- defined?(ActiveRecord::Railtie) &&
106
- (!defined?(Sequel) || Sequel::DATABASES.empty?)
85
+ def activerecord?
86
+ defined?(ActiveRecord::Railtie)
87
+ end
88
+
89
+ def sequel?
90
+ defined?(Sequel) && Sequel::DATABASES.any?
107
91
  end
108
92
 
109
93
  def session_store?
@@ -0,0 +1,6 @@
1
+ def email_auth(name, account_id, key)
2
+ @rodauth = rodauth(name, account_id) { @email_auth_key_value = key }
3
+ @account = @rodauth.rails_account
4
+
5
+ mail subject: @rodauth.email_subject_prefix + @rodauth.email_auth_email_subject
6
+ end
@@ -0,0 +1,6 @@
1
+ def otp_disabled(name, account_id)
2
+ @rodauth = rodauth(name, account_id)
3
+ @account = @rodauth.rails_account
4
+
5
+ mail subject: @rodauth.email_subject_prefix + @rodauth.otp_disabled_email_subject
6
+ end
@@ -0,0 +1,6 @@
1
+ def otp_locked_out(name, account_id)
2
+ @rodauth = rodauth(name, account_id)
3
+ @account = @rodauth.rails_account
4
+
5
+ mail subject: @rodauth.email_subject_prefix + @rodauth.otp_locked_out_email_subject
6
+ end
@@ -0,0 +1,6 @@
1
+ def otp_setup(name, account_id)
2
+ @rodauth = rodauth(name, account_id)
3
+ @account = @rodauth.rails_account
4
+
5
+ mail subject: @rodauth.email_subject_prefix + @rodauth.otp_setup_email_subject
6
+ end
@@ -0,0 +1,6 @@
1
+ def otp_unlock_failed(name, account_id)
2
+ @rodauth = rodauth(name, account_id)
3
+ @account = @rodauth.rails_account
4
+
5
+ mail subject: @rodauth.email_subject_prefix + @rodauth.otp_unlock_failed_email_subject
6
+ end
@@ -0,0 +1,6 @@
1
+ def otp_unlocked(name, account_id)
2
+ @rodauth = rodauth(name, account_id)
3
+ @account = @rodauth.rails_account
4
+
5
+ mail subject: @rodauth.email_subject_prefix + @rodauth.otp_unlocked_email_subject
6
+ end
@@ -0,0 +1,6 @@
1
+ def password_changed(name, account_id)
2
+ @rodauth = rodauth(name, account_id)
3
+ @account = @rodauth.rails_account
4
+
5
+ mail subject: @rodauth.email_subject_prefix + @rodauth.password_changed_email_subject
6
+ end
@@ -0,0 +1,6 @@
1
+ def reset_password(name, account_id, key)
2
+ @rodauth = rodauth(name, account_id) { @reset_password_key_value = key }
3
+ @account = @rodauth.rails_account
4
+
5
+ mail subject: @rodauth.email_subject_prefix + @rodauth.reset_password_email_subject
6
+ end
@@ -0,0 +1,6 @@
1
+ def reset_password_notify(name, account_id)
2
+ @rodauth = rodauth(name, account_id)
3
+ @account = @rodauth.rails_account
4
+
5
+ mail subject: @rodauth.email_subject_prefix + @rodauth.reset_password_notify_email_subject
6
+ end
@@ -0,0 +1,6 @@
1
+ def unlock_account(name, account_id, key)
2
+ @rodauth = rodauth(name, account_id) { @unlock_account_key_value = key }
3
+ @account = @rodauth.rails_account
4
+
5
+ mail subject: @rodauth.email_subject_prefix + @rodauth.unlock_account_email_subject
6
+ end
@@ -0,0 +1,6 @@
1
+ def verify_account(name, account_id, key)
2
+ @rodauth = rodauth(name, account_id) { @verify_account_key_value = key }
3
+ @account = @rodauth.rails_account
4
+
5
+ mail subject: @rodauth.email_subject_prefix + @rodauth.verify_account_email_subject
6
+ end
@@ -0,0 +1,7 @@
1
+ def verify_login_change(name, account_id, key)
2
+ @rodauth = rodauth(name, account_id) { @verify_login_change_key_value = key }
3
+ @account = @rodauth.rails_account
4
+ @new_email = @account.login_change_key.login
5
+
6
+ mail to: @new_email, subject: @rodauth.email_subject_prefix + @rodauth.verify_login_change_email_subject
7
+ end
@@ -0,0 +1,6 @@
1
+ def webauthn_authenticator_added(name, account_id)
2
+ @rodauth = rodauth(name, account_id)
3
+ @account = @rodauth.rails_account
4
+
5
+ mail subject: @rodauth.email_subject_prefix + @rodauth.webauthn_authenticator_added_email_subject
6
+ end
@@ -0,0 +1,6 @@
1
+ def webauthn_authenticator_removed(name, account_id)
2
+ @rodauth = rodauth(name, account_id)
3
+ @account = @rodauth.rails_account
4
+
5
+ mail subject: @rodauth.email_subject_prefix + @rodauth.webauthn_authenticator_removed_email_subject
6
+ end
@@ -0,0 +1,126 @@
1
+ require "rails/generators/base"
2
+
3
+ module Rodauth
4
+ module Rails
5
+ module Generators
6
+ class MailerGenerator < ::Rails::Generators::Base
7
+ source_root "#{__dir__}/templates"
8
+ namespace "rodauth:mailer"
9
+
10
+ argument :selected_features, optional: true, type: :array,
11
+ desc: "Rodauth features to generate mailer integration for (verify_account, verify_login_change, reset_password etc.)"
12
+
13
+ class_option :all, aliases: "-a", type: :boolean,
14
+ desc: "Generates mailer integration for all Rodauth features",
15
+ default: false
16
+
17
+ class_option :name, aliases: "-n", type: :string,
18
+ desc: "The configuration name for which to generate mailer configuration",
19
+ default: nil
20
+
21
+ EMAILS = {
22
+ verify_account: %w[verify_account],
23
+ reset_password: %w[reset_password],
24
+ verify_login_change: %w[verify_login_change],
25
+ email_auth: %w[email_auth],
26
+ lockout: %w[unlock_account],
27
+ reset_password_notify: %w[reset_password_notify],
28
+ change_password_notify: %w[password_changed],
29
+ otp_modify_email: %w[otp_setup otp_disabled],
30
+ otp_lockout_email: %w[otp_locked_out otp_unlocked otp_unlock_failed],
31
+ webauthn_modify_email: %w[webauthn_authenticator_added webauthn_authenticator_removed],
32
+ }
33
+
34
+ TOKENS = %w[reset_password verify_account verify_login_change email_auth unlock_account]
35
+
36
+ def copy_mailer_views
37
+ return unless validate_features
38
+
39
+ emails.each do |email|
40
+ copy_file "app/views/rodauth_mailer/#{email}.text.erb"
41
+ end
42
+ end
43
+
44
+ def copy_mailer
45
+ return unless validate_features
46
+
47
+ if File.exist?("#{destination_root}/app/mailers/rodauth_mailer.rb") && options.fetch(:skip, true) && !options[:force] && behavior == :invoke
48
+ say "\nCopy the following lines into your Rodauth mailer:\n\n#{mailer_content}"
49
+ else
50
+ template "app/mailers/rodauth_mailer.rb"
51
+ end
52
+ end
53
+
54
+ def show_configuration
55
+ return unless behavior == :invoke && validate_features
56
+
57
+ say "\nCopy the following lines into your Rodauth configuration:\n\n#{configuration_content}"
58
+ end
59
+
60
+ private
61
+
62
+ def mailer_content
63
+ emails
64
+ .map { |email| File.read("#{__dir__}/mailer/#{email}.erb") }
65
+ .map { |content| erb_eval(content) }
66
+ .join("\n")
67
+ .indent(2)
68
+ end
69
+
70
+ def configuration_content
71
+ emails
72
+ .map { |email| configuration_chunk(email) }
73
+ .join
74
+ .indent(2)
75
+ end
76
+
77
+ def configuration_chunk(email)
78
+ <<~RUBY
79
+ create_#{email}_email do#{" |_login|" if email == "verify_login_change"}
80
+ RodauthMailer.#{email}(self.class.configuration_name, account_id#{", #{email}_key_value" if TOKENS.include?(email)})
81
+ end
82
+ RUBY
83
+ end
84
+
85
+ def erb_eval(content)
86
+ if ERB.version[/\d+\.\d+\.\d+/].to_s >= "2.2.0"
87
+ ERB.new(content, trim_mode: "-").result(binding)
88
+ else
89
+ ERB.new(content, 0, "-").result(binding)
90
+ end
91
+ end
92
+
93
+ def emails
94
+ features.flat_map { |feature| EMAILS.fetch(feature) }
95
+ end
96
+
97
+ def validate_features
98
+ if (features - EMAILS.keys).any?
99
+ say "No available email template for feature(s): #{(features - EMAILS.keys).join(", ")}", :error
100
+ false
101
+ else
102
+ true
103
+ end
104
+ end
105
+
106
+ def features
107
+ if options[:all]
108
+ EMAILS.keys
109
+ elsif selected_features
110
+ selected_features.map(&:to_sym)
111
+ else
112
+ rodauth_configuration.features & EMAILS.keys
113
+ end
114
+ end
115
+
116
+ def rodauth_configuration
117
+ Rodauth::Rails.app.rodauth!(configuration_name)
118
+ end
119
+
120
+ def configuration_name
121
+ options[:name]&.to_sym
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -11,6 +11,6 @@ create_table :<%= table_prefix %>_authentication_audit_logs<%= primary_key_type
11
11
  <% else -%>
12
12
  t.string :metadata
13
13
  <% end -%>
14
- t.index [:<%= table_prefix %>_id, :at], name: "audit_<%= table_prefix %>_at_idx"
15
- t.index :at, name: "audit_at_idx"
14
+ t.index [:<%= table_prefix %>_id, :at]
15
+ t.index :at
16
16
  end
@@ -3,5 +3,4 @@ create_table :<%= table_prefix %>_jwt_refresh_keys<%= primary_key_type %> do |t|
3
3
  t.references :<%= table_prefix %>, foreign_key: true, null: false<%= primary_key_type(:type) %>
4
4
  t.string :key, null: false
5
5
  t.datetime :deadline, null: false
6
- t.index :<%= table_prefix %>_id, name: "<%= table_prefix %>_jwt_rk_<%= table_prefix %>_id_idx"
7
6
  end
@@ -0,0 +1,7 @@
1
+ # Used by the otp_unlock feature
2
+ create_table :<%= table_prefix %>_otp_unlocks, id: false do |t|
3
+ t.<%= primary_key_type(nil) %> :id, primary_key: true
4
+ t.foreign_key :<%= table_prefix.pluralize %>, column: :id
5
+ t.integer :num_successes, null: false, default: 1
6
+ t.datetime :next_auth_attempt_after, null: false, default: -> { "<%= current_timestamp %>" }
7
+ end
@@ -12,6 +12,6 @@ create_table :<%= table_prefix %>_authentication_audit_logs do
12
12
  <% else -%>
13
13
  String :metadata
14
14
  <% end -%>
15
- index [:<%= table_prefix %>_id, :at], name: :audit_<%= table_prefix %>_at_idx
16
- index :at, name: :audit_at_idx
15
+ index [:<%= table_prefix %>_id, :at]
16
+ index :at
17
17
  end
@@ -4,5 +4,5 @@ create_table :<%= table_prefix %>_jwt_refresh_keys do
4
4
  foreign_key :<%= table_prefix %>_id, :<%= table_prefix.pluralize %>, null: false, type: :Bignum
5
5
  String :key, null: false
6
6
  DateTime :deadline, null: false
7
- index :<%= table_prefix %>_id, name: :<%= table_prefix %>_jwt_rk_<%= table_prefix %>_id_idx
7
+ index :<%= table_prefix %>_id
8
8
  end
@@ -0,0 +1,6 @@
1
+ # Used by the otp_unlock feature
2
+ create_table :<%= table_prefix %>_otp_unlocks do
3
+ foreign_key :id, :<%= table_prefix.pluralize %>, primary_key: true, type: :Bignum
4
+ Integer :num_successes, null: false, default: 1
5
+ Time :next_auth_attempt_after, null: false, default: Sequel::CURRENT_TIMESTAMP
6
+ end
@@ -20,12 +20,12 @@ module Rodauth
20
20
  desc: "Name of the generated migration file"
21
21
 
22
22
  def create_rodauth_migration
23
- validate_features or return
23
+ return unless validate_features
24
24
 
25
25
  migration_template "db/migrate/create_rodauth.rb", File.join(db_migrate_path, "#{migration_name}.rb")
26
26
  end
27
27
 
28
- def show_instructions
28
+ def show_configuration
29
29
  # skip if called from install generator, it already adds configuration
30
30
  return if current_command_chain.include?(:generate_rodauth_migration)
31
31
  return unless options[:prefix] && behavior == :invoke
@@ -36,7 +36,7 @@ module Rodauth
36
36
  .join("\n")
37
37
  .indent(2)
38
38
 
39
- say "\nAdd the following to your Rodauth configuration:\n\n#{configuration}"
39
+ say "\nCopy the following lines into your Rodauth configuration:\n\n#{configuration}"
40
40
  end
41
41
 
42
42
  private
@@ -93,6 +93,7 @@ module Rodauth
93
93
  reset_password: { reset_password_table: "%{singular}_password_reset_keys" },
94
94
  email_auth: { email_auth_table: "%{singular}_email_auth_keys" },
95
95
  otp: { otp_keys_table: "%{singular}_otp_keys" },
96
+ otp_unlock: { otp_unlock_table: "%{singular}_otp_unlocks" },
96
97
  sms_codes: { sms_codes_table: "%{singular}_sms_codes" },
97
98
  recovery_codes: { recovery_codes_table: "%{singular}_recovery_codes" },
98
99
  webauthn: { webauthn_keys_table: "%{singular}_webauthn_keys", webauthn_user_ids_table: "%{singular}_webauthn_user_ids", webauthn_keys_account_id_column: "%{singular}_id" },
@@ -1,52 +1,31 @@
1
1
  ===============================================================================
2
2
 
3
- Depending on your application's configuration some manual setup may be required:
3
+ * Ensure you have defined a root path in your config/routes.rb. For example:
4
4
 
5
- 1. Ensure you have defined default url options in your environments files. Here
6
- is an example of default_url_options appropriate for a development environment
7
- in config/environments/development.rb:
5
+ root to: "pages#home"
8
6
 
9
- config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
7
+ * Ensure you're displaying flash messages in your layout template. For example:
10
8
 
11
- In production, :host should be set to the actual host of your application.
9
+ <% if notice %>
10
+ <div class="alert alert-success"><%= notice %></div>
11
+ <% end %>
12
+ <% if alert %>
13
+ <div class="alert alert-danger"><%= alert %></div>
14
+ <% end %>
12
15
 
13
- * Required for all applications. *
16
+ * Titles for Rodauth pages are available via @page_title instance variable
17
+ by default, you can use it in your layout file:
14
18
 
15
- 2. Ensure you have defined root_url to *something* in your config/routes.rb.
16
- For example:
19
+ <title><%= @page_title || "Default title" %></title>
17
20
 
18
- root to: "home#index"
21
+ * You can copy Rodauth views into your app by running:
19
22
 
20
- * Not required for API-only Applications *
23
+ rails g rodauth:views # default bootstrap views
21
24
 
22
- 3. Ensure you have flash messages in app/views/layouts/application.html.erb.
23
- For example:
25
+ rails g rodauth:views --css=tailwind # tailwind views (requires @tailwindcss/forms plugin)
24
26
 
25
- <% if notice %>
26
- <div class="alert alert-success"><%= notice %></div>
27
- <% end %>
28
- <% if alert %>
29
- <div class="alert alert-danger"><%= alert %></div>
30
- <% end %>
27
+ * You can copy email templates and generate mailer integration by running:
31
28
 
32
- * Not required for API-only Applications *
33
-
34
- 4. Titles for Rodauth pages are available via @page_title instance variable
35
- by default, you can use it in your layout file:
36
-
37
- <head>
38
- <title><%= @page_title || "Default title" %></title>
39
- ...
40
- </head>
41
-
42
- * Not required *
43
-
44
- 5. You can copy Rodauth views (for customization) to your app by running:
45
-
46
- rails g rodauth:views # default bootstrap views
47
-
48
- rails g rodauth:views --css=tailwind # tailwind views (requires @tailwindcss/forms plugin)
49
-
50
- * Not required *
29
+ rails g rodauth:mailer
51
30
 
52
31
  ===============================================================================