rodauth-rails 0.18.1 → 1.2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +52 -0
- data/LICENSE.txt +1 -1
- data/README.md +372 -653
- data/lib/generators/rodauth/install_generator.rb +32 -35
- data/lib/generators/rodauth/migration/active_sessions.erb +2 -2
- data/lib/generators/rodauth/migration/audit_logging.erb +1 -1
- data/lib/generators/rodauth/migration/base.erb +2 -2
- data/lib/generators/rodauth/migration/email_auth.erb +1 -1
- data/lib/generators/rodauth/migration/otp.erb +1 -1
- data/lib/generators/rodauth/migration/password_expiration.erb +1 -1
- data/lib/generators/rodauth/migration/reset_password.erb +1 -1
- data/lib/generators/rodauth/migration/sms_codes.erb +1 -1
- data/lib/generators/rodauth/migration/verify_account.erb +2 -2
- data/lib/generators/rodauth/migration/webauthn.erb +1 -1
- data/lib/generators/rodauth/migration_generator.rb +9 -2
- data/lib/generators/rodauth/migration_helpers.rb +8 -0
- data/lib/generators/rodauth/templates/INSTRUCTIONS +40 -0
- data/lib/generators/rodauth/templates/app/mailers/rodauth_mailer.rb +36 -19
- data/lib/generators/rodauth/templates/app/misc/rodauth_app.rb +27 -0
- data/lib/generators/rodauth/templates/app/{lib/rodauth_app.rb → misc/rodauth_main.rb} +10 -56
- data/lib/generators/rodauth/templates/app/models/account.rb +1 -0
- data/lib/generators/rodauth/templates/app/views/rodauth/_email_auth_request_form.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/change_login.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/change_password.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/close_account.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/confirm_password.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/create_account.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/email_auth.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/logout.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/otp_auth.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/otp_disable.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/otp_setup.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/recovery_auth.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/remember.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/reset_password.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/reset_password_request.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/sms_auth.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/sms_confirm.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/sms_disable.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/sms_request.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/sms_setup.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/two_factor_disable.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/unlock_account.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/unlock_account_request.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/verify_account.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/verify_account_resend.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/verify_login_change.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/webauthn_auth.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/webauthn_remove.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/webauthn_setup.html.erb +1 -1
- data/lib/rodauth/rails/app.rb +18 -4
- data/lib/rodauth/rails/auth.rb +1 -16
- data/lib/rodauth/rails/controller_methods.rb +4 -29
- data/lib/rodauth/rails/feature/base.rb +21 -0
- data/lib/rodauth/rails/feature/instrumentation.rb +1 -1
- data/lib/rodauth/rails/feature/internal_request.rb +10 -4
- data/lib/rodauth/rails/feature/render.rb +8 -0
- data/lib/rodauth/rails/tasks.rake +2 -2
- data/lib/rodauth/rails/version.rb +1 -1
- data/lib/rodauth/rails.rb +9 -20
- data/rodauth-rails.gemspec +1 -1
- metadata +8 -6
data/README.md
CHANGED
|
@@ -4,16 +4,23 @@ Provides Rails integration for the [Rodauth] authentication framework.
|
|
|
4
4
|
|
|
5
5
|
## Resources
|
|
6
6
|
|
|
7
|
-
Useful links:
|
|
7
|
+
🔗 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
|
+
🎥 Screencasts:
|
|
16
|
+
|
|
17
|
+
* [Rails Authentication with Rodauth](https://www.youtube.com/watch?v=2hDpNikacf0)
|
|
18
|
+
|
|
19
|
+
📚 Articles:
|
|
13
20
|
|
|
14
21
|
* [Rodauth: A Refreshing Authentication Solution for Ruby](https://janko.io/rodauth-a-refreshing-authentication-solution-for-ruby/)
|
|
15
|
-
* [
|
|
16
|
-
* [
|
|
22
|
+
* [Rails Authentication with Rodauth](https://janko.io/adding-authentication-in-rails-with-rodauth/)
|
|
23
|
+
* [Multifactor Authentication in Rails with Rodauth](https://janko.io/adding-multifactor-authentication-in-rails-with-rodauth/)
|
|
17
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)
|
|
18
25
|
|
|
19
26
|
## Why Rodauth?
|
|
@@ -39,17 +46,12 @@ Active Record. There are good reasons for this, and to make Rodauth work
|
|
|
39
46
|
smoothly alongside Active Record, rodauth-rails configures Sequel to [reuse
|
|
40
47
|
Active Record's database connection][sequel-activerecord_connection].
|
|
41
48
|
|
|
42
|
-
## Upgrading
|
|
43
|
-
|
|
44
|
-
For instructions on upgrading from previous rodauth-rails versions, see
|
|
45
|
-
[UPGRADING.md](/UPGRADING.md).
|
|
46
|
-
|
|
47
49
|
## Installation
|
|
48
50
|
|
|
49
51
|
Add the gem to your Gemfile:
|
|
50
52
|
|
|
51
53
|
```rb
|
|
52
|
-
gem "rodauth-rails", "~> 0
|
|
54
|
+
gem "rodauth-rails", "~> 1.0"
|
|
53
55
|
|
|
54
56
|
# gem "jwt", require: false # for JWT feature
|
|
55
57
|
# gem "rotp", require: false # for OTP feature
|
|
@@ -74,9 +76,9 @@ $ rails generate rodauth:install --jwt # token authentication via the "Authoriza
|
|
|
74
76
|
$ bundle add jwt
|
|
75
77
|
```
|
|
76
78
|
|
|
77
|
-
This generator will create a Rodauth app with common
|
|
78
|
-
enabled, a database migration with tables required by
|
|
79
|
-
with default templates, and a few other files.
|
|
79
|
+
This generator will create a Rodauth app and configuration with common
|
|
80
|
+
authentication features enabled, a database migration with tables required by
|
|
81
|
+
those features, a mailer with default templates, and a few other files.
|
|
80
82
|
|
|
81
83
|
Feel free to remove any features you don't need, along with their corresponding
|
|
82
84
|
tables. Afterwards, run the migration:
|
|
@@ -85,11 +87,23 @@ tables. Afterwards, run the migration:
|
|
|
85
87
|
$ rails db:migrate
|
|
86
88
|
```
|
|
87
89
|
|
|
90
|
+
For your mailer to be able to generate email links, you'll need to set up
|
|
91
|
+
default URL options in each environment. Here is a possible configuration for
|
|
92
|
+
`config/environments/development.rb`:
|
|
93
|
+
|
|
94
|
+
```rb
|
|
95
|
+
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
|
|
96
|
+
```
|
|
97
|
+
|
|
88
98
|
## Usage
|
|
89
99
|
|
|
90
100
|
### Routes
|
|
91
101
|
|
|
92
|
-
|
|
102
|
+
Because requests to Rodauth endpoints are handled by a Rack middleware (and not
|
|
103
|
+
a Rails controller), Rodauth routes will not show in `rails routes`.
|
|
104
|
+
|
|
105
|
+
Use the `rodauth:routes` rake task to view the list of endpoints based on
|
|
106
|
+
currently loaded features:
|
|
93
107
|
|
|
94
108
|
```sh
|
|
95
109
|
$ rails rodauth:routes
|
|
@@ -127,32 +141,17 @@ These routes are fully functional, feel free to visit them and interact with the
|
|
|
127
141
|
pages. The templates that ship with Rodauth aim to provide a complete
|
|
128
142
|
authentication experience, and the forms use [Bootstrap] markup.
|
|
129
143
|
|
|
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
144
|
### Current account
|
|
144
145
|
|
|
145
146
|
The `#current_account` method is defined in controllers and views, which
|
|
146
|
-
returns the model instance of the currently logged in account.
|
|
147
|
+
returns the model instance of the currently logged in account. If the account
|
|
148
|
+
doesn't exist in the database, the session will be cleared.
|
|
147
149
|
|
|
148
150
|
```rb
|
|
149
151
|
current_account #=> #<Account id=123 email="user@example.com">
|
|
150
152
|
current_account.email #=> "user@example.com"
|
|
151
153
|
```
|
|
152
154
|
|
|
153
|
-
If the account doesn't exist in the database, the session will be cleared and
|
|
154
|
-
login required.
|
|
155
|
-
|
|
156
155
|
Pass the configuration name to retrieve accounts belonging to other Rodauth
|
|
157
156
|
configurations:
|
|
158
157
|
|
|
@@ -160,13 +159,17 @@ configurations:
|
|
|
160
159
|
current_account(:admin)
|
|
161
160
|
```
|
|
162
161
|
|
|
162
|
+
This just delegates to the `#rails_account` method on the Rodauth object.
|
|
163
|
+
|
|
164
|
+
#### Custom account model
|
|
165
|
+
|
|
163
166
|
The `#current_account` method will try to infer the account model class from
|
|
164
167
|
the configured table name. If that fails, you can set the account model
|
|
165
168
|
manually:
|
|
166
169
|
|
|
167
170
|
```rb
|
|
168
|
-
# app/
|
|
169
|
-
class
|
|
171
|
+
# app/misc/rodauth_main.rb
|
|
172
|
+
class RodauthMain < Rodauth::Rails::Auth
|
|
170
173
|
configure do
|
|
171
174
|
# ...
|
|
172
175
|
rails_account_model Authentication::Account # custom model name
|
|
@@ -182,7 +185,7 @@ in your Rodauth app's routing block, which helps keep the authentication logic
|
|
|
182
185
|
encapsulated:
|
|
183
186
|
|
|
184
187
|
```rb
|
|
185
|
-
# app/
|
|
188
|
+
# app/misc/rodauth_app.rb
|
|
186
189
|
class RodauthApp < Rodauth::Rails::App
|
|
187
190
|
# ...
|
|
188
191
|
route do |r|
|
|
@@ -249,6 +252,18 @@ Rails.application.routes.draw do
|
|
|
249
252
|
end
|
|
250
253
|
```
|
|
251
254
|
|
|
255
|
+
The current account can be retrieved via the `#rails_account` method:
|
|
256
|
+
|
|
257
|
+
```rb
|
|
258
|
+
# config/routes.rb
|
|
259
|
+
Rails.application.routes.draw do
|
|
260
|
+
# require user to be admin
|
|
261
|
+
constraints Rodauth::Rails.authenticated { |rodauth| rodauth.rails_account.admin? } do
|
|
262
|
+
# ...
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
```
|
|
266
|
+
|
|
252
267
|
You can specify the Rodauth configuration by passing the configuration name:
|
|
253
268
|
|
|
254
269
|
```rb
|
|
@@ -305,12 +320,33 @@ Use `--name` to generate views for a different Rodauth configuration:
|
|
|
305
320
|
$ rails generate rodauth:views webauthn --name admin
|
|
306
321
|
```
|
|
307
322
|
|
|
323
|
+
#### Page titles
|
|
324
|
+
|
|
325
|
+
The generated view templates use `content_for(:title)` to store Rodauth's page
|
|
326
|
+
titles, which you can then retrieve in your layout template to set the page
|
|
327
|
+
title:
|
|
328
|
+
|
|
329
|
+
```erb
|
|
330
|
+
<!-- app/views/layouts/application.html.erb -->
|
|
331
|
+
<!DOCTYPE html>
|
|
332
|
+
<html>
|
|
333
|
+
<head>
|
|
334
|
+
<title><%= content_for(:title) %></title>
|
|
335
|
+
<!-- ... -->
|
|
336
|
+
</head>
|
|
337
|
+
<body>
|
|
338
|
+
<!-- ... -->
|
|
339
|
+
</body>
|
|
340
|
+
</html>
|
|
341
|
+
```
|
|
342
|
+
|
|
308
343
|
#### Layout
|
|
309
344
|
|
|
310
345
|
To use different layouts for different Rodauth views, you can compare the
|
|
311
346
|
request path in the layout method:
|
|
312
347
|
|
|
313
348
|
```rb
|
|
349
|
+
# app/controllers/rodauth_controller.rb
|
|
314
350
|
class RodauthController < ApplicationController
|
|
315
351
|
layout :rodauth_layout
|
|
316
352
|
|
|
@@ -331,6 +367,15 @@ class RodauthController < ApplicationController
|
|
|
331
367
|
end
|
|
332
368
|
```
|
|
333
369
|
|
|
370
|
+
#### Turbo
|
|
371
|
+
|
|
372
|
+
[Turbo] has been disabled by default on all built-in and generated view
|
|
373
|
+
templates, because some Rodauth actions (multi-phase login, adding recovery
|
|
374
|
+
codes) aren't Turbo-compatible, as they return 200 responses on POST requests.
|
|
375
|
+
|
|
376
|
+
That being said, most of Rodauth *is* Turbo-compatible, so feel free to enable
|
|
377
|
+
Turbo for actions where you want to use it.
|
|
378
|
+
|
|
334
379
|
### Mailer
|
|
335
380
|
|
|
336
381
|
The install generator will create `RodauthMailer` with default email templates,
|
|
@@ -340,48 +385,48 @@ flow to use it.
|
|
|
340
385
|
```rb
|
|
341
386
|
# app/mailers/rodauth_mailer.rb
|
|
342
387
|
class RodauthMailer < ApplicationMailer
|
|
343
|
-
def verify_account(
|
|
388
|
+
def verify_account(account_id, key)
|
|
344
389
|
# ...
|
|
345
390
|
end
|
|
346
|
-
def reset_password(
|
|
391
|
+
def reset_password(account_id, key)
|
|
347
392
|
# ...
|
|
348
393
|
end
|
|
349
|
-
def verify_login_change(
|
|
394
|
+
def verify_login_change(account_id, old_login, new_login, key)
|
|
350
395
|
# ...
|
|
351
396
|
end
|
|
352
|
-
def password_changed(
|
|
397
|
+
def password_changed(account_id)
|
|
353
398
|
# ...
|
|
354
399
|
end
|
|
355
|
-
# def email_auth(
|
|
400
|
+
# def email_auth(account_id, key)
|
|
356
401
|
# ...
|
|
357
402
|
# end
|
|
358
|
-
# def unlock_account(
|
|
403
|
+
# def unlock_account(account_id, key)
|
|
359
404
|
# ...
|
|
360
405
|
# end
|
|
361
406
|
end
|
|
362
407
|
```
|
|
363
408
|
```rb
|
|
364
|
-
# app/
|
|
365
|
-
class
|
|
409
|
+
# app/misc/rodauth_main.rb
|
|
410
|
+
class RodauthMain < Rodauth::Rails::Auth
|
|
366
411
|
configure do
|
|
367
412
|
# ...
|
|
368
413
|
create_reset_password_email do
|
|
369
|
-
RodauthMailer.reset_password(
|
|
414
|
+
RodauthMailer.reset_password(account_id, reset_password_key_value)
|
|
370
415
|
end
|
|
371
416
|
create_verify_account_email do
|
|
372
|
-
RodauthMailer.verify_account(
|
|
417
|
+
RodauthMailer.verify_account(account_id, verify_account_key_value)
|
|
373
418
|
end
|
|
374
|
-
create_verify_login_change_email do |
|
|
375
|
-
RodauthMailer.verify_login_change(
|
|
419
|
+
create_verify_login_change_email do |_login|
|
|
420
|
+
RodauthMailer.verify_login_change(account_id, verify_login_change_old_login, verify_login_change_new_login, verify_login_change_key_value)
|
|
376
421
|
end
|
|
377
422
|
create_password_changed_email do
|
|
378
|
-
RodauthMailer.password_changed(
|
|
423
|
+
RodauthMailer.password_changed(account_id)
|
|
379
424
|
end
|
|
380
425
|
# create_email_auth_email do
|
|
381
|
-
# RodauthMailer.email_auth(
|
|
426
|
+
# RodauthMailer.email_auth(account_id, email_auth_key_value)
|
|
382
427
|
# end
|
|
383
428
|
# create_unlock_account_email do
|
|
384
|
-
# RodauthMailer.unlock_account(
|
|
429
|
+
# RodauthMailer.unlock_account(account_id, unlock_account_key_value)
|
|
385
430
|
# end
|
|
386
431
|
send_email do |email|
|
|
387
432
|
# queue email delivery on the mailer after the transaction commits
|
|
@@ -399,44 +444,8 @@ deliveries. However, if you want to send emails synchronously, you can modify
|
|
|
399
444
|
the configuration to call `#deliver_now` instead.
|
|
400
445
|
|
|
401
446
|
If you're using a background processing library without an Active Job adapter,
|
|
402
|
-
or a 3rd-party service for sending transactional emails, this
|
|
403
|
-
|
|
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
|
-
```
|
|
447
|
+
or a 3rd-party service for sending transactional emails, see [this wiki
|
|
448
|
+
page][custom mailer worker] on how to set it up.
|
|
440
449
|
|
|
441
450
|
### Migrations
|
|
442
451
|
|
|
@@ -458,7 +467,23 @@ class CreateRodauthOtpSmsCodesRecoveryCodes < ActiveRecord::Migration
|
|
|
458
467
|
end
|
|
459
468
|
```
|
|
460
469
|
|
|
461
|
-
|
|
470
|
+
#### Custom migration name
|
|
471
|
+
|
|
472
|
+
You can change the default migration name:
|
|
473
|
+
|
|
474
|
+
```sh
|
|
475
|
+
$ rails generate rodauth:migration email_auth --name create_account_email_auth_keys
|
|
476
|
+
```
|
|
477
|
+
```rb
|
|
478
|
+
# db/migration/*_create_account_email_auth_keys
|
|
479
|
+
class CreateAccountEmailAuthKeys < ActiveRecord::Migration
|
|
480
|
+
def change
|
|
481
|
+
create_table :account_email_auth_keys do |t| ... end
|
|
482
|
+
end
|
|
483
|
+
end
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
## Model
|
|
462
487
|
|
|
463
488
|
The `Rodauth::Rails::Model` mixin can be included into the account model, which
|
|
464
489
|
defines a password attribute and associations for tables used by enabled
|
|
@@ -470,7 +495,7 @@ class Account < ApplicationRecord
|
|
|
470
495
|
end
|
|
471
496
|
```
|
|
472
497
|
|
|
473
|
-
|
|
498
|
+
### Password attribute
|
|
474
499
|
|
|
475
500
|
Regardless of whether you're storing the password hash in a column in the
|
|
476
501
|
accounts table, or in a separate table, the `#password` attribute can be used
|
|
@@ -494,7 +519,7 @@ Note that the password attribute doesn't come with validations, making it
|
|
|
494
519
|
unsuitable for forms. It was primarily intended to allow easily creating
|
|
495
520
|
accounts in development console and in tests.
|
|
496
521
|
|
|
497
|
-
|
|
522
|
+
### Associations
|
|
498
523
|
|
|
499
524
|
The `Rodauth::Rails::Model` mixin defines associations for Rodauth tables
|
|
500
525
|
associated to the accounts table:
|
|
@@ -544,6 +569,8 @@ class Account < ApplicationRecord
|
|
|
544
569
|
end
|
|
545
570
|
```
|
|
546
571
|
|
|
572
|
+
#### Association reference
|
|
573
|
+
|
|
547
574
|
Below is a list of all associations defined depending on the features loaded:
|
|
548
575
|
|
|
549
576
|
| Feature | Association | Type | Model | Table (default) |
|
|
@@ -568,6 +595,12 @@ Below is a list of all associations defined depending on the features loaded:
|
|
|
568
595
|
| webauthn | `:webauthn_keys` | `has_many` | `WebauthnKey` | `account_webauthn_keys` |
|
|
569
596
|
| webauthn | `:webauthn_user_id` | `has_one` | `WebauthnUserId` | `account_webauthn_user_ids` |
|
|
570
597
|
|
|
598
|
+
Note that some Rodauth tables use composite primary keys, which Active Record
|
|
599
|
+
doesn't support out of the box. For associations to work properly, you might
|
|
600
|
+
need to add the [composite_primary_keys] gem to your Gemfile.
|
|
601
|
+
|
|
602
|
+
#### Association options
|
|
603
|
+
|
|
571
604
|
By default, all associations except for audit logs have `dependent: :destroy`
|
|
572
605
|
set, to allow for easy deletion of account records in the console. You can use
|
|
573
606
|
`:association_options` to modify global or per-association options:
|
|
@@ -582,138 +615,64 @@ Rodauth::Rails.model(association_options: -> (name) {
|
|
|
582
615
|
})
|
|
583
616
|
```
|
|
584
617
|
|
|
585
|
-
|
|
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
|
|
618
|
+
## Multiple configurations
|
|
590
619
|
|
|
591
620
|
If you need to handle multiple types of accounts that require different
|
|
592
|
-
authentication logic, you can create
|
|
621
|
+
authentication logic, you can create new configurations for them. This
|
|
622
|
+
is done by creating new `Rodauth::Rails::Auth` subclasses, and registering
|
|
623
|
+
them under a name.
|
|
593
624
|
|
|
594
625
|
```rb
|
|
595
|
-
# app/
|
|
626
|
+
# app/misc/rodauth_app.rb
|
|
596
627
|
class RodauthApp < Rodauth::Rails::App
|
|
597
|
-
# primary configuration
|
|
598
|
-
configure
|
|
599
|
-
# ...
|
|
600
|
-
end
|
|
628
|
+
configure RodauthMain # primary configuration
|
|
629
|
+
configure RodauthAdmin, :admin # secondary configuration
|
|
601
630
|
|
|
602
|
-
|
|
603
|
-
|
|
631
|
+
route do |r|
|
|
632
|
+
r.rodauth # route primary rodauth requests
|
|
633
|
+
r.rodauth(:admin) # route secondary rodauth requests
|
|
634
|
+
end
|
|
635
|
+
end
|
|
636
|
+
```
|
|
637
|
+
```rb
|
|
638
|
+
# app/misc/rodauth_admin.rb
|
|
639
|
+
class RodauthAdmin < Rodauth::Rails::Auth
|
|
640
|
+
configure do
|
|
604
641
|
# ... enable features ...
|
|
605
642
|
prefix "/admin"
|
|
606
643
|
session_key_prefix "admin_"
|
|
607
644
|
remember_cookie_key "_admin_remember" # if using remember feature
|
|
608
|
-
# ...
|
|
609
|
-
end
|
|
610
|
-
|
|
611
|
-
route do |r|
|
|
612
|
-
r.rodauth
|
|
613
645
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
break # allow routing of other /admin/* requests to continue to Rails
|
|
617
|
-
end
|
|
618
|
-
|
|
619
|
-
# ...
|
|
646
|
+
# search views in `app/views/admin/rodauth` directory
|
|
647
|
+
rails_controller { Admin::RodauthController }
|
|
620
648
|
end
|
|
621
649
|
end
|
|
622
650
|
```
|
|
623
|
-
|
|
624
|
-
Then in your application you can reference the secondary Rodauth instance:
|
|
625
|
-
|
|
626
|
-
```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
651
|
```rb
|
|
638
|
-
#
|
|
639
|
-
class
|
|
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
|
|
652
|
+
# app/controllers/admin/rodauth_controller.rb
|
|
653
|
+
class Admin::RodauthController < ApplicationController
|
|
646
654
|
end
|
|
647
655
|
```
|
|
648
|
-
```sh
|
|
649
|
-
$ rails db:migrate
|
|
650
|
-
```
|
|
651
656
|
|
|
652
|
-
Then
|
|
653
|
-
Rodauth retrieves accounts you could filter only those belonging to the current
|
|
654
|
-
configuration:
|
|
657
|
+
Then in your application you can reference the secondary Rodauth instance:
|
|
655
658
|
|
|
656
659
|
```rb
|
|
657
|
-
|
|
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
|
|
660
|
+
rodauth(:admin).login_path #=> "/admin/login"
|
|
672
661
|
```
|
|
673
662
|
|
|
674
|
-
|
|
663
|
+
You'll likely want to save the information of which account belongs to which
|
|
664
|
+
configuration to the database. See [this guide][account types] on how you can do
|
|
665
|
+
that.
|
|
675
666
|
|
|
676
|
-
|
|
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`:
|
|
667
|
+
### Sharing configuration
|
|
680
668
|
|
|
681
|
-
|
|
682
|
-
|
|
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
|
|
691
|
-
class RodauthAdmin < Rodauth::Rails::Auth
|
|
692
|
-
configure do
|
|
693
|
-
# ...
|
|
694
|
-
prefix "/admin"
|
|
695
|
-
session_key_prefix "admin_"
|
|
696
|
-
# ...
|
|
697
|
-
end
|
|
698
|
-
end
|
|
699
|
-
```
|
|
700
|
-
```rb
|
|
701
|
-
# app/lib/rodauth_app.rb
|
|
702
|
-
class RodauthApp < Rodauth::Rails::App
|
|
703
|
-
configure RodauthMain
|
|
704
|
-
configure RodauthAdmin, :admin
|
|
705
|
-
# ...
|
|
706
|
-
end
|
|
707
|
-
```
|
|
708
|
-
|
|
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:
|
|
669
|
+
If there are common settings that you want to share between Rodauth
|
|
670
|
+
configurations, you can do so via inheritance:
|
|
712
671
|
|
|
713
672
|
```rb
|
|
714
|
-
# app/
|
|
673
|
+
# app/misc/rodauth_base.rb
|
|
715
674
|
class RodauthBase < Rodauth::Rails::Auth
|
|
716
|
-
# common settings that
|
|
675
|
+
# common settings that are shared between multiple configurations
|
|
717
676
|
configure do
|
|
718
677
|
enable :login, :logout
|
|
719
678
|
login_return_to_requested_location? true
|
|
@@ -723,7 +682,7 @@ class RodauthBase < Rodauth::Rails::Auth
|
|
|
723
682
|
end
|
|
724
683
|
```
|
|
725
684
|
```rb
|
|
726
|
-
# app/
|
|
685
|
+
# app/misc/rodauth_main.rb
|
|
727
686
|
class RodauthMain < RodauthBase # inherit common settings
|
|
728
687
|
configure do
|
|
729
688
|
# ... customize main ...
|
|
@@ -731,7 +690,7 @@ class RodauthMain < RodauthBase # inherit common settings
|
|
|
731
690
|
end
|
|
732
691
|
```
|
|
733
692
|
```rb
|
|
734
|
-
# app/
|
|
693
|
+
# app/misc/rodauth_admin.rb
|
|
735
694
|
class RodauthAdmin < RodauthBase # inherit common settings
|
|
736
695
|
configure do
|
|
737
696
|
# ... customize admin ...
|
|
@@ -739,112 +698,63 @@ class RodauthAdmin < RodauthBase # inherit common settings
|
|
|
739
698
|
end
|
|
740
699
|
```
|
|
741
700
|
|
|
742
|
-
|
|
743
|
-
directly at the class level instead of inside an `auth_class_eval`:
|
|
701
|
+
## Outside of a request
|
|
744
702
|
|
|
745
|
-
|
|
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
|
|
767
|
-
|
|
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
|
|
703
|
+
### Calling actions
|
|
793
704
|
|
|
794
705
|
In some cases you might need to use Rodauth more programmatically. If you want
|
|
795
706
|
to perform authentication operations outside of request context, Rodauth ships
|
|
796
707
|
with the [internal_request] feature just for that.
|
|
797
708
|
|
|
798
709
|
```rb
|
|
799
|
-
# app/
|
|
800
|
-
class
|
|
710
|
+
# app/misc/rodauth_main.rb
|
|
711
|
+
class RodauthMain < Rodauth::Rails::Auth
|
|
801
712
|
configure do
|
|
802
713
|
enable :internal_request
|
|
803
714
|
end
|
|
804
715
|
end
|
|
805
716
|
```
|
|
806
717
|
```rb
|
|
807
|
-
#
|
|
718
|
+
# primary configuration
|
|
808
719
|
RodauthApp.rodauth.create_account(login: "user@example.com", password: "secret")
|
|
809
720
|
RodauthApp.rodauth.verify_account(account_login: "user@example.com")
|
|
810
721
|
|
|
811
722
|
# secondary configuration
|
|
812
|
-
RodauthApp.rodauth(:admin).close_account(account_login: "
|
|
723
|
+
RodauthApp.rodauth(:admin).close_account(account_login: "user@example.com")
|
|
813
724
|
```
|
|
814
725
|
|
|
815
|
-
|
|
816
|
-
`config.action_mailer.default_url_options`, which is used for generating email
|
|
817
|
-
links.
|
|
726
|
+
### Generating URLs
|
|
818
727
|
|
|
819
728
|
For generating authentication URLs outside of a request use the
|
|
820
729
|
[path_class_methods] plugin:
|
|
821
730
|
|
|
822
731
|
```rb
|
|
823
|
-
# app/
|
|
824
|
-
class
|
|
732
|
+
# app/misc/rodauth_main.rb
|
|
733
|
+
class RodauthMain < Rodauth::Rails::Auth
|
|
825
734
|
configure do
|
|
826
735
|
enable :path_class_methods
|
|
736
|
+
create_account_route "register"
|
|
827
737
|
end
|
|
828
738
|
end
|
|
829
739
|
```
|
|
830
740
|
```rb
|
|
831
|
-
#
|
|
832
|
-
RodauthApp.rodauth.create_account_path
|
|
833
|
-
RodauthApp.rodauth.verify_account_url(key: "abc123")
|
|
741
|
+
# primary configuration
|
|
742
|
+
RodauthApp.rodauth.create_account_path # => "/register"
|
|
743
|
+
RodauthApp.rodauth.verify_account_url(key: "abc123") #=> "https://example.com/verify-account?key=abc123"
|
|
834
744
|
|
|
835
745
|
# secondary configuration
|
|
836
|
-
RodauthApp.rodauth(:admin).close_account_path
|
|
746
|
+
RodauthApp.rodauth(:admin).close_account_path(foo: "bar") #=> "/admin/close-account?foo=bar"
|
|
837
747
|
```
|
|
838
748
|
|
|
839
|
-
|
|
749
|
+
### Calling instance methods
|
|
840
750
|
|
|
841
751
|
If you need to access Rodauth methods not exposed as internal requests, you can
|
|
842
752
|
use `Rodauth::Rails.rodauth` to retrieve the Rodauth instance used by the
|
|
843
753
|
internal_request feature:
|
|
844
754
|
|
|
845
755
|
```rb
|
|
846
|
-
# app/
|
|
847
|
-
class
|
|
756
|
+
# app/misc/rodauth_main.rb
|
|
757
|
+
class RodauthMain < Rodauth::Rails::Auth
|
|
848
758
|
configure do
|
|
849
759
|
enable :internal_request # this is required
|
|
850
760
|
end
|
|
@@ -852,7 +762,7 @@ end
|
|
|
852
762
|
```
|
|
853
763
|
```rb
|
|
854
764
|
account = Account.find_by!(email: "user@example.com")
|
|
855
|
-
rodauth = Rodauth::Rails.rodauth(account: account)
|
|
765
|
+
rodauth = Rodauth::Rails.rodauth(account: account) #=> #<RodauthMain::InternalRequest ...>
|
|
856
766
|
|
|
857
767
|
rodauth.compute_hmac("token") #=> "TpEJTKfKwqYvIDKWsuZhkhKlhaBXtR1aodskBAflD8U"
|
|
858
768
|
rodauth.open_account? #=> true
|
|
@@ -873,480 +783,290 @@ Rodauth::Rails.rodauth(session: { two_factor_auth_setup: true })
|
|
|
873
783
|
Rodauth::Rails.rodauth(:admin, params: { "param" => "value" })
|
|
874
784
|
```
|
|
875
785
|
|
|
876
|
-
##
|
|
786
|
+
## Configuring
|
|
877
787
|
|
|
878
|
-
###
|
|
788
|
+
### Configuration methods
|
|
879
789
|
|
|
880
|
-
rodauth-rails
|
|
881
|
-
|
|
882
|
-
reaches the Rails router.
|
|
790
|
+
The `rails` feature rodauth-rails loads provides the following configuration
|
|
791
|
+
methods:
|
|
883
792
|
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
793
|
+
| Name | Description |
|
|
794
|
+
| :---- | :---------- |
|
|
795
|
+
| `rails_render(**options)` | Renders the template with given render options. |
|
|
796
|
+
| `rails_csrf_tag` | Hidden field added to Rodauth templates containing the CSRF token. |
|
|
797
|
+
| `rails_csrf_param` | Value of the `name` attribute for the CSRF tag. |
|
|
798
|
+
| `rails_csrf_token` | Value of the `value` attribute for the CSRF tag. |
|
|
799
|
+
| `rails_check_csrf!` | Verifies the authenticity token for the current request. |
|
|
800
|
+
| `rails_controller_instance` | Instance of the controller with the request env context. |
|
|
801
|
+
| `rails_controller` | Controller class to use for rendering and CSRF protection. |
|
|
802
|
+
| `rails_account_model` | Model class connected with the accounts table. |
|
|
890
803
|
|
|
891
|
-
|
|
892
|
-
|
|
804
|
+
For the list of configuration methods provided by Rodauth, see the [feature
|
|
805
|
+
documentation].
|
|
893
806
|
|
|
894
|
-
|
|
895
|
-
request.env["rodauth"] #=> #<Rodauth::Auth>
|
|
896
|
-
request.env["rodauth.admin"] #=> #<Rodauth::Auth> (if using multiple configurations)
|
|
897
|
-
```
|
|
807
|
+
### Defining custom methods
|
|
898
808
|
|
|
899
|
-
|
|
900
|
-
|
|
809
|
+
All Rodauth configuration methods are just syntax sugar for defining instance
|
|
810
|
+
methods on the auth class. You can also define your own custom methods on the
|
|
811
|
+
auth class:
|
|
901
812
|
|
|
902
813
|
```rb
|
|
903
|
-
class
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
814
|
+
class RodauthMain < Rodauth::Rails::Auth
|
|
815
|
+
configure do
|
|
816
|
+
# ...
|
|
817
|
+
password_match? { |password| ldap_valid?(password) }
|
|
818
|
+
# ...
|
|
907
819
|
end
|
|
908
|
-
end
|
|
909
|
-
```
|
|
910
|
-
```erb
|
|
911
|
-
<% rodauth #=> #<Rodauth::Auth> %>
|
|
912
|
-
<% rodauth(:admin) #=> #<Rodauth::Auth> (if using multiple configurations) %>
|
|
913
|
-
```
|
|
914
|
-
|
|
915
|
-
### App
|
|
916
820
|
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
* uses Action Dispatch CSRF protection instead of Roda's
|
|
922
|
-
* sets [HMAC] secret to Rails' secret key base
|
|
923
|
-
* uses Action Controller for rendering templates
|
|
924
|
-
* runs Action Controller callbacks & rescue handlers around Rodauth actions
|
|
925
|
-
* uses Action Mailer for sending emails
|
|
821
|
+
# Example external identities table
|
|
822
|
+
def identities
|
|
823
|
+
db[:account_identities].where(account_id: account_id).all
|
|
824
|
+
end
|
|
926
825
|
|
|
927
|
-
|
|
928
|
-
any additional [plugin options].
|
|
826
|
+
private
|
|
929
827
|
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
configure(:admin) { ... } # defining multiple Rodauth configurations
|
|
828
|
+
# Example LDAP authentication
|
|
829
|
+
def ldap_valid?(password)
|
|
830
|
+
SimpleLdapAuthenticator.valid?(account[:email], password)
|
|
831
|
+
end
|
|
935
832
|
end
|
|
936
833
|
```
|
|
834
|
+
```rb
|
|
835
|
+
rodauth.identities #=> [{ provider: "facebook", uid: "abc123", ... }, ...]
|
|
836
|
+
```
|
|
937
837
|
|
|
938
|
-
|
|
939
|
-
|
|
838
|
+
### Rails URL helpers
|
|
839
|
+
|
|
840
|
+
Inside Rodauth configuration and the `route` block you can access Rails route
|
|
841
|
+
helpers through `#rails_routes`:
|
|
940
842
|
|
|
941
843
|
```rb
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
844
|
+
# app/misc/rodauth_main.rb
|
|
845
|
+
class RodauthMain < Rodauth::Rails::Auth
|
|
846
|
+
configure do
|
|
847
|
+
login_redirect { rails_routes.activity_path }
|
|
945
848
|
end
|
|
946
849
|
end
|
|
947
850
|
```
|
|
948
851
|
|
|
949
|
-
|
|
950
|
-
|
|
852
|
+
### Calling controller methods
|
|
853
|
+
|
|
854
|
+
When using Rodauth before/after hooks or generally overriding your Rodauth
|
|
855
|
+
configuration, in some cases you might want to call methods defined on your
|
|
856
|
+
controllers. You can do so with `rails_controller_eval`, for example:
|
|
951
857
|
|
|
952
858
|
```rb
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
859
|
+
# app/controllers/application_controller.rb
|
|
860
|
+
class ApplicationController < ActionController::Base
|
|
861
|
+
private
|
|
862
|
+
def setup_tracking(account_id)
|
|
863
|
+
# ... some implementation ...
|
|
864
|
+
end
|
|
958
865
|
end
|
|
959
866
|
```
|
|
960
|
-
|
|
961
|
-
### Sequel
|
|
962
|
-
|
|
963
|
-
Rodauth uses the [Sequel] library for database queries, due to more advanced
|
|
964
|
-
database usage (SQL expressions, database-agnostic date arithmetic, SQL
|
|
965
|
-
function calls).
|
|
966
|
-
|
|
967
|
-
If ActiveRecord is used in the application, the `rodauth:install` generator
|
|
968
|
-
will have automatically configured Sequel to reuse ActiveRecord's database
|
|
969
|
-
connection, using the [sequel-activerecord_connection] gem.
|
|
970
|
-
|
|
971
|
-
This means that, from the usage perspective, Sequel can be considered just
|
|
972
|
-
as an implementation detail of Rodauth.
|
|
973
|
-
|
|
974
|
-
## JSON API
|
|
975
|
-
|
|
976
|
-
To make Rodauth endpoints accessible via JSON API, enable the [`json`][json]
|
|
977
|
-
feature:
|
|
978
|
-
|
|
979
867
|
```rb
|
|
980
|
-
# app/
|
|
981
|
-
class
|
|
868
|
+
# app/misc/rodauth_main.rb
|
|
869
|
+
class RodauthMain < Rodauth::Rails::Auth
|
|
982
870
|
configure do
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
# ...
|
|
871
|
+
after_create_account do
|
|
872
|
+
rails_controller_eval { setup_tracking(account_id) }
|
|
873
|
+
end
|
|
987
874
|
end
|
|
988
875
|
end
|
|
989
876
|
```
|
|
990
877
|
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
878
|
+
### Single-file configuration
|
|
879
|
+
|
|
880
|
+
If you would prefer to have all Rodauth logic contained inside a single file,
|
|
881
|
+
you call `Rodauth::Rails::App.configure` with a block, which will create an
|
|
882
|
+
anonymous auth class.
|
|
995
883
|
|
|
996
|
-
```sh
|
|
997
|
-
$ bundle add jwt
|
|
998
|
-
```
|
|
999
884
|
```rb
|
|
1000
|
-
# app/
|
|
885
|
+
# app/misc/rodauth_app.rb
|
|
1001
886
|
class RodauthApp < Rodauth::Rails::App
|
|
887
|
+
# primary configuration
|
|
1002
888
|
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)
|
|
889
|
+
enable :login, :logout, :create_account, :verify_account
|
|
1007
890
|
# ...
|
|
1008
891
|
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
892
|
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
private
|
|
893
|
+
# secondary configuration
|
|
894
|
+
configure(:admin) do
|
|
895
|
+
enable :email_auth, :single_session
|
|
896
|
+
# ...
|
|
897
|
+
end
|
|
1022
898
|
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
response.headers["Authorization"] = rodauth.session_jwt
|
|
1026
|
-
end
|
|
899
|
+
route do |r|
|
|
900
|
+
# ...
|
|
1027
901
|
end
|
|
1028
|
-
# ...
|
|
1029
902
|
end
|
|
1030
903
|
```
|
|
1031
904
|
|
|
1032
|
-
##
|
|
1033
|
-
|
|
1034
|
-
While Rodauth doesn't yet come with [OmniAuth] integration, we can build one
|
|
1035
|
-
ourselves using the existing Rodauth API.
|
|
1036
|
-
|
|
1037
|
-
Let's assume we're building Facebook login. We'll start by installing the
|
|
1038
|
-
necessary gems, and loading the Facebook OmniAuth strategy:
|
|
905
|
+
## How it works
|
|
1039
906
|
|
|
1040
|
-
|
|
1041
|
-
# Gemfile
|
|
1042
|
-
gem "omniauth", "~> 2.0"
|
|
1043
|
-
gem "omniauth-rails_csrf_protection" # https://github.com/omniauth/omniauth/wiki/Resolving-CVE-2015-9284
|
|
1044
|
-
gem "omniauth-facebook"
|
|
1045
|
-
```
|
|
1046
|
-
```rb
|
|
1047
|
-
# config/initializers/omniauth.rb
|
|
1048
|
-
Rails.application.config.middleware.use OmniAuth::Builder do
|
|
1049
|
-
provider :facebook, ENV["FACEBOOK_APP_ID"], ENV["FACEBOOK_APP_SECRET"],
|
|
1050
|
-
scope: "email", callback_path: "/auth/facebook/callback"
|
|
1051
|
-
end
|
|
1052
|
-
```
|
|
907
|
+
### Rack middleware
|
|
1053
908
|
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
with the `accounts` table:
|
|
909
|
+
The railtie inserts [`Rodauth::Rails::Middleware`](/lib/rodauth/rails/middleware.rb)
|
|
910
|
+
at the end of the middleware stack, which calls your Rodauth app around each request.
|
|
1057
911
|
|
|
1058
912
|
```sh
|
|
1059
|
-
$ rails
|
|
913
|
+
$ rails middleware
|
|
914
|
+
# ...
|
|
915
|
+
# use Rodauth::Rails::Middleware
|
|
916
|
+
# run MyApp::Application.routes
|
|
1060
917
|
```
|
|
1061
|
-
```rb
|
|
1062
|
-
# db/migrate/*_create_account_identities.rb
|
|
1063
|
-
class CreateAccountIdentities < ActiveRecord::Migration
|
|
1064
|
-
def change
|
|
1065
|
-
create_table :account_identities do |t|
|
|
1066
|
-
t.references :account, null: false, foreign_key: { on_delete: :cascade }
|
|
1067
|
-
t.string :provider, null: false
|
|
1068
|
-
t.string :uid, null: false
|
|
1069
|
-
t.jsonb :info, null: false, default: {} # adjust JSON column type for your database
|
|
1070
918
|
|
|
1071
|
-
|
|
919
|
+
It can be inserted at any point in the middleware stack:
|
|
1072
920
|
|
|
1073
|
-
t.index [:provider, :uid], unique: true
|
|
1074
|
-
end
|
|
1075
|
-
end
|
|
1076
|
-
end
|
|
1077
|
-
```
|
|
1078
921
|
```rb
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
belongs_to :account
|
|
1082
|
-
end
|
|
1083
|
-
```
|
|
1084
|
-
```rb
|
|
1085
|
-
# app/models/account.rb
|
|
1086
|
-
class Account < ApplicationRecord
|
|
1087
|
-
has_many :identities, class_name: "AccountIdentity"
|
|
922
|
+
Rodauth::Rails.configure do |config|
|
|
923
|
+
config.middleware = false # disable auto-insertion
|
|
1088
924
|
end
|
|
1089
|
-
```
|
|
1090
|
-
|
|
1091
|
-
Next, let's add a POST button pointing to the request URL to our login form:
|
|
1092
925
|
|
|
1093
|
-
|
|
1094
|
-
<%= button_to "Login via Facebook", "/auth/facebook",
|
|
1095
|
-
method: :post, data: { turbo: false }, class: "btn btn-link p-0" %>
|
|
926
|
+
Rails.application.config.middleware.insert_before AnotherMiddleware, Rodauth::Rails::Middleware
|
|
1096
927
|
```
|
|
1097
928
|
|
|
1098
|
-
|
|
1099
|
-
|
|
929
|
+
The middleware retrieves the Rodauth app via `Rodauth::Rails.app`, which is
|
|
930
|
+
specified as a string to keep the class autoloadable and reloadable in
|
|
931
|
+
development.
|
|
1100
932
|
|
|
1101
933
|
```rb
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
# ...
|
|
1105
|
-
get "/auth/:provider/callback", to: "rodauth#omniauth"
|
|
934
|
+
Rodauth::Rails.configure do |config|
|
|
935
|
+
config.app = "RodauthApp"
|
|
1106
936
|
end
|
|
1107
937
|
```
|
|
1108
|
-
```rb
|
|
1109
|
-
# app/controllres/rodauth_controller.rb
|
|
1110
|
-
class RodauthController < ApplicationController
|
|
1111
|
-
def omniauth
|
|
1112
|
-
auth = request.env["omniauth.auth"]
|
|
1113
|
-
|
|
1114
|
-
# attempt to find existing identity directly
|
|
1115
|
-
identity = AccountIdentity.find_by(provider: auth["provider"], uid: auth["uid"])
|
|
1116
|
-
|
|
1117
|
-
if identity
|
|
1118
|
-
# update any external info changes
|
|
1119
|
-
identity.update!(info: auth["info"])
|
|
1120
|
-
# set account from identity
|
|
1121
|
-
account = identity.account
|
|
1122
|
-
end
|
|
1123
|
-
|
|
1124
|
-
# attempt to find an existing account by email
|
|
1125
|
-
account ||= Account.find_by(email: auth["info"]["email"])
|
|
1126
|
-
|
|
1127
|
-
# disallow login if account is not verified
|
|
1128
|
-
if account && account.status != rodauth.account_open_status_value
|
|
1129
|
-
redirect_to rodauth.login_path, alert: rodauth.unverified_account_message
|
|
1130
|
-
return
|
|
1131
|
-
end
|
|
1132
938
|
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
end
|
|
939
|
+
In addition to Zeitwerk compatibility, this extra layer catches Rodauth redirects
|
|
940
|
+
that happen on the controller level (e.g. when calling
|
|
941
|
+
`rodauth.require_authentication` in a `before_action` filter).
|
|
1137
942
|
|
|
1138
|
-
|
|
1139
|
-
unless identity
|
|
1140
|
-
account.identities.create!(provider: auth["provider"], uid: auth["uid"], info: auth["info"])
|
|
1141
|
-
end
|
|
943
|
+
### Roda app
|
|
1142
944
|
|
|
1143
|
-
|
|
1144
|
-
|
|
945
|
+
The [`Rodauth::Rails::App`](/lib/rodauth/rails/app.rb) class is a [Roda]
|
|
946
|
+
subclass that provides a convenience layer for Rodauth:
|
|
1145
947
|
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
end
|
|
1151
|
-
end
|
|
1152
|
-
```
|
|
948
|
+
* uses Action Dispatch flash messages
|
|
949
|
+
* provides syntax sugar for loading the rodauth plugin
|
|
950
|
+
* saves Rodauth object(s) to Rack env hash
|
|
951
|
+
* propagates edited headers to Rails responses
|
|
1153
952
|
|
|
1154
|
-
|
|
953
|
+
#### Configure block
|
|
1155
954
|
|
|
1156
|
-
The `
|
|
1157
|
-
|
|
955
|
+
The `configure` call loads the rodauth plugin. By convention, it receives an
|
|
956
|
+
auth class and configuration name as positional arguments (forwarded as
|
|
957
|
+
`:auth_class` and `:name` plugin options), a block for anonymous auth classes,
|
|
958
|
+
and also accepts any additional plugin options.
|
|
1158
959
|
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
| `rails_csrf_token` | Value of the `value` attribute for the CSRF tag. |
|
|
1165
|
-
| `rails_check_csrf!` | Verifies the authenticity token for the current request. |
|
|
1166
|
-
| `rails_controller_instance` | Instance of the controller with the request env context. |
|
|
1167
|
-
| `rails_controller` | Controller class to use for rendering and CSRF protection. |
|
|
1168
|
-
| `rails_account_model` | Model class connected with the accounts table. |
|
|
1169
|
-
|
|
1170
|
-
The `Rodauth::Rails` module has a few config settings available as well:
|
|
960
|
+
```rb
|
|
961
|
+
class RodauthApp < Rodauth::Rails::App
|
|
962
|
+
# named auth class
|
|
963
|
+
configure(RodauthMain)
|
|
964
|
+
configure(RodauthAdmin, :admin)
|
|
1171
965
|
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
| `middleware` | Whether to insert the middleware into the Rails application's middleware stack. Defaults to `true`. |
|
|
966
|
+
# anonymous auth class
|
|
967
|
+
configure { ... }
|
|
968
|
+
configure(:admin) { ... }
|
|
1176
969
|
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
Rodauth::Rails.configure do |config|
|
|
1180
|
-
config.app = "RodauthApp"
|
|
1181
|
-
config.middleware = true
|
|
970
|
+
# plugin options
|
|
971
|
+
configure(RodauthMain, json: :only)
|
|
1182
972
|
end
|
|
1183
973
|
```
|
|
1184
974
|
|
|
1185
|
-
|
|
1186
|
-
documentation].
|
|
1187
|
-
|
|
1188
|
-
## Custom extensions
|
|
975
|
+
#### Route block
|
|
1189
976
|
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
feature design doesn't yet work well with Zeitwerk reloading.
|
|
1193
|
-
|
|
1194
|
-
Here is an example of an LDAP authentication extension that uses the
|
|
1195
|
-
[simple_ldap_authenticator] gem.
|
|
977
|
+
The `route` block is called for each request, before it reaches the Rails
|
|
978
|
+
router, and it's yielded the request object.
|
|
1196
979
|
|
|
1197
980
|
```rb
|
|
1198
|
-
# app/lib/rodauth_ldap.rb
|
|
1199
|
-
module RodauthLdap
|
|
1200
|
-
def require_bcrypt?
|
|
1201
|
-
false
|
|
1202
|
-
end
|
|
1203
|
-
|
|
1204
|
-
def password_match?(password)
|
|
1205
|
-
SimpleLdapAuthenticator.valid?(account[:email], password)
|
|
1206
|
-
end
|
|
1207
|
-
end
|
|
1208
|
-
```
|
|
1209
|
-
```rb
|
|
1210
|
-
# app/lib/rodauth_app.rb
|
|
1211
981
|
class RodauthApp < Rodauth::Rails::App
|
|
1212
|
-
|
|
1213
|
-
#
|
|
1214
|
-
auth_class_eval do
|
|
1215
|
-
include RodauthLdap
|
|
1216
|
-
end
|
|
1217
|
-
# ...
|
|
982
|
+
route do |r|
|
|
983
|
+
# called before each request
|
|
1218
984
|
end
|
|
1219
985
|
end
|
|
1220
986
|
```
|
|
1221
987
|
|
|
1222
|
-
|
|
988
|
+
#### Routing prefix
|
|
1223
989
|
|
|
1224
|
-
|
|
990
|
+
If you use a routing prefix, you don't need to add a call to `r.on` like with
|
|
991
|
+
vanilla Rodauth, as `r.rodauth` has been modified to automatically route the
|
|
992
|
+
prefix.
|
|
1225
993
|
|
|
1226
994
|
```rb
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
class AuthenticationTest < ActionDispatch::SystemTestCase
|
|
1231
|
-
include ActiveJob::TestHelper
|
|
1232
|
-
driven_by :rack_test
|
|
1233
|
-
|
|
1234
|
-
test "creating and verifying an account" do
|
|
1235
|
-
create_account
|
|
1236
|
-
assert_match "An email has been sent to you with a link to verify your account", page.text
|
|
1237
|
-
|
|
1238
|
-
verify_account
|
|
1239
|
-
assert_match "Your account has been verified", page.text
|
|
1240
|
-
end
|
|
1241
|
-
|
|
1242
|
-
test "logging in and logging out" do
|
|
1243
|
-
create_account(verify: true)
|
|
1244
|
-
|
|
1245
|
-
logout
|
|
1246
|
-
assert_match "You have been logged out", page.text
|
|
1247
|
-
|
|
1248
|
-
login
|
|
1249
|
-
assert_match "You have been logged in", page.text
|
|
1250
|
-
end
|
|
1251
|
-
|
|
1252
|
-
private
|
|
1253
|
-
|
|
1254
|
-
def create_account(email: "user@example.com", password: "secret", verify: false)
|
|
1255
|
-
visit "/create-account"
|
|
1256
|
-
fill_in "Login", with: email
|
|
1257
|
-
fill_in "Password", with: password
|
|
1258
|
-
fill_in "Confirm Password", with: password
|
|
1259
|
-
click_on "Create Account"
|
|
1260
|
-
verify_account if verify
|
|
1261
|
-
end
|
|
1262
|
-
|
|
1263
|
-
def verify_account
|
|
1264
|
-
perform_enqueued_jobs # run enqueued email deliveries
|
|
1265
|
-
email = ActionMailer::Base.deliveries.last
|
|
1266
|
-
verify_account_link = email.body.to_s[/\S+verify-account\S+/]
|
|
1267
|
-
visit verify_account_link
|
|
1268
|
-
click_on "Verify Account"
|
|
1269
|
-
end
|
|
1270
|
-
|
|
1271
|
-
def login(email: "user@example.com", password: "secret")
|
|
1272
|
-
visit "/login"
|
|
1273
|
-
fill_in "Login", with: email
|
|
1274
|
-
fill_in "Password", with: password
|
|
1275
|
-
click_on "Login"
|
|
995
|
+
class RodauthApp < Rodauth::Rails::App
|
|
996
|
+
configure do
|
|
997
|
+
prefix "/user"
|
|
1276
998
|
end
|
|
1277
999
|
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
click_on "Logout"
|
|
1000
|
+
route do |r|
|
|
1001
|
+
r.rodauth # no need to wrap with `r.on("user") { ... }`
|
|
1281
1002
|
end
|
|
1282
1003
|
end
|
|
1283
1004
|
```
|
|
1284
1005
|
|
|
1285
|
-
|
|
1286
|
-
|
|
1006
|
+
### Auth class
|
|
1007
|
+
|
|
1008
|
+
The [`Rodauth::Rails::Auth`](/lib/rodauth/rails/auth.rb) class is a subclass of
|
|
1009
|
+
`Rodauth::Auth`, which preloads the `rails` rodauth feature, sets [HMAC] secret to
|
|
1010
|
+
Rails' secret key base, and modifies some [configuration defaults](#rodauth-defaults).
|
|
1287
1011
|
|
|
1288
1012
|
```rb
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
class AuthenticationTest < ActionDispatch::IntegrationTest
|
|
1293
|
-
test "creating and verifying an account" do
|
|
1294
|
-
create_account
|
|
1295
|
-
assert_response :success
|
|
1296
|
-
assert_match "An email has been sent to you with a link to verify your account", JSON.parse(body)["success"]
|
|
1297
|
-
|
|
1298
|
-
verify_account
|
|
1299
|
-
assert_response :success
|
|
1300
|
-
assert_match "Your account has been verified", JSON.parse(body)["success"]
|
|
1013
|
+
class RodauthMain < Rodauth::Rails::Auth
|
|
1014
|
+
configure do
|
|
1015
|
+
# authentication configuration
|
|
1301
1016
|
end
|
|
1017
|
+
end
|
|
1018
|
+
```
|
|
1302
1019
|
|
|
1303
|
-
|
|
1304
|
-
create_account(verify: true)
|
|
1020
|
+
### Rodauth feature
|
|
1305
1021
|
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
assert_match "You have been logged out", JSON.parse(body)["success"]
|
|
1022
|
+
The [`rails`](/lib/rodauth/rails/feature.rb) Rodauth feature loaded by
|
|
1023
|
+
`Rodauth::Rails::Auth` provides the main part of the Rails integration for Rodauth:
|
|
1309
1024
|
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1025
|
+
* uses Action View for template rendering
|
|
1026
|
+
* uses Action Dispatch for CSRF protection
|
|
1027
|
+
* runs Action Controller callbacks and rescue from blocks around Rodauth requests
|
|
1028
|
+
* uses Action Mailer to create and deliver emails
|
|
1029
|
+
* uses Action Controller instrumentation around Rodauth requests
|
|
1030
|
+
* uses Action Mailer's default URL options when calling Rodauth outside of a request
|
|
1314
1031
|
|
|
1315
|
-
|
|
1032
|
+
### Controller
|
|
1316
1033
|
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
verify_account if verify
|
|
1320
|
-
end
|
|
1034
|
+
The Rodauth app stores the `Rodauth::Rails::Auth` instances in the Rack env
|
|
1035
|
+
hash, which is then available in your Rails app:
|
|
1321
1036
|
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
post "/verify-account", as: :json, params: { key: verify_account_key }
|
|
1327
|
-
end
|
|
1037
|
+
```rb
|
|
1038
|
+
request.env["rodauth"] #=> #<RodauthMain>
|
|
1039
|
+
request.env["rodauth.admin"] #=> #<RodauthAdmin> (if using multiple configurations)
|
|
1040
|
+
```
|
|
1328
1041
|
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
end
|
|
1042
|
+
For convenience, this object can be accessed via the `#rodauth` method in views
|
|
1043
|
+
and controllers:
|
|
1332
1044
|
|
|
1333
|
-
|
|
1334
|
-
|
|
1045
|
+
```rb
|
|
1046
|
+
class MyController < ApplicationController
|
|
1047
|
+
def my_action
|
|
1048
|
+
rodauth #=> #<RodauthMain>
|
|
1049
|
+
rodauth(:admin) #=> #<RodauthAdmin> (if using multiple configurations)
|
|
1335
1050
|
end
|
|
1336
1051
|
end
|
|
1337
1052
|
```
|
|
1053
|
+
```erb
|
|
1054
|
+
<% rodauth #=> #<RodauthMain> %>
|
|
1055
|
+
<% rodauth(:admin) #=> #<RodauthAdmin> (if using multiple configurations) %>
|
|
1056
|
+
```
|
|
1338
1057
|
|
|
1339
|
-
|
|
1340
|
-
queue adapter to `:test` or `:inline`:
|
|
1058
|
+
### Sequel
|
|
1341
1059
|
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1060
|
+
Rodauth uses the [Sequel] library for database interaction, which offers
|
|
1061
|
+
powerful APIs for building advanced queries (it supports SQL expressions,
|
|
1062
|
+
database-agnostic date arithmetic, SQL function calls).
|
|
1063
|
+
|
|
1064
|
+
If you're using Active Record in your application, the `rodauth:install`
|
|
1065
|
+
generator automatically configures Sequel to reuse ActiveRecord's database
|
|
1066
|
+
connection, using the [sequel-activerecord_connection] gem.
|
|
1067
|
+
|
|
1068
|
+
This means that, from the usage perspective, Sequel can be considered just
|
|
1069
|
+
as an implementation detail of Rodauth.
|
|
1350
1070
|
|
|
1351
1071
|
## Rodauth defaults
|
|
1352
1072
|
|
|
@@ -1393,16 +1113,15 @@ end
|
|
|
1393
1113
|
|
|
1394
1114
|
The recommended [Rodauth migration] stores possible account status values in a
|
|
1395
1115
|
separate table, and creates a foreign key on the accounts table, which ensures
|
|
1396
|
-
only a valid status value will be persisted.
|
|
1116
|
+
only a valid status value will be persisted. Unfortunately, this doesn't work
|
|
1117
|
+
when the database is restored from the schema file, in which case the account
|
|
1118
|
+
statuses table will be empty. This happens in tests by default, but it's also
|
|
1119
|
+
not unusual to do it in development.
|
|
1397
1120
|
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
To address this, rodauth-rails modifies the setup to store account status text
|
|
1403
|
-
directly in the accounts table. If you're worried about invalid status values
|
|
1404
|
-
creeping in, you may use enums instead. Alternatively, you can always go back
|
|
1405
|
-
to the setup recommended by Rodauth.
|
|
1121
|
+
To address this, rodauth-rails uses a `status` column without a separate table.
|
|
1122
|
+
If you're worried about invalid status values creeping in, you may use enums
|
|
1123
|
+
instead. Alternatively, you can always go back to the setup recommended by
|
|
1124
|
+
Rodauth.
|
|
1406
1125
|
|
|
1407
1126
|
```rb
|
|
1408
1127
|
# in the migration:
|
|
@@ -1418,14 +1137,13 @@ create_table :accounts do |t|
|
|
|
1418
1137
|
end
|
|
1419
1138
|
```
|
|
1420
1139
|
```diff
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
end
|
|
1140
|
+
class RodauthMain < Rodauth::Rails::Auth
|
|
1141
|
+
configure do
|
|
1142
|
+
# ...
|
|
1143
|
+
- account_status_column :status
|
|
1144
|
+
# ...
|
|
1145
|
+
end
|
|
1146
|
+
end
|
|
1429
1147
|
```
|
|
1430
1148
|
|
|
1431
1149
|
### Deadline values
|
|
@@ -1454,7 +1172,6 @@ conduct](https://github.com/janko/rodauth-rails/blob/master/CODE_OF_CONDUCT.md).
|
|
|
1454
1172
|
[Rodauth]: https://github.com/jeremyevans/rodauth
|
|
1455
1173
|
[Sequel]: https://github.com/jeremyevans/sequel
|
|
1456
1174
|
[feature documentation]: http://rodauth.jeremyevans.net/documentation.html
|
|
1457
|
-
[JWT gem]: https://github.com/jwt/ruby-jwt
|
|
1458
1175
|
[Bootstrap]: https://getbootstrap.com/
|
|
1459
1176
|
[Roda]: http://roda.jeremyevans.net/
|
|
1460
1177
|
[HMAC]: http://rodauth.jeremyevans.net/rdoc/files/README_rdoc.html#label-HMAC
|
|
@@ -1463,7 +1180,6 @@ conduct](https://github.com/janko/rodauth-rails/blob/master/CODE_OF_CONDUCT.md).
|
|
|
1463
1180
|
[sequel-activerecord_connection]: https://github.com/janko/sequel-activerecord_connection
|
|
1464
1181
|
[plugin options]: http://rodauth.jeremyevans.net/rdoc/files/README_rdoc.html#label-Plugin+Options
|
|
1465
1182
|
[hmac]: http://rodauth.jeremyevans.net/rdoc/files/README_rdoc.html#label-HMAC
|
|
1466
|
-
[OmniAuth]: https://github.com/omniauth/omniauth
|
|
1467
1183
|
[otp]: http://rodauth.jeremyevans.net/rdoc/files/doc/otp_rdoc.html
|
|
1468
1184
|
[sms_codes]: http://rodauth.jeremyevans.net/rdoc/files/doc/sms_codes_rdoc.html
|
|
1469
1185
|
[recovery_codes]: http://rodauth.jeremyevans.net/rdoc/files/doc/recovery_codes_rdoc.html
|
|
@@ -1484,3 +1200,6 @@ conduct](https://github.com/janko/rodauth-rails/blob/master/CODE_OF_CONDUCT.md).
|
|
|
1484
1200
|
[internal_request]: http://rodauth.jeremyevans.net/rdoc/files/doc/internal_request_rdoc.html
|
|
1485
1201
|
[composite_primary_keys]: https://github.com/composite-primary-keys/composite_primary_keys
|
|
1486
1202
|
[path_class_methods]: https://rodauth.jeremyevans.net/rdoc/files/doc/path_class_methods_rdoc.html
|
|
1203
|
+
[account types]: https://github.com/janko/rodauth-rails/wiki/Account-Types
|
|
1204
|
+
[custom mailer worker]: https://github.com/janko/rodauth-rails/wiki/Custom-Mailer-Worker
|
|
1205
|
+
[Turbo]: https://turbo.hotwired.dev/
|