rodauth-rails 0.13.0 → 0.17.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +40 -0
- data/README.md +333 -90
- data/lib/generators/rodauth/templates/app/lib/rodauth_app.rb +9 -5
- 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/_field.html.erb +2 -2
- data/lib/generators/rodauth/templates/app/views/rodauth/_field_error.html.erb +2 -2
- data/lib/generators/rodauth/templates/app/views/rodauth/_global_logout_field.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/_login_confirm_field.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/_login_display.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/_login_field.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/_login_form.html.erb +3 -3
- data/lib/generators/rodauth/templates/app/views/rodauth/_login_form_footer.html.erb +2 -2
- data/lib/generators/rodauth/templates/app/views/rodauth/_login_form_header.html.erb +2 -2
- data/lib/generators/rodauth/templates/app/views/rodauth/_login_hidden_field.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/_new_password_field.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/_otp_auth_code_field.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/_password_confirm_field.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/_password_field.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/_recovery_code_field.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/_recovery_codes_form.html.erb +4 -4
- data/lib/generators/rodauth/templates/app/views/rodauth/_sms_code_field.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/_sms_phone_field.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/add_recovery_codes.html.erb +2 -2
- data/lib/generators/rodauth/templates/app/views/rodauth/change_login.html.erb +3 -3
- data/lib/generators/rodauth/templates/app/views/rodauth/change_password.html.erb +3 -3
- data/lib/generators/rodauth/templates/app/views/rodauth/close_account.html.erb +2 -2
- 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 +4 -4
- 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 +2 -2
- data/lib/generators/rodauth/templates/app/views/rodauth/multi_phase_login.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 +2 -2
- data/lib/generators/rodauth/templates/app/views/rodauth/otp_setup.html.erb +7 -7
- 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 +4 -4
- data/lib/generators/rodauth/templates/app/views/rodauth/reset_password.html.erb +2 -2
- data/lib/generators/rodauth/templates/app/views/rodauth/reset_password_request.html.erb +2 -2
- 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 +2 -2
- 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 +2 -2
- data/lib/generators/rodauth/templates/app/views/rodauth/two_factor_auth.html.erb +1 -1
- data/lib/generators/rodauth/templates/app/views/rodauth/two_factor_disable.html.erb +2 -2
- data/lib/generators/rodauth/templates/app/views/rodauth/two_factor_manage.html.erb +6 -6
- data/lib/generators/rodauth/templates/app/views/rodauth/unlock_account.html.erb +2 -2
- 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 +3 -3
- data/lib/generators/rodauth/templates/app/views/rodauth/verify_account_resend.html.erb +2 -2
- 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 +7 -7
- data/lib/generators/rodauth/templates/app/views/rodauth/webauthn_remove.html.erb +5 -5
- data/lib/generators/rodauth/templates/app/views/rodauth/webauthn_setup.html.erb +7 -7
- data/lib/generators/rodauth/views_generator.rb +29 -4
- data/lib/rodauth/rails/auth.rb +10 -13
- data/lib/rodauth/rails/controller_methods.rb +42 -1
- data/lib/rodauth/rails/feature/base.rb +17 -8
- data/lib/rodauth/rails/feature/callbacks.rb +6 -2
- data/lib/rodauth/rails/feature/internal_request.rb +50 -0
- data/lib/rodauth/rails/feature/render.rb +7 -0
- data/lib/rodauth/rails/feature.rb +2 -0
- data/lib/rodauth/rails/model/associations.rb +195 -0
- data/lib/rodauth/rails/model.rb +101 -0
- data/lib/rodauth/rails/tasks.rake +5 -5
- data/lib/rodauth/rails/version.rb +1 -1
- data/lib/rodauth/rails.rb +27 -28
- data/rodauth-rails.gemspec +4 -1
- metadata +49 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1539e5f70a8cefa3c40e06b5b177152e4772f099deb11a077f07f59529622a62
|
4
|
+
data.tar.gz: 67c9a6829f8a9c45708cb1ab0781a2eebe1998d5f31f66b26d5c7f58cb37cdf8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bf1f132504de2266dc4ef7f71ffdd630e348119f6681f84288aeb6ba24481336948c78183d4fa7e90100dedc85e04c4bb98f915de3ecf156630d523d91d74c00
|
7
|
+
data.tar.gz: e1858507c3ee9a2855e04fa67957859f41347adbf448793b8cebe263a0bd95517ef913b4132470a31609be9741ff73e4769309df5924f19b0db0503a1a25fa2a
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,43 @@
|
|
1
|
+
## 0.17.0 (2021-10-05)
|
2
|
+
|
3
|
+
* Set `delete_account_on_close?` to `true` in generated `rodauth_app.rb` (@janko)
|
4
|
+
|
5
|
+
* Change default `:dependent` option for associations to `:delete`/`:delete_all` (@janko)
|
6
|
+
|
7
|
+
* Add `rails_account_model` configuration method for when the account model cannot be inferred (@janko)
|
8
|
+
|
9
|
+
## 0.16.0 (2021-09-26)
|
10
|
+
|
11
|
+
* Add `#current_account` to methods defined on `ActionController::Base` (@janko)
|
12
|
+
|
13
|
+
* Add missing template for verify_login_change feature to `rodauth:views` generator (@janko)
|
14
|
+
|
15
|
+
* Add `#rodauth_response` controller method for converting rodauth responses into controller responses (@janko)
|
16
|
+
|
17
|
+
## 0.15.0 (2021-07-29)
|
18
|
+
|
19
|
+
* Add `Rodauth::Rails::Model` mixin that defines password attribute and associations on the model (@janko)
|
20
|
+
|
21
|
+
* Add support for the new internal_request feature (@janko)
|
22
|
+
|
23
|
+
* Implement `Rodauth::Rails.rodauth` in terms of the internal_request feature (@janko)
|
24
|
+
|
25
|
+
## 0.14.0 (2021-07-10)
|
26
|
+
|
27
|
+
* Speed up template rendering by only searching formats accepted by the request (@janko)
|
28
|
+
|
29
|
+
* Add `--name` option to `rodauth:views` generator for specifying different rodauth configuration (@janko)
|
30
|
+
|
31
|
+
* Infer correct template path from configured controller in `rodauth:views` generator (@janko)
|
32
|
+
|
33
|
+
* Raise `ArgumentError` if undefined rodauth configuration is passed to `Rodauth::Rails.app` (@janko)
|
34
|
+
|
35
|
+
* Make `#rails_controller` method on the rodauth instance public (@janko)
|
36
|
+
|
37
|
+
* Remove `--directory` option from `rodauth:views` generator (@janko)
|
38
|
+
|
39
|
+
* Remove `#features` and `#routes` writer and `#configuration` reader from `Rodauth::Rails::Auth` (@janko)
|
40
|
+
|
1
41
|
## 0.13.0 (2021-06-10)
|
2
42
|
|
3
43
|
* Add `:query`, `:form`, `:session`, `:account`, and `:env` options to `Rodauth::Rails.rodauth` (@janko)
|
data/README.md
CHANGED
@@ -49,7 +49,7 @@ For instructions on upgrading from previous rodauth-rails versions, see
|
|
49
49
|
Add the gem to your Gemfile:
|
50
50
|
|
51
51
|
```rb
|
52
|
-
gem "rodauth-rails", "~> 0.
|
52
|
+
gem "rodauth-rails", "~> 0.17"
|
53
53
|
|
54
54
|
# gem "jwt", require: false # for JWT feature
|
55
55
|
# gem "rotp", require: false # for OTP feature
|
@@ -142,31 +142,36 @@ end
|
|
142
142
|
|
143
143
|
### Current account
|
144
144
|
|
145
|
-
|
146
|
-
|
147
|
-
retrieves the corresponding account record:
|
145
|
+
The `#current_account` method is defined in controllers and views, which
|
146
|
+
returns the model instance of the currently logged in account.
|
148
147
|
|
149
148
|
```rb
|
150
|
-
|
151
|
-
|
152
|
-
|
149
|
+
current_account #=> #<Account id=123 email="user@example.com">
|
150
|
+
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
155
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
end
|
162
|
-
helper_method :current_account # skip if inheriting from ActionController::API
|
163
|
-
end
|
156
|
+
Pass the configuration name to retrieve accounts belonging to other Rodauth
|
157
|
+
configurations:
|
158
|
+
|
159
|
+
```rb
|
160
|
+
current_account(:admin)
|
164
161
|
```
|
165
162
|
|
166
|
-
|
163
|
+
The `#current_account` method will try to infer the account model class from
|
164
|
+
the configured table name. If that fails, you can set the account model
|
165
|
+
manually:
|
167
166
|
|
168
|
-
```
|
169
|
-
|
167
|
+
```rb
|
168
|
+
# app/lib/rodauth_app.rb
|
169
|
+
class RodauthApp < Rodauth::Rails::App
|
170
|
+
configure do
|
171
|
+
# ...
|
172
|
+
rails_account_model Authentication::Account # custom model name
|
173
|
+
end
|
174
|
+
end
|
170
175
|
```
|
171
176
|
|
172
177
|
### Requiring authentication
|
@@ -278,8 +283,8 @@ $ rails generate rodauth:views
|
|
278
283
|
```
|
279
284
|
|
280
285
|
This will generate views for the default set of Rodauth features into the
|
281
|
-
`app/views/rodauth` directory,
|
282
|
-
|
286
|
+
`app/views/rodauth` directory, provided that `RodauthController` is set for the
|
287
|
+
main configuration.
|
283
288
|
|
284
289
|
You can pass a list of Rodauth features to the generator to create views for
|
285
290
|
these features (this will not remove or overwrite any existing views):
|
@@ -294,12 +299,10 @@ Or you can generate views for all features:
|
|
294
299
|
$ rails generate rodauth:views --all
|
295
300
|
```
|
296
301
|
|
297
|
-
|
298
|
-
case make sure to rename the Rodauth controller accordingly):
|
302
|
+
Use `--name` to generate views for a different Rodauth configuration:
|
299
303
|
|
300
304
|
```sh
|
301
|
-
|
302
|
-
$ rails generate rodauth:views --name authentication
|
305
|
+
$ rails generate rodauth:views --name admin
|
303
306
|
```
|
304
307
|
|
305
308
|
#### Layout
|
@@ -392,14 +395,48 @@ end
|
|
392
395
|
This configuration calls `#deliver_later`, which uses Active Job to deliver
|
393
396
|
emails in a background job. It's generally recommended to send emails
|
394
397
|
asynchronously for better request throughput and the ability to retry
|
395
|
-
deliveries. However, if you want to send emails synchronously, modify
|
396
|
-
configuration to call `#deliver_now` instead.
|
398
|
+
deliveries. However, if you want to send emails synchronously, you can modify
|
399
|
+
the configuration to call `#deliver_now` instead.
|
397
400
|
|
398
401
|
If you're using a background processing library without an Active Job adapter,
|
399
402
|
or a 3rd-party service for sending transactional emails, this two-phase API
|
400
403
|
might not be suitable. In this case, instead of overriding `#create_*_email`
|
401
404
|
and `#send_email`, override the `#send_*_email` methods instead, which are
|
402
|
-
required to send the email immediately.
|
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
|
+
```
|
403
440
|
|
404
441
|
### Migrations
|
405
442
|
|
@@ -421,6 +458,134 @@ class CreateRodauthOtpSmsCodesRecoveryCodes < ActiveRecord::Migration
|
|
421
458
|
end
|
422
459
|
```
|
423
460
|
|
461
|
+
### Model
|
462
|
+
|
463
|
+
The `Rodauth::Rails::Model` mixin can be included into the account model, which
|
464
|
+
defines a password attribute and associations for tables used by enabled
|
465
|
+
authentication features.
|
466
|
+
|
467
|
+
```rb
|
468
|
+
class Account < ApplicationRecord
|
469
|
+
include Rodauth::Rails.model # or `Rodauth::Rails.model(:admin)`
|
470
|
+
end
|
471
|
+
```
|
472
|
+
|
473
|
+
#### Password attribute
|
474
|
+
|
475
|
+
Regardless of whether you're storing the password hash in a column in the
|
476
|
+
accounts table, or in a separate table, the `#password` attribute can be used
|
477
|
+
to set or clear the password hash.
|
478
|
+
|
479
|
+
```rb
|
480
|
+
account = Account.create!(email: "user@example.com", password: "secret")
|
481
|
+
|
482
|
+
# when password hash is stored in a column on the accounts table
|
483
|
+
account.password_hash #=> "$2a$12$k/Ub1I2iomi84RacqY89Hu4.M0vK7klRnRtzorDyvOkVI.hKhkNw."
|
484
|
+
|
485
|
+
# when password hash is stored in a separate table
|
486
|
+
account.password_hash #=> #<Account::PasswordHash...> (record from `account_password_hashes` table)
|
487
|
+
account.password_hash.password_hash #=> "$2a$12$k/Ub1..." (inaccessible when using database authentication functions)
|
488
|
+
|
489
|
+
account.password = nil # clears password hash
|
490
|
+
account.password_hash #=> nil
|
491
|
+
```
|
492
|
+
|
493
|
+
Note that the password attribute doesn't come with validations, making it
|
494
|
+
unsuitable for forms. It was primarily intended to allow easily creating
|
495
|
+
accounts in development console and in tests.
|
496
|
+
|
497
|
+
#### Associations
|
498
|
+
|
499
|
+
The `Rodauth::Rails::Model` mixin defines associations for Rodauth tables
|
500
|
+
associated to the accounts table:
|
501
|
+
|
502
|
+
```rb
|
503
|
+
account.remember_key #=> #<Account::RememberKey> (record from `account_remember_keys` table)
|
504
|
+
account.active_session_keys #=> [#<Account::ActiveSessionKey>,...] (records from `account_active_session_keys` table)
|
505
|
+
```
|
506
|
+
|
507
|
+
You can also reference the associated models directly:
|
508
|
+
|
509
|
+
```rb
|
510
|
+
# model referencing the `account_authentication_audit_logs` table
|
511
|
+
Account::AuthenticationAuditLog.where(message: "login").group(:account_id)
|
512
|
+
```
|
513
|
+
|
514
|
+
The associated models define the inverse `belongs_to :account` association:
|
515
|
+
|
516
|
+
```rb
|
517
|
+
Account::ActiveSessionKey.includes(:account).map(&:account)
|
518
|
+
```
|
519
|
+
|
520
|
+
Here is an example of using associations to create a method that returns
|
521
|
+
whether the account has multifactor authentication enabled:
|
522
|
+
|
523
|
+
```rb
|
524
|
+
class Account < ApplicationRecord
|
525
|
+
include Rodauth::Rails.model
|
526
|
+
|
527
|
+
def mfa_enabled?
|
528
|
+
otp_key || (sms_code && sms_code.num_failures.nil?) || recovery_codes.any?
|
529
|
+
end
|
530
|
+
end
|
531
|
+
```
|
532
|
+
|
533
|
+
Here is another example of creating a query scope that selects accounts with
|
534
|
+
multifactor authentication enabled:
|
535
|
+
|
536
|
+
```rb
|
537
|
+
class Account < ApplicationRecord
|
538
|
+
include Rodauth::Rails.model
|
539
|
+
|
540
|
+
scope :otp_setup, -> { where(otp_key: OtpKey.all) }
|
541
|
+
scope :sms_codes_setup, -> { where(sms_code: SmsCode.where(num_failures: nil)) }
|
542
|
+
scope :recovery_codes_setup, -> { where(recovery_codes: RecoveryCode.all) }
|
543
|
+
scope :mfa_enabled, -> { merge(otp_setup.or(sms_codes_setup).or(recovery_codes_setup)) }
|
544
|
+
end
|
545
|
+
```
|
546
|
+
|
547
|
+
Below is a list of all associations defined depending on the features loaded:
|
548
|
+
|
549
|
+
| Feature | Association | Type | Model | Table (default) |
|
550
|
+
| :------ | :---------- | :--- | :---- | :---- |
|
551
|
+
| account_expiration | `:activity_time` | `has_one` | `ActivityTime` | `account_activity_times` |
|
552
|
+
| active_sessions | `:active_session_keys` | `has_many` | `ActiveSessionKey` | `account_active_session_keys` |
|
553
|
+
| audit_logging | `:authentication_audit_logs` | `has_many` | `AuthenticationAuditLog` | `account_authentication_audit_logs` |
|
554
|
+
| disallow_password_reuse | `:previous_password_hashes` | `has_many` | `PreviousPasswordHash` | `account_previous_password_hashes` |
|
555
|
+
| email_auth | `:email_auth_key` | `has_one` | `EmailAuthKey` | `account_email_auth_keys` |
|
556
|
+
| jwt_refresh | `:jwt_refresh_keys` | `has_many` | `JwtRefreshKey` | `account_jwt_refresh_keys` |
|
557
|
+
| lockout | `:lockout` | `has_one` | `Lockout` | `account_lockouts` |
|
558
|
+
| lockout | `:login_failure` | `has_one` | `LoginFailure` | `account_login_failures` |
|
559
|
+
| otp | `:otp_key` | `has_one` | `OtpKey` | `account_otp_keys` |
|
560
|
+
| password_expiration | `:password_change_time` | `has_one` | `PasswordChangeTime` | `account_password_change_times` |
|
561
|
+
| recovery_codes | `:recovery_codes` | `has_many` | `RecoveryCode` | `account_recovery_codes` |
|
562
|
+
| remember | `:remember_key` | `has_one` | `RememberKey` | `account_remember_keys` |
|
563
|
+
| reset_password | `:password_reset_key` | `has_one` | `PasswordResetKey` | `account_password_reset_keys` |
|
564
|
+
| single_session | `:session_key` | `has_one` | `SessionKey` | `account_session_keys` |
|
565
|
+
| sms_codes | `:sms_code` | `has_one` | `SmsCode` | `account_sms_codes` |
|
566
|
+
| verify_account | `:verification_key` | `has_one` | `VerificationKey` | `account_verification_keys` |
|
567
|
+
| verify_login_change | `:login_change_key` | `has_one` | `LoginChangeKey` | `account_login_change_keys` |
|
568
|
+
| webauthn | `:webauthn_keys` | `has_many` | `WebauthnKey` | `account_webauthn_keys` |
|
569
|
+
| webauthn | `:webauthn_user_id` | `has_one` | `WebauthnUserId` | `account_webauthn_user_ids` |
|
570
|
+
|
571
|
+
By default, all associations except for audit logs have `dependent: :destroy`
|
572
|
+
set, to allow for easy deletion of account records in the console. You can use
|
573
|
+
`:association_options` to modify global or per-association options:
|
574
|
+
|
575
|
+
```rb
|
576
|
+
# don't auto-delete associations when account model is deleted
|
577
|
+
Rodauth::Rails.model(association_options: { dependent: nil })
|
578
|
+
|
579
|
+
# require authentication audit logs to be eager loaded before retrieval
|
580
|
+
Rodauth::Rails.model(association_options: -> (name) {
|
581
|
+
{ strict_loading: true } if name == :authentication_audit_logs
|
582
|
+
})
|
583
|
+
```
|
584
|
+
|
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
|
+
|
424
589
|
### Multiple configurations
|
425
590
|
|
426
591
|
If you need to handle multiple types of accounts that require different
|
@@ -440,10 +605,6 @@ class RodauthApp < Rodauth::Rails::App
|
|
440
605
|
prefix "/admin"
|
441
606
|
session_key_prefix "admin_"
|
442
607
|
remember_cookie_key "_admin_remember" # if using remember feature
|
443
|
-
|
444
|
-
# if you want separate tables
|
445
|
-
accounts_table :admin_accounts
|
446
|
-
password_hash_table :admin_account_password_hashes
|
447
608
|
# ...
|
448
609
|
end
|
449
610
|
|
@@ -466,6 +627,50 @@ Then in your application you can reference the secondary Rodauth instance:
|
|
466
627
|
rodauth(:admin).login_path #=> "/admin/login"
|
467
628
|
```
|
468
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
|
+
|
469
674
|
#### Named auth classes
|
470
675
|
|
471
676
|
A `configure` block inside `Rodauth::Rails::App` will internally create an
|
@@ -584,37 +789,88 @@ class RodauthApp < Rodauth::Rails::App
|
|
584
789
|
end
|
585
790
|
```
|
586
791
|
|
587
|
-
###
|
792
|
+
### Outside of a request
|
588
793
|
|
589
|
-
In some cases you might need to use Rodauth more programmatically
|
590
|
-
|
591
|
-
|
794
|
+
In some cases you might need to use Rodauth more programmatically. If you want
|
795
|
+
to perform authentication operations outside of request context, Rodauth ships
|
796
|
+
with the [internal_request] feature just for that.
|
592
797
|
|
593
798
|
```rb
|
594
|
-
|
799
|
+
# app/lib/rodauth_app.rb
|
800
|
+
class RodauthApp < Rodauth::Rails::App
|
801
|
+
configure do
|
802
|
+
enable :internal_request
|
803
|
+
end
|
804
|
+
end
|
805
|
+
```
|
806
|
+
```rb
|
807
|
+
# main configuration
|
808
|
+
RodauthApp.rodauth.create_account(login: "user@example.com", password: "secret")
|
809
|
+
RodauthApp.rodauth.verify_account(account_login: "user@example.com")
|
595
810
|
|
596
|
-
|
597
|
-
rodauth.
|
598
|
-
rodauth.password_match?("secret") #=> true
|
599
|
-
rodauth.setup_account_verification
|
600
|
-
rodauth.close_account
|
811
|
+
# secondary configuration
|
812
|
+
RodauthApp.rodauth(:admin).close_account(account_login: "admin@example.com")
|
601
813
|
```
|
602
814
|
|
603
|
-
The
|
604
|
-
|
605
|
-
|
815
|
+
The rodauth-rails gem additionally updates the internal rack env hash with your
|
816
|
+
`config.action_mailer.default_url_options`, which is used for generating email
|
817
|
+
links.
|
606
818
|
|
607
|
-
|
608
|
-
|
609
|
-
* `:session` – set any session values
|
610
|
-
* `:env` – set any additional Rack env values
|
819
|
+
For generating authentication URLs outside of a request use the
|
820
|
+
[path_class_methods] plugin:
|
611
821
|
|
612
822
|
```rb
|
613
|
-
|
614
|
-
Rodauth::Rails
|
615
|
-
|
616
|
-
|
823
|
+
# app/lib/rodauth_app.rb
|
824
|
+
class RodauthApp < Rodauth::Rails::App
|
825
|
+
configure do
|
826
|
+
enable :path_class_methods
|
827
|
+
end
|
828
|
+
end
|
829
|
+
```
|
830
|
+
```rb
|
831
|
+
# main configuration
|
832
|
+
RodauthApp.rodauth.create_account_path
|
833
|
+
RodauthApp.rodauth.verify_account_url(key: "abc123")
|
834
|
+
|
835
|
+
# secondary configuration
|
836
|
+
RodauthApp.rodauth(:admin).close_account_path
|
837
|
+
```
|
838
|
+
|
839
|
+
#### Calling instance methods
|
840
|
+
|
841
|
+
If you need to access Rodauth methods not exposed as internal requests, you can
|
842
|
+
use `Rodauth::Rails.rodauth` to retrieve the Rodauth instance used by the
|
843
|
+
internal_request feature:
|
844
|
+
|
845
|
+
```rb
|
846
|
+
# app/lib/rodauth_app.rb
|
847
|
+
class RodauthApp < Rodauth::Rails::App
|
848
|
+
configure do
|
849
|
+
enable :internal_request # this is required
|
850
|
+
end
|
851
|
+
end
|
852
|
+
```
|
853
|
+
```rb
|
854
|
+
account = Account.find_by!(email: "user@example.com")
|
855
|
+
rodauth = Rodauth::Rails.rodauth(account: account)
|
856
|
+
|
857
|
+
rodauth.compute_hmac("token") #=> "TpEJTKfKwqYvIDKWsuZhkhKlhaBXtR1aodskBAflD8U"
|
858
|
+
rodauth.open_account? #=> true
|
859
|
+
rodauth.two_factor_authentication_setup? #=> true
|
860
|
+
rodauth.password_meets_requirements?("foo") #=> false
|
861
|
+
rodauth.locked_out? #=> false
|
862
|
+
```
|
863
|
+
|
864
|
+
In addition to the `:account` option, the `Rodauth::Rails.rodauth`
|
865
|
+
method accepts any options supported by the internal_request feature.
|
866
|
+
|
867
|
+
```rb
|
868
|
+
# main configuration
|
617
869
|
Rodauth::Rails.rodauth(env: { "HTTP_USER_AGENT" => "programmatic" })
|
870
|
+
Rodauth::Rails.rodauth(session: { two_factor_auth_setup: true })
|
871
|
+
|
872
|
+
# secondary configuration
|
873
|
+
Rodauth::Rails.rodauth(:admin, params: { "param" => "value" })
|
618
874
|
```
|
619
875
|
|
620
876
|
## How it works
|
@@ -753,21 +1009,23 @@ class RodauthApp < Rodauth::Rails::App
|
|
753
1009
|
end
|
754
1010
|
```
|
755
1011
|
|
756
|
-
|
757
|
-
|
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:
|
758
1015
|
|
759
|
-
```sh
|
760
|
-
$ rails generate rodauth:migration jwt_refresh
|
761
|
-
$ rails db:migrate
|
762
|
-
```
|
763
1016
|
```rb
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
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
|
770
1027
|
end
|
1028
|
+
# ...
|
771
1029
|
end
|
772
1030
|
```
|
773
1031
|
|
@@ -872,9 +1130,13 @@ class RodauthController < ApplicationController
|
|
872
1130
|
account.identities.create!(provider: auth["provider"], uid: auth["uid"], info: auth["info"])
|
873
1131
|
end
|
874
1132
|
|
875
|
-
#
|
1133
|
+
# load the account into the rodauth instance
|
876
1134
|
rodauth.account_from_login(account.email)
|
877
|
-
|
1135
|
+
|
1136
|
+
rodauth_response do # ensures any `after_action` callbacks get called
|
1137
|
+
# sign in the loaded account
|
1138
|
+
rodauth.login("omniauth")
|
1139
|
+
end
|
878
1140
|
end
|
879
1141
|
end
|
880
1142
|
```
|
@@ -893,6 +1155,7 @@ methods:
|
|
893
1155
|
| `rails_check_csrf!` | Verifies the authenticity token for the current request. |
|
894
1156
|
| `rails_controller_instance` | Instance of the controller with the request env context. |
|
895
1157
|
| `rails_controller` | Controller class to use for rendering and CSRF protection. |
|
1158
|
+
| `rails_account_model` | Model class connected with the accounts table. |
|
896
1159
|
|
897
1160
|
The `Rodauth::Rails` module has a few config settings available as well:
|
898
1161
|
|
@@ -1075,29 +1338,6 @@ Rails.application.configure do |config|
|
|
1075
1338
|
end
|
1076
1339
|
```
|
1077
1340
|
|
1078
|
-
If you need to create an account record with a password directly, you can do it
|
1079
|
-
as follows:
|
1080
|
-
|
1081
|
-
```rb
|
1082
|
-
# app/models/account.rb
|
1083
|
-
class Account < ApplicationRecord
|
1084
|
-
has_one :password_hash, foreign_key: :id
|
1085
|
-
end
|
1086
|
-
```
|
1087
|
-
```rb
|
1088
|
-
# app/models/account/password_hash.rb
|
1089
|
-
class Account::PasswordHash < ApplicationRecord
|
1090
|
-
belongs_to :account, foreign_key: :id
|
1091
|
-
end
|
1092
|
-
```
|
1093
|
-
```rb
|
1094
|
-
require "bcrypt"
|
1095
|
-
|
1096
|
-
account = Account.create!(email: "user@example.com", status: "verified")
|
1097
|
-
password_hash = BCrypt::Password.create("secret", cost: BCrypt::Engine::MIN_COST)
|
1098
|
-
account.create_password_hash!(id: account.id, password_hash: password_hash)
|
1099
|
-
```
|
1100
|
-
|
1101
1341
|
## Rodauth defaults
|
1102
1342
|
|
1103
1343
|
rodauth-rails changes some of the default Rodauth settings for easier setup:
|
@@ -1231,3 +1471,6 @@ conduct](https://github.com/janko/rodauth-rails/blob/master/CODE_OF_CONDUCT.md).
|
|
1231
1471
|
[single_session]: http://rodauth.jeremyevans.net/rdoc/files/doc/single_session_rdoc.html
|
1232
1472
|
[account_expiration]: http://rodauth.jeremyevans.net/rdoc/files/doc/account_expiration_rdoc.html
|
1233
1473
|
[simple_ldap_authenticator]: https://github.com/jeremyevans/simple_ldap_authenticator
|
1474
|
+
[internal_request]: http://rodauth.jeremyevans.net/rdoc/files/doc/internal_request_rdoc.html
|
1475
|
+
[composite_primary_keys]: https://github.com/composite-primary-keys/composite_primary_keys
|
1476
|
+
[path_class_methods]: https://rodauth.jeremyevans.net/rdoc/files/doc/path_class_methods_rdoc.html
|
@@ -52,7 +52,7 @@ class RodauthApp < Rodauth::Rails::App
|
|
52
52
|
# reset_password_autologin? true
|
53
53
|
|
54
54
|
# Delete the account record when the user has closed their account.
|
55
|
-
|
55
|
+
delete_account_on_close? true
|
56
56
|
|
57
57
|
# Redirect to the app from login and registration pages if already logged in.
|
58
58
|
# already_logged_in { redirect login_redirect }
|
@@ -154,11 +154,15 @@ class RodauthApp < Rodauth::Rails::App
|
|
154
154
|
<% end -%>
|
155
155
|
end
|
156
156
|
|
157
|
-
# ==>
|
157
|
+
# ==> Secondary configurations
|
158
158
|
# configure(:admin) do
|
159
|
-
#
|
159
|
+
# # ... enable features ...
|
160
160
|
# prefix "/admin"
|
161
161
|
# session_key_prefix "admin_"
|
162
|
+
# # remember_cookie_key "_admin_remember" # if using remember feature
|
163
|
+
#
|
164
|
+
# # search views in `app/views/admin/rodauth` directory
|
165
|
+
# rails_controller { Admin::RodauthController }
|
162
166
|
# end
|
163
167
|
|
164
168
|
route do |r|
|
@@ -180,7 +184,7 @@ class RodauthApp < Rodauth::Rails::App
|
|
180
184
|
# rodauth.require_authentication
|
181
185
|
# end
|
182
186
|
|
183
|
-
# ==>
|
187
|
+
# ==> Secondary configurations
|
184
188
|
# r.on "admin" do
|
185
189
|
# r.rodauth(:admin)
|
186
190
|
#
|
@@ -188,7 +192,7 @@ class RodauthApp < Rodauth::Rails::App
|
|
188
192
|
# rodauth(:admin).require_http_basic_auth
|
189
193
|
# end
|
190
194
|
#
|
191
|
-
#
|
195
|
+
# break # allow the Rails app to handle other "/admin/*" requests
|
192
196
|
# end
|
193
197
|
end
|
194
198
|
end
|
@@ -4,7 +4,7 @@
|
|
4
4
|
autocomplete: local_assigns[:autocomplete],
|
5
5
|
inputmode: local_assigns[:inputmode],
|
6
6
|
required: local_assigns[:required] != false,
|
7
|
-
class: "#{local_assigns[:class] || "form-control"} #{"is-invalid" if rodauth
|
8
|
-
aria: ({ invalid: "true", describedby: "#{name}_error_message" } if rodauth
|
7
|
+
class: "#{local_assigns[:class] || "form-control"} #{"is-invalid" if <%= rodauth %>.field_error(name)}",
|
8
|
+
aria: ({ invalid: "true", describedby: "#{name}_error_message" } if <%= rodauth %>.field_error(name)) %>
|
9
9
|
|
10
10
|
<%%= render "field_error", name: name unless local_assigns[:skip_error_message] %>
|
@@ -1,3 +1,3 @@
|
|
1
|
-
<%% if rodauth
|
2
|
-
<div class="invalid-feedback" id="<%%= name %>_error_message"><%%= rodauth
|
1
|
+
<%% if <%= rodauth %>.field_error(name) %>
|
2
|
+
<div class="invalid-feedback" id="<%%= name %>_error_message"><%%= <%= rodauth %>.field_error(name) %></div>
|
3
3
|
<%% end %>
|