rodauth-rails 0.14.0 → 0.17.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 +42 -0
- data/README.md +248 -77
- data/lib/generators/rodauth/install_generator.rb +10 -2
- data/lib/generators/rodauth/migration/base.erb +8 -2
- data/lib/generators/rodauth/templates/app/lib/rodauth_app.rb +3 -8
- data/lib/generators/rodauth/templates/app/models/account.rb +1 -0
- data/lib/generators/rodauth/templates/app/views/rodauth/_recovery_codes_form.html.erb +1 -1
- data/lib/generators/rodauth/views_generator.rb +6 -2
- data/lib/rodauth/rails/auth.rb +2 -2
- data/lib/rodauth/rails/controller_methods.rb +42 -1
- data/lib/rodauth/rails/feature/base.rb +9 -0
- data/lib/rodauth/rails/feature/callbacks.rb +6 -2
- data/lib/rodauth/rails/feature/csrf.rb +15 -4
- data/lib/rodauth/rails/feature/internal_request.rb +46 -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 +32 -31
- 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: 458a4a5552c124a1e7587837bbd57eed020857ae691bcc4b1f2e2639df774bb8
|
4
|
+
data.tar.gz: b19da17830585641950d026dff75ebd32ded19ad16dd5fb5c3b8a33c34b0a8f6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 01d5c774269d260e2805e0a2e4a509d9db81a3a966e94176cd6c5df6d808356feee48c5009b9692e0647034ae1c4d6ea3dc29627fa2000d05821a4402080601a
|
7
|
+
data.tar.gz: 1dde8d0ffb4605d3abf7893628a804eca7c0fbc94f2418176651dce81fac14eb4dfe1d165e315288a2a46373b818c795e15e9d58b5ad77f15b1fb5c3d9efc4be
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,45 @@
|
|
1
|
+
## 0.17.1 (2021-10-20)
|
2
|
+
|
3
|
+
* Skip checking CSRF when request forgery protection wasn't loaded on the controller (@janko)
|
4
|
+
|
5
|
+
* Create partial unique index for `accounts.email` column when using `sqlite3` adapter (@janko)
|
6
|
+
|
7
|
+
* Revert setting `delete_account_on_close?` to `true` in generated `rodauth_app.rb` (@janko)
|
8
|
+
|
9
|
+
* Disable Turbo in `_recovery_codes_form.html.erb`, since viewing recovery codes isn't Turbo-compatible (@janko)
|
10
|
+
|
11
|
+
* Generate JSON configuration on `rodauth:install` for API-only with sessions enabled (@janko)
|
12
|
+
|
13
|
+
* Generate JWT configuration on `rodauth:install` only for API-only apps without sessions enabled (@janko)
|
14
|
+
|
15
|
+
* Don't generate JWT configuration when `rodauth:install --json` was run in API-only app (@janko)
|
16
|
+
|
17
|
+
* Use `config.action_mailer.default_url_options` in path_class_methods feature (@janko)
|
18
|
+
|
19
|
+
## 0.17.0 (2021-10-05)
|
20
|
+
|
21
|
+
* Set `delete_account_on_close?` to `true` in generated `rodauth_app.rb` (@janko)
|
22
|
+
|
23
|
+
* Change default `:dependent` option for associations to `:delete`/`:delete_all` (@janko)
|
24
|
+
|
25
|
+
* Add `rails_account_model` configuration method for when the account model cannot be inferred (@janko)
|
26
|
+
|
27
|
+
## 0.16.0 (2021-09-26)
|
28
|
+
|
29
|
+
* Add `#current_account` to methods defined on `ActionController::Base` (@janko)
|
30
|
+
|
31
|
+
* Add missing template for verify_login_change feature to `rodauth:views` generator (@janko)
|
32
|
+
|
33
|
+
* Add `#rodauth_response` controller method for converting rodauth responses into controller responses (@janko)
|
34
|
+
|
35
|
+
## 0.15.0 (2021-07-29)
|
36
|
+
|
37
|
+
* Add `Rodauth::Rails::Model` mixin that defines password attribute and associations on the model (@janko)
|
38
|
+
|
39
|
+
* Add support for the new internal_request feature (@janko)
|
40
|
+
|
41
|
+
* Implement `Rodauth::Rails.rodauth` in terms of the internal_request feature (@janko)
|
42
|
+
|
1
43
|
## 0.14.0 (2021-07-10)
|
2
44
|
|
3
45
|
* Speed up template rendering by only searching formats accepted by the request (@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
|
@@ -453,6 +458,134 @@ class CreateRodauthOtpSmsCodesRecoveryCodes < ActiveRecord::Migration
|
|
453
458
|
end
|
454
459
|
```
|
455
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
|
+
|
456
589
|
### Multiple configurations
|
457
590
|
|
458
591
|
If you need to handle multiple types of accounts that require different
|
@@ -656,37 +789,88 @@ class RodauthApp < Rodauth::Rails::App
|
|
656
789
|
end
|
657
790
|
```
|
658
791
|
|
659
|
-
###
|
792
|
+
### Outside of a request
|
660
793
|
|
661
|
-
In some cases you might need to use Rodauth more programmatically
|
662
|
-
|
663
|
-
|
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.
|
664
797
|
|
665
798
|
```rb
|
666
|
-
|
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")
|
667
810
|
|
668
|
-
|
669
|
-
rodauth.
|
670
|
-
rodauth.password_match?("secret") #=> true
|
671
|
-
rodauth.setup_account_verification
|
672
|
-
rodauth.close_account
|
811
|
+
# secondary configuration
|
812
|
+
RodauthApp.rodauth(:admin).close_account(account_login: "admin@example.com")
|
673
813
|
```
|
674
814
|
|
675
|
-
The
|
676
|
-
|
677
|
-
|
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.
|
678
818
|
|
679
|
-
|
680
|
-
|
681
|
-
* `:session` – set any session values
|
682
|
-
* `:env` – set any additional Rack env values
|
819
|
+
For generating authentication URLs outside of a request use the
|
820
|
+
[path_class_methods] plugin:
|
683
821
|
|
684
822
|
```rb
|
685
|
-
|
686
|
-
Rodauth::Rails
|
687
|
-
|
688
|
-
|
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
|
689
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" })
|
690
874
|
```
|
691
875
|
|
692
876
|
## How it works
|
@@ -825,21 +1009,23 @@ class RodauthApp < Rodauth::Rails::App
|
|
825
1009
|
end
|
826
1010
|
```
|
827
1011
|
|
828
|
-
|
829
|
-
|
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:
|
830
1015
|
|
831
|
-
```sh
|
832
|
-
$ rails generate rodauth:migration jwt_refresh
|
833
|
-
$ rails db:migrate
|
834
|
-
```
|
835
1016
|
```rb
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
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
|
842
1027
|
end
|
1028
|
+
# ...
|
843
1029
|
end
|
844
1030
|
```
|
845
1031
|
|
@@ -944,9 +1130,13 @@ class RodauthController < ApplicationController
|
|
944
1130
|
account.identities.create!(provider: auth["provider"], uid: auth["uid"], info: auth["info"])
|
945
1131
|
end
|
946
1132
|
|
947
|
-
#
|
1133
|
+
# load the account into the rodauth instance
|
948
1134
|
rodauth.account_from_login(account.email)
|
949
|
-
|
1135
|
+
|
1136
|
+
rodauth_response do # ensures any `after_action` callbacks get called
|
1137
|
+
# sign in the loaded account
|
1138
|
+
rodauth.login("omniauth")
|
1139
|
+
end
|
950
1140
|
end
|
951
1141
|
end
|
952
1142
|
```
|
@@ -965,6 +1155,7 @@ methods:
|
|
965
1155
|
| `rails_check_csrf!` | Verifies the authenticity token for the current request. |
|
966
1156
|
| `rails_controller_instance` | Instance of the controller with the request env context. |
|
967
1157
|
| `rails_controller` | Controller class to use for rendering and CSRF protection. |
|
1158
|
+
| `rails_account_model` | Model class connected with the accounts table. |
|
968
1159
|
|
969
1160
|
The `Rodauth::Rails` module has a few config settings available as well:
|
970
1161
|
|
@@ -1147,29 +1338,6 @@ Rails.application.configure do |config|
|
|
1147
1338
|
end
|
1148
1339
|
```
|
1149
1340
|
|
1150
|
-
If you need to create an account record with a password directly, you can do it
|
1151
|
-
as follows:
|
1152
|
-
|
1153
|
-
```rb
|
1154
|
-
# app/models/account.rb
|
1155
|
-
class Account < ApplicationRecord
|
1156
|
-
has_one :password_hash, foreign_key: :id
|
1157
|
-
end
|
1158
|
-
```
|
1159
|
-
```rb
|
1160
|
-
# app/models/account/password_hash.rb
|
1161
|
-
class Account::PasswordHash < ApplicationRecord
|
1162
|
-
belongs_to :account, foreign_key: :id
|
1163
|
-
end
|
1164
|
-
```
|
1165
|
-
```rb
|
1166
|
-
require "bcrypt"
|
1167
|
-
|
1168
|
-
account = Account.create!(email: "user@example.com", status: "verified")
|
1169
|
-
password_hash = BCrypt::Password.create("secret", cost: BCrypt::Engine::MIN_COST)
|
1170
|
-
account.create_password_hash!(id: account.id, password_hash: password_hash)
|
1171
|
-
```
|
1172
|
-
|
1173
1341
|
## Rodauth defaults
|
1174
1342
|
|
1175
1343
|
rodauth-rails changes some of the default Rodauth settings for easier setup:
|
@@ -1303,3 +1471,6 @@ conduct](https://github.com/janko/rodauth-rails/blob/master/CODE_OF_CONDUCT.md).
|
|
1303
1471
|
[single_session]: http://rodauth.jeremyevans.net/rdoc/files/doc/single_session_rdoc.html
|
1304
1472
|
[account_expiration]: http://rodauth.jeremyevans.net/rdoc/files/doc/account_expiration_rdoc.html
|
1305
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
|
@@ -95,11 +95,11 @@ module Rodauth
|
|
95
95
|
end
|
96
96
|
|
97
97
|
def json?
|
98
|
-
options[:json]
|
98
|
+
options[:json] || api_only? && session_store? && !options[:jwt]
|
99
99
|
end
|
100
100
|
|
101
101
|
def jwt?
|
102
|
-
options[:jwt] ||
|
102
|
+
options[:jwt] || api_only? && !session_store? && !options[:json]
|
103
103
|
end
|
104
104
|
|
105
105
|
def migration_features
|
@@ -107,6 +107,14 @@ module Rodauth
|
|
107
107
|
features << :remember unless jwt?
|
108
108
|
features
|
109
109
|
end
|
110
|
+
|
111
|
+
def session_store?
|
112
|
+
!!::Rails.application.config.session_store
|
113
|
+
end
|
114
|
+
|
115
|
+
def api_only?
|
116
|
+
Rodauth::Rails.api_only?
|
117
|
+
end
|
110
118
|
end
|
111
119
|
end
|
112
120
|
end
|
@@ -5,11 +5,17 @@ enable_extension "citext"
|
|
5
5
|
create_table :accounts<%= primary_key_type %> do |t|
|
6
6
|
<% case activerecord_adapter -%>
|
7
7
|
<% when "postgresql" -%>
|
8
|
-
t.citext :email, null: false
|
8
|
+
t.citext :email, null: false
|
9
9
|
<% else -%>
|
10
|
-
t.string :email, null: false
|
10
|
+
t.string :email, null: false
|
11
11
|
<% end -%>
|
12
12
|
t.string :status, null: false, default: "unverified"
|
13
|
+
<% case activerecord_adapter -%>
|
14
|
+
<% when "postgresql", "sqlite3" -%>
|
15
|
+
t.index :email, unique: true, where: "status IN ('unverified', 'verified')"
|
16
|
+
<% else -%>
|
17
|
+
t.index :email, unique: true
|
18
|
+
<% end -%>
|
13
19
|
end
|
14
20
|
|
15
21
|
# Used if storing password hashes in a separate table (default)
|
@@ -154,7 +154,7 @@ 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"
|
@@ -163,11 +163,6 @@ class RodauthApp < Rodauth::Rails::App
|
|
163
163
|
#
|
164
164
|
# # search views in `app/views/admin/rodauth` directory
|
165
165
|
# rails_controller { Admin::RodauthController }
|
166
|
-
#
|
167
|
-
# # use separate tables (requires creating the new tables)
|
168
|
-
# methods.grep(/_table$/) do |table_method|
|
169
|
-
# public_send(table_method) { :"admin_#{super()}" }
|
170
|
-
# end
|
171
166
|
# end
|
172
167
|
|
173
168
|
route do |r|
|
@@ -189,7 +184,7 @@ class RodauthApp < Rodauth::Rails::App
|
|
189
184
|
# rodauth.require_authentication
|
190
185
|
# end
|
191
186
|
|
192
|
-
# ==>
|
187
|
+
# ==> Secondary configurations
|
193
188
|
# r.on "admin" do
|
194
189
|
# r.rodauth(:admin)
|
195
190
|
#
|
@@ -197,7 +192,7 @@ class RodauthApp < Rodauth::Rails::App
|
|
197
192
|
# rodauth(:admin).require_http_basic_auth
|
198
193
|
# end
|
199
194
|
#
|
200
|
-
#
|
195
|
+
# break # allow the Rails app to handle other "/admin/*" requests
|
201
196
|
# end
|
202
197
|
end
|
203
198
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
<%%= form_tag <%= rodauth %>.recovery_codes_path, method: :post do %>
|
1
|
+
<%%= form_tag <%= rodauth %>.recovery_codes_path, method: :post, data: { turbo: false } do %>
|
2
2
|
<%%= render "password_field" if <%= rodauth %>.two_factor_modifications_require_password? %>
|
3
3
|
<%%= render "submit",
|
4
4
|
value: <%= rodauth %>.recovery_codes_button || "View Authentication Recovery Codes",
|
@@ -61,6 +61,9 @@ module Rodauth
|
|
61
61
|
_field _field_error _login_hidden_field _login_field _submit
|
62
62
|
verify_account_resend verify_account
|
63
63
|
],
|
64
|
+
verify_login_change: %w[
|
65
|
+
_submit verify_login_change
|
66
|
+
],
|
64
67
|
lockout: %w[
|
65
68
|
_login_hidden_field _submit unlock_account_request unlock_account
|
66
69
|
],
|
@@ -129,8 +132,9 @@ module Rodauth
|
|
129
132
|
end
|
130
133
|
|
131
134
|
def controller
|
132
|
-
rodauth = Rodauth::Rails.rodauth(configuration_name)
|
133
|
-
rodauth.
|
135
|
+
rodauth = Rodauth::Rails.app.rodauth(configuration_name)
|
136
|
+
fail ArgumentError, "unknown rodauth configuration: #{configuration_name.inspect}" unless rodauth
|
137
|
+
rodauth.allocate.rails_controller
|
134
138
|
end
|
135
139
|
|
136
140
|
def configuration_name
|
data/lib/rodauth/rails/auth.rb
CHANGED
@@ -6,10 +6,10 @@ module Rodauth
|
|
6
6
|
# Base auth class that applies some default configuration and supports
|
7
7
|
# multi-level inheritance.
|
8
8
|
class Auth < Rodauth::Auth
|
9
|
-
def self.inherited(
|
9
|
+
def self.inherited(subclass)
|
10
10
|
super
|
11
11
|
superclass = self
|
12
|
-
|
12
|
+
subclass.class_eval do
|
13
13
|
@roda_class = Rodauth::Rails.app
|
14
14
|
@features = superclass.features.clone
|
15
15
|
@routes = superclass.routes.clone
|
@@ -4,13 +4,54 @@ module Rodauth
|
|
4
4
|
def self.included(controller)
|
5
5
|
# ActionController::API doesn't have helper methods
|
6
6
|
if controller.respond_to?(:helper_method)
|
7
|
-
controller.helper_method :rodauth
|
7
|
+
controller.helper_method :rodauth, :current_account
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
11
|
def rodauth(name = nil)
|
12
12
|
request.env.fetch ["rodauth", *name].join(".")
|
13
13
|
end
|
14
|
+
|
15
|
+
def current_account(name = nil)
|
16
|
+
model = rodauth(name).rails_account_model
|
17
|
+
id = rodauth(name).session_value
|
18
|
+
|
19
|
+
@current_account ||= {}
|
20
|
+
@current_account[name] ||= fetch_account(model, id) do
|
21
|
+
rodauth(name).clear_session
|
22
|
+
rodauth(name).login_required
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def fetch_account(model, id, ¬_found)
|
29
|
+
if defined?(ActiveRecord::Base) && model < ActiveRecord::Base
|
30
|
+
begin
|
31
|
+
model.find(id)
|
32
|
+
rescue ActiveRecord::RecordNotFound
|
33
|
+
not_found.call
|
34
|
+
end
|
35
|
+
elsif model < Sequel::Model
|
36
|
+
begin
|
37
|
+
model.with_pk!(id)
|
38
|
+
rescue Sequel::NoMatchingRow
|
39
|
+
not_found.call
|
40
|
+
end
|
41
|
+
else
|
42
|
+
fail Error, "unsupported model type: #{model}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def rodauth_response
|
47
|
+
res = catch(:halt) { return yield }
|
48
|
+
|
49
|
+
self.status = res[0]
|
50
|
+
self.headers.merge! res[1]
|
51
|
+
self.response_body = res[2]
|
52
|
+
|
53
|
+
res
|
54
|
+
end
|
14
55
|
end
|
15
56
|
end
|
16
57
|
end
|