rodauth-rails 0.17.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +80 -0
  3. data/README.md +224 -548
  4. data/lib/generators/rodauth/install_generator.rb +40 -35
  5. data/lib/generators/rodauth/migration/base.erb +8 -2
  6. data/lib/generators/rodauth/migration_generator.rb +9 -2
  7. data/lib/generators/rodauth/templates/INSTRUCTIONS +40 -0
  8. data/lib/generators/rodauth/templates/app/mailers/rodauth_mailer.rb +36 -19
  9. data/lib/generators/rodauth/templates/app/misc/rodauth_app.rb +38 -0
  10. data/lib/generators/rodauth/templates/app/{lib/rodauth_app.rb → misc/rodauth_main.rb} +10 -53
  11. data/lib/generators/rodauth/templates/app/views/rodauth/_email_auth_request_form.html.erb +7 -4
  12. data/lib/generators/rodauth/templates/app/views/rodauth/_login_form.html.erb +26 -9
  13. data/lib/generators/rodauth/templates/app/views/rodauth/_login_form_footer.html.erb +7 -6
  14. data/lib/generators/rodauth/templates/app/views/rodauth/_login_form_header.html.erb +3 -3
  15. data/lib/generators/rodauth/templates/app/views/rodauth/add_recovery_codes.html.erb +7 -5
  16. data/lib/generators/rodauth/templates/app/views/rodauth/change_login.html.erb +29 -6
  17. data/lib/generators/rodauth/templates/app/views/rodauth/change_password.html.erb +29 -6
  18. data/lib/generators/rodauth/templates/app/views/rodauth/close_account.html.erb +15 -4
  19. data/lib/generators/rodauth/templates/app/views/rodauth/confirm_password.html.erb +13 -4
  20. data/lib/generators/rodauth/templates/app/views/rodauth/create_account.html.erb +37 -7
  21. data/lib/generators/rodauth/templates/app/views/rodauth/email_auth.html.erb +7 -3
  22. data/lib/generators/rodauth/templates/app/views/rodauth/login.html.erb +5 -3
  23. data/lib/generators/rodauth/templates/app/views/rodauth/logout.html.erb +16 -4
  24. data/lib/generators/rodauth/templates/app/views/rodauth/multi_phase_login.html.erb +5 -3
  25. data/lib/generators/rodauth/templates/app/views/rodauth/otp_auth.html.erb +17 -4
  26. data/lib/generators/rodauth/templates/app/views/rodauth/otp_disable.html.erb +15 -4
  27. data/lib/generators/rodauth/templates/app/views/rodauth/otp_setup.html.erb +30 -10
  28. data/lib/generators/rodauth/templates/app/views/rodauth/recovery_auth.html.erb +13 -4
  29. data/lib/generators/rodauth/templates/app/views/rodauth/recovery_codes.html.erb +15 -1
  30. data/lib/generators/rodauth/templates/app/views/rodauth/remember.html.erb +14 -9
  31. data/lib/generators/rodauth/templates/app/views/rodauth/reset_password.html.erb +21 -5
  32. data/lib/generators/rodauth/templates/app/views/rodauth/reset_password_request.html.erb +19 -9
  33. data/lib/generators/rodauth/templates/app/views/rodauth/sms_auth.html.erb +17 -4
  34. data/lib/generators/rodauth/templates/app/views/rodauth/sms_confirm.html.erb +17 -4
  35. data/lib/generators/rodauth/templates/app/views/rodauth/sms_disable.html.erb +15 -4
  36. data/lib/generators/rodauth/templates/app/views/rodauth/sms_request.html.erb +7 -3
  37. data/lib/generators/rodauth/templates/app/views/rodauth/sms_setup.html.erb +25 -5
  38. data/lib/generators/rodauth/templates/app/views/rodauth/two_factor_auth.html.erb +5 -3
  39. data/lib/generators/rodauth/templates/app/views/rodauth/two_factor_disable.html.erb +15 -4
  40. data/lib/generators/rodauth/templates/app/views/rodauth/two_factor_manage.html.erb +17 -15
  41. data/lib/generators/rodauth/templates/app/views/rodauth/unlock_account.html.erb +17 -5
  42. data/lib/generators/rodauth/templates/app/views/rodauth/unlock_account_request.html.erb +11 -5
  43. data/lib/generators/rodauth/templates/app/views/rodauth/verify_account.html.erb +23 -5
  44. data/lib/generators/rodauth/templates/app/views/rodauth/verify_account_resend.html.erb +19 -9
  45. data/lib/generators/rodauth/templates/app/views/rodauth/verify_login_change.html.erb +7 -3
  46. data/lib/generators/rodauth/templates/app/views/rodauth/webauthn_auth.html.erb +13 -9
  47. data/lib/generators/rodauth/templates/app/views/rodauth/webauthn_remove.html.erb +21 -9
  48. data/lib/generators/rodauth/templates/app/views/rodauth/webauthn_setup.html.erb +21 -9
  49. data/lib/generators/rodauth/templates/app/views/rodauth_mailer/email_auth.text.erb +1 -1
  50. data/lib/generators/rodauth/templates/app/views/rodauth_mailer/reset_password.text.erb +1 -1
  51. data/lib/generators/rodauth/templates/app/views/rodauth_mailer/unlock_account.text.erb +1 -1
  52. data/lib/generators/rodauth/templates/app/views/rodauth_mailer/verify_account.text.erb +1 -1
  53. data/lib/generators/rodauth/templates/app/views/rodauth_mailer/verify_login_change.text.erb +3 -3
  54. data/lib/generators/rodauth/views_generator.rb +55 -93
  55. data/lib/rodauth/rails/app.rb +5 -4
  56. data/lib/rodauth/rails/auth.rb +1 -16
  57. data/lib/rodauth/rails/controller_methods.rb +1 -1
  58. data/lib/rodauth/rails/feature/csrf.rb +15 -4
  59. data/lib/rodauth/rails/feature/internal_request.rb +22 -20
  60. data/lib/rodauth/rails/feature/render.rb +9 -1
  61. data/lib/rodauth/rails/railtie.rb +4 -2
  62. data/lib/rodauth/rails/tasks.rake +2 -2
  63. data/lib/rodauth/rails/version.rb +1 -1
  64. data/lib/rodauth/rails.rb +16 -20
  65. data/rodauth-rails.gemspec +2 -2
  66. metadata +9 -23
  67. data/lib/generators/rodauth/templates/app/views/rodauth/_field.html.erb +0 -10
  68. data/lib/generators/rodauth/templates/app/views/rodauth/_field_error.html.erb +0 -3
  69. data/lib/generators/rodauth/templates/app/views/rodauth/_global_logout_field.html.erb +0 -6
  70. data/lib/generators/rodauth/templates/app/views/rodauth/_login_confirm_field.html.erb +0 -4
  71. data/lib/generators/rodauth/templates/app/views/rodauth/_login_display.html.erb +0 -4
  72. data/lib/generators/rodauth/templates/app/views/rodauth/_login_field.html.erb +0 -4
  73. data/lib/generators/rodauth/templates/app/views/rodauth/_login_hidden_field.html.erb +0 -1
  74. data/lib/generators/rodauth/templates/app/views/rodauth/_new_password_field.html.erb +0 -4
  75. data/lib/generators/rodauth/templates/app/views/rodauth/_otp_auth_code_field.html.erb +0 -8
  76. data/lib/generators/rodauth/templates/app/views/rodauth/_password_confirm_field.html.erb +0 -4
  77. data/lib/generators/rodauth/templates/app/views/rodauth/_password_field.html.erb +0 -4
  78. data/lib/generators/rodauth/templates/app/views/rodauth/_recovery_code_field.html.erb +0 -4
  79. data/lib/generators/rodauth/templates/app/views/rodauth/_recovery_codes_form.html.erb +0 -6
  80. data/lib/generators/rodauth/templates/app/views/rodauth/_sms_code_field.html.erb +0 -8
  81. data/lib/generators/rodauth/templates/app/views/rodauth/_sms_phone_field.html.erb +0 -8
  82. data/lib/generators/rodauth/templates/app/views/rodauth/_submit.html.erb +0 -3
data/README.md CHANGED
@@ -8,6 +8,9 @@ Useful links:
8
8
 
9
9
  * [Rodauth documentation](http://rodauth.jeremyevans.net/documentation.html)
10
10
  * [Rails demo](https://github.com/janko/rodauth-demo-rails)
11
+ * [JSON API guide](https://github.com/janko/rodauth-rails/wiki/JSON-API)
12
+ * [OmniAuth guide](https://github.com/janko/rodauth-rails/wiki/OmniAuth)
13
+ * [Testing guide](https://github.com/janko/rodauth-rails/wiki/Testing)
11
14
 
12
15
  Articles:
13
16
 
@@ -39,17 +42,12 @@ Active Record. There are good reasons for this, and to make Rodauth work
39
42
  smoothly alongside Active Record, rodauth-rails configures Sequel to [reuse
40
43
  Active Record's database connection][sequel-activerecord_connection].
41
44
 
42
- ## Upgrading
43
-
44
- For instructions on upgrading from previous rodauth-rails versions, see
45
- [UPGRADING.md](/UPGRADING.md).
46
-
47
45
  ## Installation
48
46
 
49
47
  Add the gem to your Gemfile:
50
48
 
51
49
  ```rb
52
- gem "rodauth-rails", "~> 0.17"
50
+ gem "rodauth-rails", "~> 1.0"
53
51
 
54
52
  # gem "jwt", require: false # for JWT feature
55
53
  # gem "rotp", require: false # for OTP feature
@@ -74,9 +72,9 @@ $ rails generate rodauth:install --jwt # token authentication via the "Authoriza
74
72
  $ bundle add jwt
75
73
  ```
76
74
 
77
- This generator will create a Rodauth app with common authentication features
78
- enabled, a database migration with tables required by those features, a mailer
79
- with default templates, and a few other files.
75
+ This generator will create a Rodauth app and configuration with common
76
+ authentication features enabled, a database migration with tables required by
77
+ those features, a mailer with default templates, and a few other files.
80
78
 
81
79
  Feel free to remove any features you don't need, along with their corresponding
82
80
  tables. Afterwards, run the migration:
@@ -85,11 +83,23 @@ tables. Afterwards, run the migration:
85
83
  $ rails db:migrate
86
84
  ```
87
85
 
86
+ For your mailer to be able to generate email links, you'll need to set up
87
+ default URL options in each environment. Here is a possible configuration for
88
+ `config/environments/development.rb`:
89
+
90
+ ```rb
91
+ config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
92
+ ```
93
+
88
94
  ## Usage
89
95
 
90
96
  ### Routes
91
97
 
92
- You can see the list of routes our Rodauth middleware handles:
98
+ Because requests to Rodauth endpoints are handled by the Rodauth middleware, and
99
+ not a Rails controller, Rodauth routes will not show in `rails routes`.
100
+
101
+ Use the `rodauth:routes` rake task to view the list of endpoints based on
102
+ currently loaded features:
93
103
 
94
104
  ```sh
95
105
  $ rails rodauth:routes
@@ -127,19 +137,6 @@ These routes are fully functional, feel free to visit them and interact with the
127
137
  pages. The templates that ship with Rodauth aim to provide a complete
128
138
  authentication experience, and the forms use [Bootstrap] markup.
129
139
 
130
- Inside Rodauth configuration and the `route` block you can access Rails route
131
- helpers through `#rails_routes`:
132
-
133
- ```rb
134
- class RodauthApp < Rodauth::Rails::App
135
- configure do
136
- # ...
137
- login_redirect { rails_routes.activity_path }
138
- # ...
139
- end
140
- end
141
- ```
142
-
143
140
  ### Current account
144
141
 
145
142
  The `#current_account` method is defined in controllers and views, which
@@ -160,13 +157,15 @@ configurations:
160
157
  current_account(:admin)
161
158
  ```
162
159
 
160
+ #### Custom account model
161
+
163
162
  The `#current_account` method will try to infer the account model class from
164
163
  the configured table name. If that fails, you can set the account model
165
164
  manually:
166
165
 
167
166
  ```rb
168
- # app/lib/rodauth_app.rb
169
- class RodauthApp < Rodauth::Rails::App
167
+ # app/misc/rodauth_main.rb
168
+ class RodauthMain < Rodauth::Rails::Auth
170
169
  configure do
171
170
  # ...
172
171
  rails_account_model Authentication::Account # custom model name
@@ -182,7 +181,7 @@ in your Rodauth app's routing block, which helps keep the authentication logic
182
181
  encapsulated:
183
182
 
184
183
  ```rb
185
- # app/lib/rodauth_app.rb
184
+ # app/misc/rodauth_app.rb
186
185
  class RodauthApp < Rodauth::Rails::App
187
186
  # ...
188
187
  route do |r|
@@ -282,12 +281,12 @@ copy Rodauth templates into your Rails app:
282
281
  $ rails generate rodauth:views
283
282
  ```
284
283
 
285
- This will generate views for the default set of Rodauth features into the
286
- `app/views/rodauth` directory, provided that `RodauthController` is set for the
287
- main configuration.
284
+ This will generate views for Rodauth features you have currently enabled into
285
+ the `app/views/rodauth` directory, provided that `RodauthController` is set for
286
+ the main configuration.
288
287
 
289
288
  You can pass a list of Rodauth features to the generator to create views for
290
- these features (this will not remove or overwrite any existing views):
289
+ these features (this will not remove any existing views):
291
290
 
292
291
  ```sh
293
292
  $ rails generate rodauth:views login create_account lockout otp
@@ -302,7 +301,27 @@ $ rails generate rodauth:views --all
302
301
  Use `--name` to generate views for a different Rodauth configuration:
303
302
 
304
303
  ```sh
305
- $ rails generate rodauth:views --name admin
304
+ $ rails generate rodauth:views webauthn --name admin
305
+ ```
306
+
307
+ #### Page titles
308
+
309
+ The generated view templates use `content_for(:title)` to store Rodauth's page
310
+ titles, which you can then retrieve in your layout template to set the page
311
+ title:
312
+
313
+ ```erb
314
+ <!-- app/views/layouts/application.html.erb -->
315
+ <!DOCTYPE html>
316
+ <html>
317
+ <head>
318
+ <title><%= content_for(:title) %></title>
319
+ <!-- ... -->
320
+ </head>
321
+ <body>
322
+ <!-- ... -->
323
+ </body>
324
+ </html>
306
325
  ```
307
326
 
308
327
  #### Layout
@@ -311,6 +330,7 @@ To use different layouts for different Rodauth views, you can compare the
311
330
  request path in the layout method:
312
331
 
313
332
  ```rb
333
+ # app/controllers/rodauth_controller.rb
314
334
  class RodauthController < ApplicationController
315
335
  layout :rodauth_layout
316
336
 
@@ -331,6 +351,15 @@ class RodauthController < ApplicationController
331
351
  end
332
352
  ```
333
353
 
354
+ #### Turbo
355
+
356
+ [Turbo] has been disabled by default on all built-in and generated view
357
+ templates, because some Rodauth actions (multi-phase login, adding recovery
358
+ codes) aren't Turbo-compatible, as they return 200 responses on POST requests.
359
+
360
+ That being said, most of Rodauth *is* Turbo-compatible, so feel free to enable
361
+ Turbo for actions where you want to use it.
362
+
334
363
  ### Mailer
335
364
 
336
365
  The install generator will create `RodauthMailer` with default email templates,
@@ -340,48 +369,48 @@ flow to use it.
340
369
  ```rb
341
370
  # app/mailers/rodauth_mailer.rb
342
371
  class RodauthMailer < ApplicationMailer
343
- def verify_account(recipient, email_link)
372
+ def verify_account(account_id, key)
344
373
  # ...
345
374
  end
346
- def reset_password(recipient, email_link)
375
+ def reset_password(account_id, key)
347
376
  # ...
348
377
  end
349
- def verify_login_change(recipient, old_login, new_login, email_link)
378
+ def verify_login_change(account_id, old_login, new_login, key)
350
379
  # ...
351
380
  end
352
- def password_changed(recipient)
381
+ def password_changed(account_id)
353
382
  # ...
354
383
  end
355
- # def email_auth(recipient, email_link)
384
+ # def email_auth(account_id, key)
356
385
  # ...
357
386
  # end
358
- # def unlock_account(recipient, email_link)
387
+ # def unlock_account(account_id, key)
359
388
  # ...
360
389
  # end
361
390
  end
362
391
  ```
363
392
  ```rb
364
- # app/lib/rodauth_app.rb
365
- class RodauthApp < Rodauth::Rails::App
393
+ # app/misc/rodauth_main.rb
394
+ class RodauthMain < Rodauth::Rails::Auth
366
395
  configure do
367
396
  # ...
368
397
  create_reset_password_email do
369
- RodauthMailer.reset_password(email_to, reset_password_email_link)
398
+ RodauthMailer.reset_password(account_id, reset_password_key_value)
370
399
  end
371
400
  create_verify_account_email do
372
- RodauthMailer.verify_account(email_to, verify_account_email_link)
401
+ RodauthMailer.verify_account(account_id, verify_account_key_value)
373
402
  end
374
- create_verify_login_change_email do |login|
375
- RodauthMailer.verify_login_change(login, verify_login_change_old_login, verify_login_change_new_login, verify_login_change_email_link)
403
+ create_verify_login_change_email do |_login|
404
+ RodauthMailer.verify_login_change(account_id, verify_login_change_old_login, verify_login_change_new_login, verify_login_change_key_value)
376
405
  end
377
406
  create_password_changed_email do
378
- RodauthMailer.password_changed(email_to)
407
+ RodauthMailer.password_changed(account_id)
379
408
  end
380
409
  # create_email_auth_email do
381
- # RodauthMailer.email_auth(email_to, email_auth_email_link)
410
+ # RodauthMailer.email_auth(account_id, email_auth_key_value)
382
411
  # end
383
412
  # create_unlock_account_email do
384
- # RodauthMailer.unlock_account(email_to, unlock_account_email_link)
413
+ # RodauthMailer.unlock_account(account_id, unlock_account_key_value)
385
414
  # end
386
415
  send_email do |email|
387
416
  # queue email delivery on the mailer after the transaction commits
@@ -399,44 +428,8 @@ deliveries. However, if you want to send emails synchronously, you can modify
399
428
  the configuration to call `#deliver_now` instead.
400
429
 
401
430
  If you're using a background processing library without an Active Job adapter,
402
- or a 3rd-party service for sending transactional emails, this two-phase API
403
- might not be suitable. In this case, instead of overriding `#create_*_email`
404
- and `#send_email`, override the `#send_*_email` methods instead, which are
405
- required to send the email immediately. For example:
406
-
407
- ```rb
408
- # app/workers/rodauth_mailer_worker.rb
409
- class RodauthMailerWorker
410
- include Sidekiq::Worker
411
-
412
- def perform(name, *args)
413
- email = RodauthMailer.public_send(name, *args)
414
- email.deliver_now
415
- end
416
- end
417
- ```
418
- ```rb
419
- # app/lib/rodauth_app.rb
420
- class RodauthApp < Rodauth::Rails::App
421
- configure do
422
- # ...
423
- # use `#send_*_email` method to be able to immediately enqueue email delivery
424
- send_reset_password_email do
425
- enqueue_email(:reset_password, email_to, reset_password_email_link)
426
- end
427
- # ...
428
- auth_class_eval do
429
- # custom method for enqueuing email delivery using our worker
430
- def enqueue_email(name, *args)
431
- db.after_commit do
432
- RodauthMailerWorker.perform_async(name, *args)
433
- end
434
- end
435
- end
436
- # ...
437
- end
438
- end
439
- ```
431
+ or a 3rd-party service for sending transactional emails, see [this wiki
432
+ page][custom mailer worker] on how to set it up.
440
433
 
441
434
  ### Migrations
442
435
 
@@ -458,7 +451,23 @@ class CreateRodauthOtpSmsCodesRecoveryCodes < ActiveRecord::Migration
458
451
  end
459
452
  ```
460
453
 
461
- ### Model
454
+ #### Custom migration name
455
+
456
+ You can change the default migration name:
457
+
458
+ ```sh
459
+ $ rails generate rodauth:migration email_auth --name create_account_email_auth_keys
460
+ ```
461
+ ```rb
462
+ # db/migration/*_create_account_email_auth_keys
463
+ class CreateAccountEmailAuthKeys < ActiveRecord::Migration
464
+ def change
465
+ create_table :account_email_auth_keys do |t| ... end
466
+ end
467
+ end
468
+ ```
469
+
470
+ ## Model
462
471
 
463
472
  The `Rodauth::Rails::Model` mixin can be included into the account model, which
464
473
  defines a password attribute and associations for tables used by enabled
@@ -470,7 +479,7 @@ class Account < ApplicationRecord
470
479
  end
471
480
  ```
472
481
 
473
- #### Password attribute
482
+ ### Password attribute
474
483
 
475
484
  Regardless of whether you're storing the password hash in a column in the
476
485
  accounts table, or in a separate table, the `#password` attribute can be used
@@ -494,7 +503,7 @@ Note that the password attribute doesn't come with validations, making it
494
503
  unsuitable for forms. It was primarily intended to allow easily creating
495
504
  accounts in development console and in tests.
496
505
 
497
- #### Associations
506
+ ### Associations
498
507
 
499
508
  The `Rodauth::Rails::Model` mixin defines associations for Rodauth tables
500
509
  associated to the accounts table:
@@ -544,6 +553,8 @@ class Account < ApplicationRecord
544
553
  end
545
554
  ```
546
555
 
556
+ #### Association reference
557
+
547
558
  Below is a list of all associations defined depending on the features loaded:
548
559
 
549
560
  | Feature | Association | Type | Model | Table (default) |
@@ -568,6 +579,12 @@ Below is a list of all associations defined depending on the features loaded:
568
579
  | webauthn | `:webauthn_keys` | `has_many` | `WebauthnKey` | `account_webauthn_keys` |
569
580
  | webauthn | `:webauthn_user_id` | `has_one` | `WebauthnUserId` | `account_webauthn_user_ids` |
570
581
 
582
+ Note that some Rodauth tables use composite primary keys, which Active Record
583
+ doesn't support out of the box. For associations to work properly, you might
584
+ need to add the [composite_primary_keys] gem to your Gemfile.
585
+
586
+ #### Association options
587
+
571
588
  By default, all associations except for audit logs have `dependent: :destroy`
572
589
  set, to allow for easy deletion of account records in the console. You can use
573
590
  `:association_options` to modify global or per-association options:
@@ -582,31 +599,21 @@ Rodauth::Rails.model(association_options: -> (name) {
582
599
  })
583
600
  ```
584
601
 
585
- Note that some Rodauth tables use composite primary keys, which Active Record
586
- doesn't support out of the box. For associations to work properly, you might
587
- need to add the [composite_primary_keys] gem to your Gemfile.
588
-
589
- ### Multiple configurations
602
+ ## Multiple configurations
590
603
 
591
604
  If you need to handle multiple types of accounts that require different
592
- authentication logic, you can create additional configurations for them:
605
+ authentication logic, you can create new configurations for them. This
606
+ is done by creating new `Rodauth::Rails::Auth` subclasses, and registering
607
+ them under a name.
593
608
 
594
609
  ```rb
595
- # app/lib/rodauth_app.rb
610
+ # app/misc/rodauth_app.rb
596
611
  class RodauthApp < Rodauth::Rails::App
597
612
  # primary configuration
598
- configure do
599
- # ...
600
- end
613
+ configure RodauthMain
601
614
 
602
- # alternative configuration
603
- configure(:admin) do
604
- # ... enable features ...
605
- prefix "/admin"
606
- session_key_prefix "admin_"
607
- remember_cookie_key "_admin_remember" # if using remember feature
608
- # ...
609
- end
615
+ # secondary configuration
616
+ configure RodauthAdmin, :admin
610
617
 
611
618
  route do |r|
612
619
  r.rodauth
@@ -620,100 +627,46 @@ class RodauthApp < Rodauth::Rails::App
620
627
  end
621
628
  end
622
629
  ```
623
-
624
- Then in your application you can reference the secondary Rodauth instance:
625
-
626
630
  ```rb
627
- rodauth(:admin).login_path #=> "/admin/login"
628
- ```
629
-
630
- You'll likely want to save the information of which account belongs to which
631
- configuration to the database. One way would be to have a separate table that
632
- stores account types:
633
-
634
- ```sh
635
- $ rails generate migration create_account_types
636
- ```
637
- ```rb
638
- # db/migrate/*_create_account_types.rb
639
- class CreateAccountTypes < ActiveRecord::Migration
640
- def change
641
- create_table :account_types do |t|
642
- t.references :account, foreign_key: { on_delete: :cascade }, null: false
643
- t.string :type, null: false
644
- end
645
- end
646
- end
647
- ```
648
- ```sh
649
- $ rails db:migrate
650
- ```
651
-
652
- Then an entry would be inserted after account creation, and optionally whenever
653
- Rodauth retrieves accounts you could filter only those belonging to the current
654
- configuration:
655
-
656
- ```rb
657
- # app/lib/rodauth_app.rb
658
- class RodauthApp < Rodauth::Rails::App
659
- configure(:admin) do
660
- # ...
661
- after_create_account do
662
- db[:account_types].insert(account_id: account_id, type: "admin")
663
- end
664
- auth_class_eval do
665
- def account_ds(*)
666
- super.join(:account_types, account_id: :id).where(type: "admin")
667
- end
668
- end
669
- # ...
670
- end
671
- end
672
- ```
673
-
674
- #### Named auth classes
675
-
676
- A `configure` block inside `Rodauth::Rails::App` will internally create an
677
- anonymous `Rodauth::Auth` subclass, and register it under the given name.
678
- However, you can also define the auth classes explicitly, by creating
679
- subclasses of `Rodauth::Rails::Auth`:
680
-
681
- ```rb
682
- # app/lib/rodauth_main.rb
683
- class RodauthMain < Rodauth::Rails::Auth
684
- configure do
685
- # ... main configuration ...
686
- end
687
- end
688
- ```
689
- ```rb
690
- # app/lib/rodauth_admin.rb
631
+ # app/misc/rodauth_admin.rb
691
632
  class RodauthAdmin < Rodauth::Rails::Auth
692
633
  configure do
693
- # ...
634
+ # ... enable features ...
694
635
  prefix "/admin"
695
636
  session_key_prefix "admin_"
637
+ remember_cookie_key "_admin_remember" # if using remember feature
696
638
  # ...
639
+
640
+ # search views in `app/views/admin/rodauth` directory
641
+ rails_controller { Admin::RodauthController }
697
642
  end
698
643
  end
699
644
  ```
700
645
  ```rb
701
- # app/lib/rodauth_app.rb
702
- class RodauthApp < Rodauth::Rails::App
703
- configure RodauthMain
704
- configure RodauthAdmin, :admin
705
- # ...
646
+ # app/controllers/admin/rodauth_controller.rb
647
+ class Admin::RodauthController < ApplicationController
706
648
  end
707
649
  ```
708
650
 
709
- This allows having each configuration in a dedicated file, and named constants
710
- improve introspection and error messages. You can also use inheritance to share
711
- common settings:
651
+ Then in your application you can reference the secondary Rodauth instance:
712
652
 
713
653
  ```rb
714
- # app/lib/rodauth_base.rb
654
+ rodauth(:admin).login_path #=> "/admin/login"
655
+ ```
656
+
657
+ You'll likely want to save the information of which account belongs to which
658
+ configuration to the database. See [this guide][account types] on how you can do
659
+ that.
660
+
661
+ ### Sharing configuration
662
+
663
+ If there are common settings that you want to share between Rodauth
664
+ configurations, you can do so via inheritance:
665
+
666
+ ```rb
667
+ # app/misc/rodauth_base.rb
715
668
  class RodauthBase < Rodauth::Rails::Auth
716
- # common settings that can be shared between multiple configurations
669
+ # common settings that are shared between multiple configurations
717
670
  configure do
718
671
  enable :login, :logout
719
672
  login_return_to_requested_location? true
@@ -723,7 +676,7 @@ class RodauthBase < Rodauth::Rails::Auth
723
676
  end
724
677
  ```
725
678
  ```rb
726
- # app/lib/rodauth_main.rb
679
+ # app/misc/rodauth_main.rb
727
680
  class RodauthMain < RodauthBase # inherit common settings
728
681
  configure do
729
682
  # ... customize main ...
@@ -731,7 +684,7 @@ class RodauthMain < RodauthBase # inherit common settings
731
684
  end
732
685
  ```
733
686
  ```rb
734
- # app/lib/rodauth_admin.rb
687
+ # app/misc/rodauth_admin.rb
735
688
  class RodauthAdmin < RodauthBase # inherit common settings
736
689
  configure do
737
690
  # ... customize admin ...
@@ -739,112 +692,67 @@ class RodauthAdmin < RodauthBase # inherit common settings
739
692
  end
740
693
  ```
741
694
 
742
- Another benefit of explicit classes is that you can define custom methods
743
- directly at the class level instead of inside an `auth_class_eval`:
744
-
745
- ```rb
746
- # app/lib/rodauth_admin.rb
747
- class RodauthAdmin < Rodauth::Rails::Auth
748
- configure do
749
- # ...
750
- end
751
-
752
- def superadmin?
753
- Role.where(account_id: session_id, type: "superadmin").any?
754
- end
755
- end
756
- ```
757
- ```rb
758
- # config/routes.rb
759
- Rails.application.routes.draw do
760
- constraints Rodauth::Rails.authenticated(:admin) { |rodauth| rodauth.superadmin? } do
761
- mount Sidekiq::Web => "sidekiq"
762
- end
763
- end
764
- ```
765
-
766
- ### Calling controller methods
695
+ ## Outside of a request
767
696
 
768
- When using Rodauth before/after hooks or generally overriding your Rodauth
769
- configuration, in some cases you might want to call methods defined on your
770
- controllers. You can do so with `rails_controller_eval`, for example:
771
-
772
- ```rb
773
- # app/controllers/application_controller.rb
774
- class ApplicationController < ActionController::Base
775
- private
776
- def setup_tracking(account_id)
777
- # ... some implementation ...
778
- end
779
- end
780
- ```
781
- ```rb
782
- # app/lib/rodauth_app.rb
783
- class RodauthApp < Rodauth::Rails::App
784
- configure do
785
- after_create_account do
786
- rails_controller_eval { setup_tracking(account_id) }
787
- end
788
- end
789
- end
790
- ```
791
-
792
- ### Outside of a request
697
+ ### Calling actions
793
698
 
794
699
  In some cases you might need to use Rodauth more programmatically. If you want
795
700
  to perform authentication operations outside of request context, Rodauth ships
796
701
  with the [internal_request] feature just for that.
797
702
 
798
703
  ```rb
799
- # app/lib/rodauth_app.rb
800
- class RodauthApp < Rodauth::Rails::App
704
+ # app/misc/rodauth_main.rb
705
+ class RodauthMain < Rodauth::Rails::Auth
801
706
  configure do
802
707
  enable :internal_request
803
708
  end
804
709
  end
805
710
  ```
806
711
  ```rb
807
- # main configuration
712
+ # primary configuration
808
713
  RodauthApp.rodauth.create_account(login: "user@example.com", password: "secret")
809
714
  RodauthApp.rodauth.verify_account(account_login: "user@example.com")
810
715
 
811
716
  # secondary configuration
812
- RodauthApp.rodauth(:admin).close_account(account_login: "admin@example.com")
717
+ RodauthApp.rodauth(:admin).close_account(account_login: "user@example.com")
813
718
  ```
814
719
 
815
720
  The rodauth-rails gem additionally updates the internal rack env hash with your
816
721
  `config.action_mailer.default_url_options`, which is used for generating email
817
722
  links.
818
723
 
724
+ ### Generating URLs
725
+
819
726
  For generating authentication URLs outside of a request use the
820
727
  [path_class_methods] plugin:
821
728
 
822
729
  ```rb
823
- # app/lib/rodauth_app.rb
824
- class RodauthApp < Rodauth::Rails::App
730
+ # app/misc/rodauth_main.rb
731
+ class RodauthMain < Rodauth::Rails::Auth
825
732
  configure do
826
733
  enable :path_class_methods
734
+ create_account_route "register"
827
735
  end
828
736
  end
829
737
  ```
830
738
  ```rb
831
- # main configuration
832
- RodauthApp.rodauth.create_account_path
833
- RodauthApp.rodauth.verify_account_url(key: "abc123")
739
+ # primary configuration
740
+ RodauthApp.rodauth.create_account_path # => "/register"
741
+ RodauthApp.rodauth.verify_account_url(key: "abc123") #=> "https://example.com/verify-account?key=abc123"
834
742
 
835
743
  # secondary configuration
836
- RodauthApp.rodauth(:admin).close_account_path
744
+ RodauthApp.rodauth(:admin).close_account_path(foo: "bar") #=> "/admin/close-account?foo=bar"
837
745
  ```
838
746
 
839
- #### Calling instance methods
747
+ ### Calling instance methods
840
748
 
841
749
  If you need to access Rodauth methods not exposed as internal requests, you can
842
750
  use `Rodauth::Rails.rodauth` to retrieve the Rodauth instance used by the
843
751
  internal_request feature:
844
752
 
845
753
  ```rb
846
- # app/lib/rodauth_app.rb
847
- class RodauthApp < Rodauth::Rails::App
754
+ # app/misc/rodauth_main.rb
755
+ class RodauthMain < Rodauth::Rails::Auth
848
756
  configure do
849
757
  enable :internal_request # this is required
850
758
  end
@@ -852,7 +760,7 @@ end
852
760
  ```
853
761
  ```rb
854
762
  account = Account.find_by!(email: "user@example.com")
855
- rodauth = Rodauth::Rails.rodauth(account: account)
763
+ rodauth = Rodauth::Rails.rodauth(account: account) #=> #<RodauthMain::InternalRequest ...>
856
764
 
857
765
  rodauth.compute_hmac("token") #=> "TpEJTKfKwqYvIDKWsuZhkhKlhaBXtR1aodskBAflD8U"
858
766
  rodauth.open_account? #=> true
@@ -971,178 +879,10 @@ connection, using the [sequel-activerecord_connection] gem.
971
879
  This means that, from the usage perspective, Sequel can be considered just
972
880
  as an implementation detail of Rodauth.
973
881
 
974
- ## JSON API
975
-
976
- To make Rodauth endpoints accessible via JSON API, enable the [`json`][json]
977
- feature:
978
-
979
- ```rb
980
- # app/lib/rodauth_app.rb
981
- class RodauthApp < Rodauth::Rails::App
982
- configure do
983
- # ...
984
- enable :json
985
- only_json? true # accept only JSON requests (optional)
986
- # ...
987
- end
988
- end
989
- ```
990
-
991
- This will store account session data into the Rails session. If you rather want
992
- stateless token-based authentication via the `Authorization` header, enable the
993
- [`jwt`][jwt] feature (which builds on top of the `json` feature) and add the
994
- [JWT gem] to the Gemfile:
995
-
996
- ```sh
997
- $ bundle add jwt
998
- ```
999
- ```rb
1000
- # app/lib/rodauth_app.rb
1001
- class RodauthApp < Rodauth::Rails::App
1002
- configure do
1003
- # ...
1004
- enable :jwt
1005
- jwt_secret "<YOUR_SECRET_KEY>" # store the JWT secret in a safe place
1006
- only_json? true # accept only JSON requests (optional)
1007
- # ...
1008
- end
1009
- end
1010
- ```
1011
-
1012
- The JWT token will be returned after each request to Rodauth routes. To also
1013
- return the JWT token on requests to your app's routes, you can add the
1014
- following code to your base controller:
1015
-
1016
- ```rb
1017
- class ApplicationController < ActionController::Base
1018
- # ...
1019
- after_action :set_jwt_token
1020
-
1021
- private
1022
-
1023
- def set_jwt_token
1024
- if rodauth.use_jwt? && rodauth.valid_jwt?
1025
- response.headers["Authorization"] = rodauth.session_jwt
1026
- end
1027
- end
1028
- # ...
1029
- end
1030
- ```
1031
-
1032
- ## OmniAuth
1033
-
1034
- While Rodauth doesn't yet come with [OmniAuth] integration, we can build one
1035
- ourselves using the existing Rodauth API.
1036
-
1037
- In order to allow the user to login via multiple external providers, let's
1038
- create an `account_identities` table that will have a many-to-one relationship
1039
- with the `accounts` table:
1040
-
1041
- ```sh
1042
- $ rails generate model AccountIdentity
1043
- ```
1044
- ```rb
1045
- # db/migrate/*_create_account_identities.rb
1046
- class CreateAccountIdentities < ActiveRecord::Migration
1047
- def change
1048
- create_table :account_identities do |t|
1049
- t.references :account, null: false, foreign_key: { on_delete: :cascade }
1050
- t.string :provider, null: false
1051
- t.string :uid, null: false
1052
- t.jsonb :info, null: false, default: {} # adjust JSON column type for your database
1053
-
1054
- t.timestamps
1055
-
1056
- t.index [:provider, :uid], unique: true
1057
- end
1058
- end
1059
- end
1060
- ```
1061
- ```rb
1062
- # app/models/account_identity.rb
1063
- class AcccountIdentity < ApplicationRecord
1064
- belongs_to :account
1065
- end
1066
- ```
1067
- ```rb
1068
- # app/models/account.rb
1069
- class Account < ApplicationRecord
1070
- has_many :identities, class_name: "AccountIdentity"
1071
- end
1072
- ```
1073
-
1074
- Let's assume we want to implement Facebook login, and have added the
1075
- corresponding OmniAuth strategy to the middleware stack, together with an
1076
- authorization link on the login form:
1077
-
1078
- ```rb
1079
- Rails.application.config.middleware.use OmniAuth::Builder do
1080
- provider :facebook, ENV["FACEBOOK_APP_ID"], ENV["FACEBOOK_APP_SECRET"],
1081
- scope: "email", callback_path: "/auth/facebook/callback"
1082
- end
1083
- ```
1084
- ```erb
1085
- <%= link_to "Login via Facebook", "/auth/facebook" %>
1086
- ```
1087
-
1088
- Finally, let's implement the OmniAuth callback endpoint on our Rodauth
1089
- controller:
1090
-
1091
- ```rb
1092
- # config/routes.rb
1093
- Rails.application.routes.draw do
1094
- # ...
1095
- get "/auth/:provider/callback", to: "rodauth#omniauth"
1096
- end
1097
- ```
1098
- ```rb
1099
- # app/controllres/rodauth_controller.rb
1100
- class RodauthController < ApplicationController
1101
- def omniauth
1102
- auth = request.env["omniauth.auth"]
1103
-
1104
- # attempt to find existing identity directly
1105
- identity = AccountIdentity.find_by(provider: auth["provider"], uid: auth["uid"])
1106
-
1107
- if identity
1108
- # update any external info changes
1109
- identity.update!(info: auth["info"])
1110
- # set account from identity
1111
- account = identity.account
1112
- end
1113
-
1114
- # attempt to find an existing account by email
1115
- account ||= Account.find_by(email: auth["info"]["email"])
1116
-
1117
- # disallow login if account is not verified
1118
- if account && account.status != rodauth.account_open_status_value
1119
- redirect_to rodauth.login_path, alert: rodauth.unverified_account_message
1120
- return
1121
- end
1122
-
1123
- # create new account if it doesn't exist
1124
- unless account
1125
- account = Account.create!(email: auth["info"]["email"], status: rodauth.account_open_status_value)
1126
- end
1127
-
1128
- # create new identity if it doesn't exist
1129
- unless identity
1130
- account.identities.create!(provider: auth["provider"], uid: auth["uid"], info: auth["info"])
1131
- end
1132
-
1133
- # load the account into the rodauth instance
1134
- rodauth.account_from_login(account.email)
1135
-
1136
- rodauth_response do # ensures any `after_action` callbacks get called
1137
- # sign in the loaded account
1138
- rodauth.login("omniauth")
1139
- end
1140
- end
1141
- end
1142
- ```
1143
-
1144
882
  ## Configuring
1145
883
 
884
+ ### Configuration methods
885
+
1146
886
  The `rails` feature rodauth-rails loads provides the following configuration
1147
887
  methods:
1148
888
 
@@ -1157,6 +897,8 @@ methods:
1157
897
  | `rails_controller` | Controller class to use for rendering and CSRF protection. |
1158
898
  | `rails_account_model` | Model class connected with the accounts table. |
1159
899
 
900
+ ### General configuration
901
+
1160
902
  The `Rodauth::Rails` module has a few config settings available as well:
1161
903
 
1162
904
  | Name | Description |
@@ -1175,169 +917,102 @@ end
1175
917
  For the list of configuration methods provided by Rodauth, see the [feature
1176
918
  documentation].
1177
919
 
1178
- ## Custom extensions
1179
-
1180
- When developing custom extensions for Rodauth inside your Rails project, it's
1181
- probably better to use plain modules, at least in the beginning, as Rodauth
1182
- feature design doesn't yet work well with Zeitwerk reloading.
920
+ ### Defining custom methods
1183
921
 
1184
- Here is an example of an LDAP authentication extension that uses the
1185
- [simple_ldap_authenticator] gem.
922
+ All Rodauth configuration methods are just syntax sugar for defining instance
923
+ methods on the auth class. You can also define your own custom methods on the
924
+ auth class:
1186
925
 
1187
926
  ```rb
1188
- # app/lib/rodauth_ldap.rb
1189
- module RodauthLdap
1190
- def require_bcrypt?
1191
- false
927
+ class RodauthMain < Rodauth::Rails::Auth
928
+ configure do
929
+ password_match? { |password| ldap_valid?(password) }
930
+ end
931
+
932
+ # Example external identities table
933
+ def identities
934
+ db[:account_identities].where(account_id: account_id).all
1192
935
  end
1193
936
 
1194
- def password_match?(password)
937
+ private
938
+
939
+ # Example LDAP authentication
940
+ def ldap_valid?(password)
1195
941
  SimpleLdapAuthenticator.valid?(account[:email], password)
1196
942
  end
1197
943
  end
1198
944
  ```
1199
945
  ```rb
1200
- # app/lib/rodauth_app.rb
1201
- class RodauthApp < Rodauth::Rails::App
1202
- configure do
1203
- # ...
1204
- auth_class_eval do
1205
- include RodauthLdap
1206
- end
1207
- # ...
1208
- end
1209
- end
946
+ rodauth.identities #=> [{ provider: "facebook", uid: "abc123", ... }, ...]
1210
947
  ```
1211
948
 
1212
- ## Testing
949
+ ### Rails URL helpers
1213
950
 
1214
- System (browser) tests for Rodauth actions could look something like this:
951
+ Inside Rodauth configuration and the `route` block you can access Rails route
952
+ helpers through `#rails_routes`:
1215
953
 
1216
954
  ```rb
1217
- # test/system/authentication_test.rb
1218
- require "test_helper"
1219
-
1220
- class AuthenticationTest < ActionDispatch::SystemTestCase
1221
- include ActiveJob::TestHelper
1222
- driven_by :rack_test
1223
-
1224
- test "creating and verifying an account" do
1225
- create_account
1226
- assert_match "An email has been sent to you with a link to verify your account", page.text
1227
-
1228
- verify_account
1229
- assert_match "Your account has been verified", page.text
955
+ # app/misc/rodauth_main.rb
956
+ class RodauthMain < Rodauth::Rails::Auth
957
+ configure do
958
+ login_redirect { rails_routes.activity_path }
1230
959
  end
960
+ end
961
+ ```
1231
962
 
1232
- test "logging in and logging out" do
1233
- create_account(verify: true)
1234
-
1235
- logout
1236
- assert_match "You have been logged out", page.text
963
+ ### Calling controller methods
1237
964
 
1238
- login
1239
- assert_match "You have been logged in", page.text
1240
- end
965
+ When using Rodauth before/after hooks or generally overriding your Rodauth
966
+ configuration, in some cases you might want to call methods defined on your
967
+ controllers. You can do so with `rails_controller_eval`, for example:
1241
968
 
969
+ ```rb
970
+ # app/controllers/application_controller.rb
971
+ class ApplicationController < ActionController::Base
1242
972
  private
1243
-
1244
- def create_account(email: "user@example.com", password: "secret", verify: false)
1245
- visit "/create-account"
1246
- fill_in "Login", with: email
1247
- fill_in "Password", with: password
1248
- fill_in "Confirm Password", with: password
1249
- click_on "Create Account"
1250
- verify_account if verify
1251
- end
1252
-
1253
- def verify_account
1254
- perform_enqueued_jobs # run enqueued email deliveries
1255
- email = ActionMailer::Base.deliveries.last
1256
- verify_account_link = email.body.to_s[/\S+verify-account\S+/]
1257
- visit verify_account_link
1258
- click_on "Verify Account"
1259
- end
1260
-
1261
- def login(email: "user@example.com", password: "secret")
1262
- visit "/login"
1263
- fill_in "Login", with: email
1264
- fill_in "Password", with: password
1265
- click_on "Login"
1266
- end
1267
-
1268
- def logout
1269
- visit "/logout"
1270
- click_on "Logout"
973
+ def setup_tracking(account_id)
974
+ # ... some implementation ...
1271
975
  end
1272
976
  end
1273
977
  ```
1274
-
1275
- While request tests in JSON API mode with JWT tokens could look something like
1276
- this:
1277
-
1278
978
  ```rb
1279
- # test/integration/authentication_test.rb
1280
- require "test_helper"
1281
-
1282
- class AuthenticationTest < ActionDispatch::IntegrationTest
1283
- test "creating and verifying an account" do
1284
- create_account
1285
- assert_response :success
1286
- assert_match "An email has been sent to you with a link to verify your account", JSON.parse(body)["success"]
1287
-
1288
- verify_account
1289
- assert_response :success
1290
- assert_match "Your account has been verified", JSON.parse(body)["success"]
1291
- end
1292
-
1293
- test "logging in and logging out" do
1294
- create_account(verify: true)
1295
-
1296
- logout
1297
- assert_response :success
1298
- assert_match "You have been logged out", JSON.parse(body)["success"]
1299
-
1300
- login
1301
- assert_response :success
1302
- assert_match "You have been logged in", JSON.parse(body)["success"]
979
+ # app/misc/rodauth_main.rb
980
+ class RodauthMain < Rodauth::Rails::Auth
981
+ configure do
982
+ after_create_account do
983
+ rails_controller_eval { setup_tracking(account_id) }
984
+ end
1303
985
  end
986
+ end
987
+ ```
1304
988
 
1305
- private
989
+ ### Single-file configuration
1306
990
 
1307
- def create_account(email: "user@example.com", password: "secret", verify: false)
1308
- post "/create-account", as: :json, params: { login: email, password: password, "password-confirm": password }
1309
- verify_account if verify
1310
- end
991
+ If you would prefer to have all Rodauth logic contained inside a single file,
992
+ you call `Rodauth::Rails::App.configure` with a block, which will create an
993
+ anonymous auth class.
1311
994
 
1312
- def verify_account
1313
- perform_enqueued_jobs # run enqueued email deliveries
1314
- email = ActionMailer::Base.deliveries.last
1315
- verify_account_key = email.body.to_s[/verify-account\?key=(\S+)/, 1]
1316
- post "/verify-account", as: :json, params: { key: verify_account_key }
995
+ ```rb
996
+ # app/misc/rodauth_app.rb
997
+ class RodauthApp < Rodauth::Rails::App
998
+ # primary configuration
999
+ configure do
1000
+ enable :login, :logout, :create_account, :verify_account
1001
+ # ...
1317
1002
  end
1318
1003
 
1319
- def login(email: "user@example.com", password: "secret")
1320
- post "/login", as: :json, params: { login: email, password: password }
1004
+ # secondary configuration
1005
+ configure(:admin) do
1006
+ enable :email_auth, :single_session
1007
+ # ...
1321
1008
  end
1322
1009
 
1323
- def logout
1324
- post "/logout", as: :json, headers: { "Authorization" => headers["Authorization"] }
1010
+ route do |r|
1011
+ # ...
1325
1012
  end
1326
1013
  end
1327
1014
  ```
1328
1015
 
1329
- If you're delivering emails in the background, make sure to set Active Job
1330
- queue adapter to `:test` or `:inline`:
1331
-
1332
- ```rb
1333
- # config/environments/test.rb
1334
- Rails.application.configure do |config|
1335
- # ...
1336
- config.active_job.queue_adapter = :test # or :inline
1337
- # ...
1338
- end
1339
- ```
1340
-
1341
1016
  ## Rodauth defaults
1342
1017
 
1343
1018
  rodauth-rails changes some of the default Rodauth settings for easier setup:
@@ -1444,7 +1119,6 @@ conduct](https://github.com/janko/rodauth-rails/blob/master/CODE_OF_CONDUCT.md).
1444
1119
  [Rodauth]: https://github.com/jeremyevans/rodauth
1445
1120
  [Sequel]: https://github.com/jeremyevans/sequel
1446
1121
  [feature documentation]: http://rodauth.jeremyevans.net/documentation.html
1447
- [JWT gem]: https://github.com/jwt/ruby-jwt
1448
1122
  [Bootstrap]: https://getbootstrap.com/
1449
1123
  [Roda]: http://roda.jeremyevans.net/
1450
1124
  [HMAC]: http://rodauth.jeremyevans.net/rdoc/files/README_rdoc.html#label-HMAC
@@ -1453,7 +1127,6 @@ conduct](https://github.com/janko/rodauth-rails/blob/master/CODE_OF_CONDUCT.md).
1453
1127
  [sequel-activerecord_connection]: https://github.com/janko/sequel-activerecord_connection
1454
1128
  [plugin options]: http://rodauth.jeremyevans.net/rdoc/files/README_rdoc.html#label-Plugin+Options
1455
1129
  [hmac]: http://rodauth.jeremyevans.net/rdoc/files/README_rdoc.html#label-HMAC
1456
- [OmniAuth]: https://github.com/omniauth/omniauth
1457
1130
  [otp]: http://rodauth.jeremyevans.net/rdoc/files/doc/otp_rdoc.html
1458
1131
  [sms_codes]: http://rodauth.jeremyevans.net/rdoc/files/doc/sms_codes_rdoc.html
1459
1132
  [recovery_codes]: http://rodauth.jeremyevans.net/rdoc/files/doc/recovery_codes_rdoc.html
@@ -1474,3 +1147,6 @@ conduct](https://github.com/janko/rodauth-rails/blob/master/CODE_OF_CONDUCT.md).
1474
1147
  [internal_request]: http://rodauth.jeremyevans.net/rdoc/files/doc/internal_request_rdoc.html
1475
1148
  [composite_primary_keys]: https://github.com/composite-primary-keys/composite_primary_keys
1476
1149
  [path_class_methods]: https://rodauth.jeremyevans.net/rdoc/files/doc/path_class_methods_rdoc.html
1150
+ [account types]: https://github.com/janko/rodauth-rails/wiki/Account-Types
1151
+ [custom mailer worker]: https://github.com/janko/rodauth-rails/wiki/Custom-Mailer-Worker
1152
+ [Turbo]: https://turbo.hotwired.dev/