rodauth-rails 0.5.0 → 0.8.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f0d00b7ad2f6198fff3a5cc3c720c6f30d9296898e3fd764ddf7408e36232a6d
4
- data.tar.gz: 9ba008116fc5521c98ed62dda5b1f6b2eccd733d06ccb0a2c5b9b4db8df94539
3
+ metadata.gz: a3f1fd01ddd20052bd15e3c822ff44ca4a6f5d425dd18892ecffa34146364437
4
+ data.tar.gz: 8907b8616edf882d21ebff69b461cc12dae737f8ec34bdaf5c2d58ac5cc9b632
5
5
  SHA512:
6
- metadata.gz: 848873e599cfb8dc8a5d274ac5fec5987cf4c895c77757a4b21529ddcd9a635ba8086c037f55c55ef097ae926549234e5abebb97a5300f0ead6118927d737d91
7
- data.tar.gz: aa75fb48217e79c40000cf1f226f36a65fdffdcb764233572ae45341b7ab9dd2f1d8f8470e593d8260495962b1b5c352e62d0d5e71fed0cb96891da93a0f344c
6
+ metadata.gz: 917915fc72c29b668716d3234f2f85e8c92406ea14a63e82284750a5536ec016acb8714064888732eed9fb1443cb803c11e0764b23a76de9de64a9fda11d577f
7
+ data.tar.gz: f30214be433882a3be04fb988fcd1d4860c50c8cb8beced94f8b7529f45904c8a8c1eb12486e39f890ccce39f1321a02f1e92fce2cc07ca24f1a86509ddd4a59
@@ -1,3 +1,41 @@
1
+ ## 0.8.1 (2021-01-04)
2
+
3
+ * Fix blank email body when `json: true` and `ActionController::API` descendant are used (@janko)
4
+
5
+ * Make view and email rendering work when there are multiple configurations and one is `json: :only` (@janko)
6
+
7
+ * Don't attempt to protect against forgery when `ActionController::API` descendant is used (@janko)
8
+
9
+ * Mark content of rodauth built-in partials as HTML-safe (@janko)
10
+
11
+ ## 0.8.0 (2021-01-03)
12
+
13
+ * Add `--api` option to `rodauth:install` generator for choosing JSON-only configuration (@janko)
14
+
15
+ * Don't blow up when a Rodauth request is made using an unsupported HTTP verb (@janko)
16
+
17
+ ## 0.7.0 (2020-11-27)
18
+
19
+ * Add `#rails_controller_eval` method for running code in context of a controller instance (@janko)
20
+
21
+ * Detect `secret_key_base` from credentials and `$SECRET_KEY_BASE` environment variable (@janko)
22
+
23
+ ## 0.6.1 (2020-11-25)
24
+
25
+ * Generate the Rodauth controller for API-only Rails apps as well (@janko)
26
+
27
+ * Fix remember cookie deadline not extending in remember feature (@janko)
28
+
29
+ ## 0.6.0 (2020-11-22)
30
+
31
+ * Add `Rodauth::Rails.rodauth` method for retrieving Rodauth instance outside of request context (@janko)
32
+
33
+ * Add default Action Dispatch response headers in Rodauth responses (@janko)
34
+
35
+ * Run controller rescue handlers around Rodauth actions (@janko)
36
+
37
+ * Run controller action callbacks around Rodauth actions (@janko)
38
+
1
39
  ## 0.5.0 (2020-11-16)
2
40
 
3
41
  * Support more Active Record adapters in `rodauth:install` generator (@janko)
data/README.md CHANGED
@@ -4,16 +4,57 @@ Provides Rails integration for the [Rodauth] authentication framework.
4
4
 
5
5
  ## Resources
6
6
 
7
+ Useful links:
8
+
7
9
  * [Rodauth documentation](http://rodauth.jeremyevans.net/documentation.html)
8
- * [rodauth-rails wiki](https://github.com/janko/rodauth-rails/wiki)
9
10
  * [Rails demo](https://github.com/janko/rodauth-demo-rails)
10
11
 
12
+ Articles:
13
+
14
+ * [Rodauth: A Refreshing Authentication Solution for Ruby](https://janko.io/rodauth-a-refreshing-authentication-solution-for-ruby/)
15
+ * [Adding Authentication in Rails with Rodauth](https://janko.io/adding-authentication-in-rails-with-rodauth/)
16
+ * [Adding Multifactor Authentication in Rails with Rodauth](https://janko.io/adding-multifactor-authentication-in-rails-with-rodauth/)
17
+
18
+ ## Why Rodauth?
19
+
20
+ There are already several popular authentication solutions for Rails (Devise,
21
+ Sorcery, Clearance, Authlogic), so why would you choose Rodauth? Well, because
22
+ it has many advantages over the mentioned alternatives:
23
+
24
+ * multifactor authentication ([TOTP][otp], [SMS codes][sms_codes], [recovery codes][recovery_codes], [WebAuthn][webauthn])
25
+ * standardized [JSON API support][jwt] (for every feature)
26
+ * enterprise security features ([password complexity][password_complexity], [disallow password reuse][disallow_password_reuse], [password expiration][password_expiration], [session expiration][session_expiration], [single session][single_session], [account expiration][account_expiration])
27
+ * [email authentication][email_auth] (aka "passwordless")
28
+ * [audit logging][audit_logging] (for any action)
29
+ * ability to protect password hashes even in case of SQL injection ([more details][password protection])
30
+ * additional bruteforce protection for tokens ([more details][bruteforce tokens])
31
+ * uniform configuration DSL (any setting can be static or dynamic)
32
+ * consistent before/after hooks around everything
33
+ * dedicated object encapsulating all authentication logic
34
+
35
+ ## Upgrading
36
+
37
+ ### Upgrading to 0.7.0
38
+
39
+ Starting from version 0.7.0, rodauth-rails now correctly detects Rails
40
+ application's `secret_key_base` when setting default `hmac_secret`, including
41
+ when it's set via credentials or `$SECRET_KEY_BASE` environment variable. This
42
+ means that your authentication will now be more secure by default, and Rodauth
43
+ features that require `hmac_secret` should now work automatically as well.
44
+
45
+ However, if you've already been using rodauth-rails in production, where the
46
+ `secret_key_base` is set via credentials or environment variable and `hmac_secret`
47
+ was not explicitly set, the fact that your authentication will now start using
48
+ HMACs has backwards compatibility considerations. See the [Rodauth
49
+ documentation][hmac] for instructions on how to safely transition, or just set
50
+ `hmac_secret nil` in your Rodauth configuration.
51
+
11
52
  ## Installation
12
53
 
13
54
  Add the gem to your Gemfile:
14
55
 
15
56
  ```rb
16
- gem "rodauth-rails", "~> 0.4"
57
+ gem "rodauth-rails", "~> 0.6"
17
58
 
18
59
  # gem "jwt", require: false # for JWT feature
19
60
  # gem "rotp", require: false # for OTP feature
@@ -25,10 +66,17 @@ Then run `bundle install`.
25
66
 
26
67
  Next, run the install generator:
27
68
 
28
- ```
69
+ ```sh
29
70
  $ rails generate rodauth:install
30
71
  ```
31
72
 
73
+ Or if you want Rodauth endpoints to be exposed via JSON API:
74
+
75
+ ```sh
76
+ $ rails generate rodauth:install --api
77
+ $ bundle add jwt
78
+ ```
79
+
32
80
  The generator will create the following files:
33
81
 
34
82
  * Rodauth migration at `db/migrate/*_create_rodauth.rb`
@@ -111,8 +159,9 @@ end
111
159
 
112
160
  ### Controller
113
161
 
114
- Your Rodauth app will by default use `RodauthController` for view rendering
115
- and CSRF protection.
162
+ Your Rodauth app will by default use `RodauthController` for view rendering,
163
+ CSRF protection, and running controller callbacks and rescue handlers around
164
+ Rodauth actions.
116
165
 
117
166
  ```rb
118
167
  # app/controllers/rodauth_controller.rb
@@ -131,9 +180,11 @@ class Account < ApplicationRecord
131
180
  end
132
181
  ```
133
182
 
134
- ## Getting started
183
+ ## Usage
184
+
185
+ ### Routes
135
186
 
136
- First, let's see what routes our Rodauth middleware will handle:
187
+ We can see the list of routes our Rodauth middleware handles:
137
188
 
138
189
  ```sh
139
190
  $ rails rodauth:routes
@@ -155,54 +206,57 @@ Routes handled by RodauthApp:
155
206
  /close-account rodauth.close_account_path
156
207
  ```
157
208
 
158
- We can use this information to add some basic authentication navigation links
159
- to our home page:
209
+ Using this information, we could add some basic authentication links to our
210
+ navigation header:
160
211
 
161
212
  ```erb
162
- <ul>
163
- <% if rodauth.authenticated? %>
164
- <li><%= link_to "Sign out", rodauth.logout_path, method: :post %></li>
165
- <% else %>
166
- <li><%= link_to "Sign in", rodauth.login_path %></li>
167
- <li><%= link_to "Sign up", rodauth.create_account_path %></li>
168
- <% end %>
169
- </ul>
213
+ <% if rodauth.logged_in? %>
214
+ <%= link_to "Sign out", rodauth.logout_path, method: :post %>
215
+ <% else %>
216
+ <%= link_to "Sign in", rodauth.login_path %>
217
+ <%= link_to "Sign up", rodauth.create_account_path %>
218
+ <% end %>
170
219
  ```
171
220
 
172
- These links are fully functional, feel free to visit them and interact with the
221
+ These routes are fully functional, feel free to visit them and interact with the
173
222
  pages. The templates that ship with Rodauth aim to provide a complete
174
223
  authentication experience, and the forms use [Bootstrap] markup.
175
224
 
176
- Let's also load the account record for authenticated requests and expose it via
177
- `#current_account`:
225
+ ### Current account
226
+
227
+ To be able to fetch currently authenticated account, let's define a
228
+ `#current_account` method that fetches the account id from session and
229
+ retrieves the corresponding account record:
178
230
 
179
231
  ```rb
180
232
  # app/controllers/application_controller.rb
181
233
  class ApplicationController < ActionController::Base
182
- before_action :load_account, if: -> { rodauth.authenticated? }
234
+ before_action :current_account, if: -> { rodauth.logged_in? }
183
235
 
184
236
  private
185
237
 
186
- def load_account
187
- @current_account = Account.find(rodauth.session_value)
238
+ def current_account
239
+ @current_account ||= Account.find(rodauth.session_value)
188
240
  rescue ActiveRecord::RecordNotFound
189
241
  rodauth.logout
190
242
  rodauth.login_required
191
243
  end
192
-
193
- attr_reader :current_account
194
244
  helper_method :current_account
195
245
  end
196
246
  ```
247
+
248
+ This allows us to access the current account in controllers and views:
249
+
197
250
  ```erb
198
251
  <p>Authenticated as: <%= current_account.email %></p>
199
252
  ```
200
253
 
201
254
  ### Requiring authentication
202
255
 
203
- Next, we'll likely want to require authentication for certain sections/pages of
204
- our app. We can do this in our Rodauth app's routing block, which helps keep
205
- the authentication logic encapsulated:
256
+ We'll likely want to require authentication for certain parts of our app,
257
+ redirecting the user to the login page if they're not logged in. We can do this
258
+ in our Rodauth app's routing block, which helps keep the authentication logic
259
+ encapsulated:
206
260
 
207
261
  ```rb
208
262
  # app/lib/rodauth_app.rb
@@ -260,9 +314,9 @@ end
260
314
 
261
315
  ### Views
262
316
 
263
- The templates built into Rodauth are useful when getting started, but at some
264
- point we'll probably want more control over the markup. For that we can run the
265
- following command:
317
+ The templates built into Rodauth are useful when getting started, but soon
318
+ you'll want to start editing the markup. You can run the following command to
319
+ copy Rodauth templates into your Rails app:
266
320
 
267
321
  ```sh
268
322
  $ rails generate rodauth:views
@@ -286,7 +340,7 @@ $ rails generate rodauth:views --all
286
340
  ```
287
341
 
288
342
  You can also tell the generator to create views into another directory (in this
289
- case make sure to rename the Rodauth controller accordingly).
343
+ case make sure to rename the Rodauth controller accordingly):
290
344
 
291
345
  ```sh
292
346
  # generates views into app/views/authentication
@@ -351,7 +405,7 @@ $ rails generate rodauth:mailer
351
405
  ```
352
406
 
353
407
  This will create a `RodauthMailer` with the associated mailer views in
354
- `app/views/rodauth_mailer` directory.
408
+ `app/views/rodauth_mailer` directory:
355
409
 
356
410
  ```rb
357
411
  # app/mailers/rodauth_mailer.rb
@@ -366,8 +420,8 @@ end
366
420
  ```
367
421
 
368
422
  You can then uncomment the lines in your Rodauth configuration to have it call
369
- your mailer. If you've enabled additional authentication features, make sure to
370
- override their `send_*_email` methods as well.
423
+ your mailer. If you've enabled additional authentication features that send
424
+ emails, make sure to override their `create_*_email` methods as well.
371
425
 
372
426
  ```rb
373
427
  # app/lib/rodauth_app.rb
@@ -375,43 +429,45 @@ class RodauthApp < Rodauth::Rails::App
375
429
  # ...
376
430
  configure do
377
431
  # ...
378
- send_reset_password_email do
379
- mailer_send(:reset_password, email_to, reset_password_email_link)
432
+ create_reset_password_email do
433
+ RodauthMailer.reset_password(email_to, reset_password_email_link)
380
434
  end
381
- send_verify_account_email do
382
- mailer_send(:verify_account, email_to, verify_account_email_link)
435
+ create_verify_account_email do
436
+ RodauthMailer.verify_account(email_to, verify_account_email_link)
383
437
  end
384
- send_verify_login_change_email do |login|
385
- mailer_send(:verify_login_change, login, verify_login_change_old_login, verify_login_change_new_login, verify_login_change_email_link)
438
+ create_verify_login_change_email do |login|
439
+ RodauthMailer.verify_login_change(login, verify_login_change_old_login, verify_login_change_new_login, verify_login_change_email_link)
386
440
  end
387
- send_password_changed_email do
388
- mailer_send(:password_changed, email_to)
441
+ create_password_changed_email do
442
+ RodauthMailer.password_changed(email_to)
389
443
  end
390
- # send_email_auth_email do
391
- # mailer_send(:email_auth, email_to, email_auth_email_link)
444
+ # create_email_auth_email do
445
+ # RodauthMailer.email_auth(email_to, email_auth_email_link)
392
446
  # end
393
- # send_unlock_account_email do
394
- # mailer_send(:unlock_account, email_to, unlock_account_email_link)
447
+ # create_unlock_account_email do
448
+ # RodauthMailer.unlock_account(email_to, unlock_account_email_link)
395
449
  # end
396
- auth_class_eval do
450
+ send_email do |email|
397
451
  # queue email delivery on the mailer after the transaction commits
398
- def mailer_send(type, *args)
399
- db.after_commit do
400
- RodauthMailer.public_send(type, *args).deliver_later
401
- end
402
- end
452
+ db.after_commit { email.deliver_later }
403
453
  end
404
454
  # ...
405
455
  end
406
456
  end
407
457
  ```
408
458
 
459
+ This approach can be used even if you're using a 3rd-party service for
460
+ transactional emails, where emails are sent via HTTP instead of SMTP. Whatever
461
+ the `create_*_email` block returns will be passed to `send_email`, so you can
462
+ be creative.
463
+
409
464
  ### Migrations
410
465
 
411
- The install generator will have created some default tables, but you can use
412
- the migration generator to create tables for any additional Rodauth features:
466
+ The install generator will create a migration for tables used by the Rodauth
467
+ features enabled by default. For any additional features, you can use the
468
+ migration generator to create the corresponding tables:
413
469
 
414
- ```
470
+ ```sh
415
471
  $ rails generate rodauth:migration otp sms_codes recovery_codes
416
472
  ```
417
473
  ```rb
@@ -425,36 +481,50 @@ class CreateRodauthOtpSmsCodesRecoveryCodes < ActiveRecord::Migration
425
481
  end
426
482
  ```
427
483
 
428
- ### JSON API
484
+ ### Calling controller methods
429
485
 
430
- JSON API support in Rodauth is provided by the [JWT feature]. First you'll need
431
- to add the [JWT gem] to your Gemfile:
486
+ When using Rodauth before/after hooks or generally overriding your Rodauth
487
+ configuration, in some cases you might want to call methods defined on your
488
+ controllers. You can do so with `rails_controller_eval`, for example:
432
489
 
433
490
  ```rb
434
- gem "jwt"
491
+ # app/controllers/application_controller.rb
492
+ class ApplicationController < ActionController::Base
493
+ private
494
+ def setup_tracking(account_id)
495
+ # ... some implementation ...
496
+ end
497
+ end
435
498
  ```
436
-
437
- The following configuration will enable the Rodauth endpoints to be accessed
438
- via JSON requests (in addition to HTML requests):
439
-
440
499
  ```rb
441
500
  # app/lib/rodauth_app.rb
442
501
  class RodauthApp < Rodauth::Rails::App
443
- configure(json: true) do
444
- # ...
445
- enable :jwt
446
- jwt_secret "...your secret key..."
447
- # ...
502
+ configure do
503
+ after_create_account do
504
+ rails_controller_eval { setup_tracking(account_id) }
505
+ end
448
506
  end
449
507
  end
450
508
  ```
451
509
 
452
- If you want the endpoints to be only accessible via JSON requests, or if your
453
- Rails app is in API-only mode, instead of `json: true` pass `json: :only` to
454
- the configure method.
510
+ ### Rodauth instance
511
+
512
+ In some cases you might need to use Rodauth more programmatically, and perform
513
+ Rodauth operations outside of the request context. rodauth-rails gives you the
514
+ ability to retrieve the Rodauth instance:
455
515
 
456
- Make sure to store the `jwt_secret` in a secure place, such as Rails
457
- credentials or environment variables.
516
+ ```rb
517
+ rodauth = Rodauth::Rails.rodauth # or Rodauth::Rails.rodauth(:secondary)
518
+
519
+ rodauth.login_url #=> "https://example.com/login"
520
+ rodauth.account_from_login("user@example.com") # loads user by email
521
+ rodauth.password_match?("secret") #=> true
522
+ rodauth.setup_account_verification
523
+ rodauth.close_account
524
+ ```
525
+
526
+ This Rodauth instance will be initialized with basic Rack env that allows is it
527
+ to generate URLs, using `config.action_mailer.default_url_options` options.
458
528
 
459
529
  ## How it works
460
530
 
@@ -500,19 +570,45 @@ end
500
570
  The `Rodauth::Rails::App` class is a [Roda] subclass that provides Rails
501
571
  integration for Rodauth:
502
572
 
503
- * uses Rails' flash instead of Roda's
504
- * uses Rails' CSRF protection instead of Roda's
573
+ * uses Action Dispatch flash instead of Roda's
574
+ * uses Action Dispatch CSRF protection instead of Roda's
505
575
  * sets [HMAC] secret to Rails' secret key base
506
- * uses ActionController for rendering templates
507
- * uses ActionMailer for sending emails
576
+ * uses Action Controller for rendering templates
577
+ * runs Action Controller callbacks & rescue handlers around Rodauth actions
578
+ * uses Action Mailer for sending emails
508
579
 
509
- The `configure { ... }` method wraps configuring the Rodauth plugin, forwarding
580
+ The `configure` method wraps configuring the Rodauth plugin, forwarding
510
581
  any additional [plugin options].
511
582
 
512
583
  ```rb
513
- configure { ... } # defining default Rodauth configuration
514
- configure(json: true) { ... } # passing options to the Rodauth plugin
515
- configure(:secondary) { ... } # defining multiple Rodauth configurations
584
+ class RodauthApp < Rodauth::Rails::App
585
+ configure { ... } # defining default Rodauth configuration
586
+ configure(json: true) { ... } # passing options to the Rodauth plugin
587
+ configure(:secondary) { ... } # defining multiple Rodauth configurations
588
+ end
589
+ ```
590
+
591
+ The `route` block is provided by Roda, and it's called on each request before
592
+ it reaches the Rails router.
593
+
594
+ ```rb
595
+ class RodauthApp < Rodauth::Rails::App
596
+ route do |r|
597
+ # ... called before each request ...
598
+ end
599
+ end
600
+ ```
601
+
602
+ Since `Rodauth::Rails::App` is just a Roda subclass, you can do anything you
603
+ would with a Roda app, such as loading additional Roda plugins:
604
+
605
+ ```rb
606
+ class RodauthApp < Rodauth::Rails::App
607
+ plugin :request_headers # easier access to request headers
608
+ plugin :typecast_params # methods for conversion of request params
609
+ plugin :default_headers, { "Foo" => "Bar" }
610
+ # ...
611
+ end
516
612
  ```
517
613
 
518
614
  ### Sequel
@@ -528,6 +624,142 @@ connection (using the [sequel-activerecord_connection] gem).
528
624
  This means that, from the usage perspective, Sequel can be considered just
529
625
  as an implementation detail of Rodauth.
530
626
 
627
+ ## JSON API
628
+
629
+ JSON API support in Rodauth is provided by the [JWT feature][jwt]. You'll need
630
+ to install the [JWT gem], enable JSON support and enable the JWT feature:
631
+
632
+ ```sh
633
+ $ bundle add jwt
634
+ ```
635
+ ```rb
636
+ # app/lib/rodauth_app.rb
637
+ class RodauthApp < Rodauth::Rails::App
638
+ configure(json: :only) do
639
+ # ...
640
+ enable :jwt
641
+ # make sure to store the JWT secret below in a safe place
642
+ jwt_secret "...your secret key..."
643
+ # ...
644
+ end
645
+ end
646
+ ```
647
+
648
+ With the above configuration, Rodauth routes will only be accessible via JSON
649
+ requests. If you still want to allow HTML access alongside JSON, change `json:
650
+ :only` to `json: true`.
651
+
652
+ Emails will automatically work in JSON-only mode, because `Rodauth::Rails::App`
653
+ comes with Roda's `render` plugin loaded. They are customized the same as in
654
+ the non-JSON case.
655
+
656
+ ## OmniAuth
657
+
658
+ While Rodauth doesn't yet come with [OmniAuth] integration, we can build one
659
+ ourselves using the existing Rodauth API.
660
+
661
+ In order to allow the user to login via multiple external providers, let's
662
+ create an `account_identities` table that will have a many-to-one relationship
663
+ with the `accounts` table:
664
+
665
+ ```sh
666
+ $ rails generate model AccountIdentity
667
+ ```
668
+ ```rb
669
+ # db/migrate/*_create_account_identities.rb
670
+ class CreateAccountIdentities < ActiveRecord::Migration
671
+ def change
672
+ create_table :account_identities do |t|
673
+ t.references :account, null: false, foreign_key: { on_delete: :cascade }
674
+ t.string :provider, null: false
675
+ t.string :uid, null: false
676
+ t.jsonb :info, null: false, default: {} # adjust JSON column type for your database
677
+
678
+ t.timestamps
679
+
680
+ t.index [:provider, :uid], unique: true
681
+ end
682
+ end
683
+ end
684
+ ```
685
+ ```rb
686
+ # app/models/account_identity.rb
687
+ class AcccountIdentity < ApplicationRecord
688
+ belongs_to :account
689
+ end
690
+ ```
691
+ ```rb
692
+ # app/models/account.rb
693
+ class Account < ApplicationRecord
694
+ has_many :identities, class_name: "AccountIdentity"
695
+ end
696
+ ```
697
+
698
+ Let's assume we want to implement Facebook login, and have added the
699
+ corresponding OmniAuth strategy to the middleware stack, together with an
700
+ authorization link on the login form:
701
+
702
+ ```rb
703
+ Rails.application.config.middleware.use OmniAuth::Builder do
704
+ provider :facebook, ENV["FACEBOOK_APP_ID"], ENV["FACEBOOK_APP_SECRET"],
705
+ scope: "email", callback_path: "/auth/facebook/callback"
706
+ end
707
+ ```
708
+ ```erb
709
+ <%= link_to "Login via Facebook", "/auth/facebook" %>
710
+ ```
711
+
712
+ Let's implement the OmniAuth callback endpoint on our Rodauth controller:
713
+
714
+ ```rb
715
+ # config/routes.rb
716
+ Rails.application.routes.draw do
717
+ # ...
718
+ get "/auth/:provider/callback", to: "rodauth#omniauth"
719
+ end
720
+ ```
721
+ ```rb
722
+ # app/controllres/rodauth_controller.rb
723
+ class RodauthController < ApplicationController
724
+ def omniauth
725
+ auth = request.env["omniauth.auth"]
726
+
727
+ # attempt to find existing identity directly
728
+ identity = AccountIdentity.find_by(provider: auth["provider"], uid: auth["uid"])
729
+
730
+ if identity
731
+ # update any external info changes
732
+ identity.update!(info: auth["info"])
733
+ # set account from identity
734
+ account = identity.account
735
+ end
736
+
737
+ # attempt to find an existing account by email
738
+ account ||= Account.find_by(email: auth["info"]["email"])
739
+
740
+ # disallow login if account is not verified
741
+ if account && account.status != rodauth.account_open_status_value
742
+ redirect_to rodauth.login_path, alert: rodauth.unverified_account_message
743
+ return
744
+ end
745
+
746
+ # create new account if it doesn't exist
747
+ unless account
748
+ account = Account.create!(email: auth["info"]["email"])
749
+ end
750
+
751
+ # create new identity if it doesn't exist
752
+ unless identity
753
+ account.identities.create!(provider: auth["provider"], uid: auth["uid"], info: auth["info"])
754
+ end
755
+
756
+ # login with Rodauth
757
+ rodauth.account_from_login(account.email)
758
+ rodauth.login("omniauth")
759
+ end
760
+ end
761
+ ```
762
+
531
763
  ## Configuring
532
764
 
533
765
  For the list of configuration methods provided by Rodauth, see the [feature
@@ -561,6 +793,37 @@ Rodauth::Rails.configure do |config|
561
793
  end
562
794
  ```
563
795
 
796
+ ## Custom extensions
797
+
798
+ When developing custom extensions for Rodauth inside your Rails project, it's
799
+ better to use plain modules (at least in the beginning), because Rodauth
800
+ feature API doesn't yet support Zeitwerk reloading well.
801
+
802
+ ```rb
803
+ # app/lib/rodauth_argon2.rb
804
+ module RodauthArgon2
805
+ def password_hash(password)
806
+ Argon2::Password.create(password, t_cost: password_hash_cost, m_cost: password_hash_cost)
807
+ end
808
+
809
+ def password_hash_match?(hash, password)
810
+ Argon2::Password.verify_password(password, hash)
811
+ end
812
+ end
813
+ ```
814
+ ```rb
815
+ # app/lib/rodauth_app.rb
816
+ class RodauthApp < Rodauth::Rails::App
817
+ configure do
818
+ # ...
819
+ auth_class_eval do
820
+ include RodauthArgon2
821
+ end
822
+ # ...
823
+ end
824
+ end
825
+ ```
826
+
564
827
  ## Testing
565
828
 
566
829
  If you're writing system tests, it's generally better to go through the actual
@@ -633,17 +896,15 @@ Rodauth method for creating database functions:
633
896
 
634
897
  ```rb
635
898
  # db/migrate/*_create_rodauth_database_functions.rb
899
+ require "rodauth/migrations"
900
+
636
901
  class CreateRodauthDatabaseFunctions < ActiveRecord::Migration
637
902
  def up
638
- # ...
639
903
  Rodauth.create_database_authentication_functions(DB)
640
- # ...
641
904
  end
642
905
 
643
906
  def down
644
- # ...
645
907
  Rodauth.drop_database_authentication_functions(DB)
646
- # ...
647
908
  end
648
909
  end
649
910
  ```
@@ -700,9 +961,7 @@ conduct](https://github.com/janko/rodauth-rails/blob/master/CODE_OF_CONDUCT.md).
700
961
 
701
962
  [Rodauth]: https://github.com/jeremyevans/rodauth
702
963
  [Sequel]: https://github.com/jeremyevans/sequel
703
- [rendering views outside of controllers]: https://blog.bigbinary.com/2016/01/08/rendering-views-outside-of-controllers-in-rails-5.html
704
964
  [feature documentation]: http://rodauth.jeremyevans.net/documentation.html
705
- [JWT feature]: http://rodauth.jeremyevans.net/rdoc/files/doc/jwt_rdoc.html
706
965
  [JWT gem]: https://github.com/jwt/ruby-jwt
707
966
  [Bootstrap]: https://getbootstrap.com/
708
967
  [Roda]: http://roda.jeremyevans.net/
@@ -711,3 +970,20 @@ conduct](https://github.com/janko/rodauth-rails/blob/master/CODE_OF_CONDUCT.md).
711
970
  [Rodauth migration]: http://rodauth.jeremyevans.net/rdoc/files/README_rdoc.html#label-Creating+tables
712
971
  [sequel-activerecord_connection]: https://github.com/janko/sequel-activerecord_connection
713
972
  [plugin options]: http://rodauth.jeremyevans.net/rdoc/files/README_rdoc.html#label-Plugin+Options
973
+ [hmac]: http://rodauth.jeremyevans.net/rdoc/files/README_rdoc.html#label-HMAC
974
+ [OmniAuth]: https://github.com/omniauth/omniauth
975
+ [otp]: http://rodauth.jeremyevans.net/rdoc/files/doc/otp_rdoc.html
976
+ [sms_codes]: http://rodauth.jeremyevans.net/rdoc/files/doc/sms_codes_rdoc.html
977
+ [recovery_codes]: http://rodauth.jeremyevans.net/rdoc/files/doc/recovery_codes_rdoc.html
978
+ [webauthn]: http://rodauth.jeremyevans.net/rdoc/files/doc/webauthn_rdoc.html
979
+ [jwt]: http://rodauth.jeremyevans.net/rdoc/files/doc/jwt_rdoc.html
980
+ [email_auth]: http://rodauth.jeremyevans.net/rdoc/files/doc/email_auth_rdoc.html
981
+ [audit_logging]: http://rodauth.jeremyevans.net/rdoc/files/doc/audit_logging_rdoc.html
982
+ [password protection]: https://github.com/jeremyevans/rodauth#label-Password+Hash+Access+Via+Database+Functions
983
+ [bruteforce tokens]: https://github.com/jeremyevans/rodauth#label-Tokens
984
+ [password_complexity]: http://rodauth.jeremyevans.net/rdoc/files/doc/password_complexity_rdoc.html
985
+ [disallow_password_reuse]: http://rodauth.jeremyevans.net/rdoc/files/doc/disallow_password_reuse_rdoc.html
986
+ [password_expiration]: http://rodauth.jeremyevans.net/rdoc/files/doc/password_expiration_rdoc.html
987
+ [session_expiration]: http://rodauth.jeremyevans.net/rdoc/files/doc/session_expiration_rdoc.html
988
+ [single_session]: http://rodauth.jeremyevans.net/rdoc/files/doc/single_session_rdoc.html
989
+ [account_expiration]: http://rodauth.jeremyevans.net/rdoc/files/doc/account_expiration_rdoc.html
@@ -13,6 +13,15 @@ module Rodauth
13
13
  source_root "#{__dir__}/templates"
14
14
  namespace "rodauth:install"
15
15
 
16
+ # The :api option is a Rails-recognized option that always
17
+ # defaults to false, so we make it use our provided default
18
+ # value instead.
19
+ def self.default_value_for_option(name, options)
20
+ name == :api ? options[:default] : super
21
+ end
22
+
23
+ class_option :api, type: :boolean, desc: "Generate JSON-only configuration"
24
+
16
25
  def create_rodauth_migration
17
26
  return unless defined?(ActiveRecord::Base)
18
27
 
@@ -35,8 +44,6 @@ module Rodauth
35
44
  end
36
45
 
37
46
  def create_rodauth_controller
38
- return if api_only?
39
-
40
47
  template "app/controllers/rodauth_controller.rb"
41
48
  end
42
49
 
@@ -77,9 +84,11 @@ module Rodauth
77
84
  end
78
85
 
79
86
  def api_only?
80
- return unless ::Rails.gem_version >= Gem::Version.new("5.0")
81
-
82
- ::Rails.application.config.api_only
87
+ if options.key?(:api)
88
+ options[:api]
89
+ elsif ::Rails.gem_version >= Gem::Version.new("5.0")
90
+ ::Rails.application.config.api_only
91
+ end
83
92
  end
84
93
 
85
94
  def migration_features
@@ -1,3 +1,4 @@
1
1
  class RodauthController < ApplicationController
2
- # used by Rodauth for rendering views and CSRF protection
2
+ # used by Rodauth for rendering views, CSRF protection, and running any
3
+ # registered action callbacks and rescue_from handlers
3
4
  end
@@ -15,11 +15,9 @@ class RodauthApp < Rodauth::Rails::App
15
15
  # Defaults to Rails `secret_key_base`, but you can use your own secret key.
16
16
  # hmac_secret "<%= SecureRandom.hex(64) %>"
17
17
 
18
- <% unless api_only? -%>
19
18
  # Specify the controller used for view rendering and CSRF verification.
20
19
  rails_controller { RodauthController }
21
20
 
22
- <% end -%>
23
21
  # Store account status in a text column.
24
22
  account_status_column :status
25
23
  account_unverified_status_value "unverified"
@@ -59,31 +57,27 @@ class RodauthApp < Rodauth::Rails::App
59
57
 
60
58
  # ==> Emails
61
59
  # Uncomment the lines below once you've imported mailer views.
62
- # send_reset_password_email do
63
- # mailer_send(:reset_password, email_to, reset_password_email_link)
60
+ # create_reset_password_email do
61
+ # RodauthMailer.reset_password(email_to, reset_password_email_link)
64
62
  # end
65
- # send_verify_account_email do
66
- # mailer_send(:verify_account, email_to, verify_account_email_link)
63
+ # create_verify_account_email do
64
+ # RodauthMailer.verify_account(email_to, verify_account_email_link)
67
65
  # end
68
- # send_verify_login_change_email do |login|
69
- # mailer_send(:verify_login_change, login, verify_login_change_old_login, verify_login_change_new_login, verify_login_change_email_link)
66
+ # create_verify_login_change_email do |login|
67
+ # RodauthMailer.verify_login_change(login, verify_login_change_old_login, verify_login_change_new_login, verify_login_change_email_link)
70
68
  # end
71
- # send_password_changed_email do
72
- # mailer_send(:password_changed, email_to)
69
+ # create_password_changed_email do
70
+ # RodauthMailer.password_changed(email_to)
73
71
  # end
74
- # # send_email_auth_email do
75
- # # mailer_send(:email_auth, email_to, email_auth_email_link)
72
+ # # create_email_auth_email do
73
+ # # RodauthMailer.email_auth(email_to, email_auth_email_link)
76
74
  # # end
77
- # # send_unlock_account_email do
78
- # # mailer_send(:unlock_account, email_to, unlock_account_email_link)
75
+ # # create_unlock_account_email do
76
+ # # RodauthMailer.unlock_account(email_to, unlock_account_email_link)
79
77
  # # end
80
- # auth_class_eval do
78
+ # send_email do |email|
81
79
  # # queue email delivery on the mailer after the transaction commits
82
- # def mailer_send(type, *args)
83
- # db.after_commit do
84
- # RodauthMailer.public_send(type, *args).deliver_later
85
- # end
86
- # end
80
+ # db.after_commit { email.deliver_later }
87
81
  # end
88
82
 
89
83
  # In the meantime you can tweak settings for emails created by Rodauth
@@ -9,14 +9,43 @@ module Rodauth
9
9
  # This allows the developer to avoid loading Rodauth at boot time.
10
10
  autoload :App, "rodauth/rails/app"
11
11
 
12
- def self.configure
13
- yield self
14
- end
15
-
16
12
  @app = nil
17
13
  @middleware = true
18
14
 
19
15
  class << self
16
+ def rodauth(name = nil)
17
+ url_options = ActionMailer::Base.default_url_options
18
+
19
+ scheme = url_options[:protocol] || "http"
20
+ port = url_options[:port]
21
+ port ||= Rack::Request::DEFAULT_PORTS[scheme] if Gem::Version.new(Rack.release) < Gem::Version.new("2.0")
22
+ host = url_options[:host]
23
+ host += ":#{port}" if port
24
+
25
+ rack_env = {
26
+ "HTTP_HOST" => host,
27
+ "rack.url_scheme" => scheme,
28
+ }
29
+
30
+ scope = app.new(rack_env)
31
+
32
+ scope.rodauth(name)
33
+ end
34
+
35
+ if ::Rails.gem_version >= Gem::Version.new("5.2")
36
+ def secret_key_base
37
+ ::Rails.application.secret_key_base
38
+ end
39
+ else
40
+ def secret_key_base
41
+ ::Rails.application.secrets.secret_key_base
42
+ end
43
+ end
44
+
45
+ def configure
46
+ yield self
47
+ end
48
+
20
49
  attr_writer :app
21
50
  attr_writer :middleware
22
51
 
@@ -1,10 +1,14 @@
1
1
  require "roda"
2
+ require "rodauth"
3
+ require "rodauth/rails/feature"
2
4
 
3
5
  module Rodauth
4
6
  module Rails
5
7
  # The superclass for creating a Rodauth middleware.
6
8
  class App < Roda
7
- plugin :middleware
9
+ require "rodauth/rails/app/middleware"
10
+ plugin Middleware
11
+
8
12
  plugin :hooks
9
13
  plugin :render, layout: false
10
14
 
@@ -18,6 +22,12 @@ module Rodauth
18
22
  # load the Rails integration
19
23
  enable :rails
20
24
 
25
+ if options[:json] == :only && ActionPack.version >= Gem::Version.new("5.0")
26
+ rails_controller { ActionController::API }
27
+ else
28
+ rails_controller { ActionController::Base }
29
+ end
30
+
21
31
  # database functions are more complex to set up, so disable them by default
22
32
  use_database_authentication_functions? false
23
33
 
@@ -25,7 +35,7 @@ module Rodauth
25
35
  set_deadline_values? true
26
36
 
27
37
  # use HMACs for additional security
28
- hmac_secret { ::Rails.application.secrets.secret_key_base }
38
+ hmac_secret { Rodauth::Rails.secret_key_base }
29
39
 
30
40
  # evaluate user configuration
31
41
  instance_exec(&block)
@@ -1,7 +1,7 @@
1
1
  module Rodauth
2
2
  module Rails
3
3
  class App
4
- # Sets up Rails' flash integration.
4
+ # Roda plugin that sets up Rails flash integration.
5
5
  module Flash
6
6
  def self.load_dependencies(app)
7
7
  app.plugin :hooks
@@ -0,0 +1,26 @@
1
+ module Rodauth
2
+ module Rails
3
+ class App
4
+ # Roda plugin that extends middleware plugin by propagating response headers.
5
+ module Middleware
6
+ def self.load_dependencies(app)
7
+ app.plugin :hooks
8
+ end
9
+
10
+ def self.configure(app)
11
+ app.after do
12
+ if response.empty? && response.headers.any?
13
+ env["rodauth.rails.headers"] = response.headers
14
+ end
15
+ end
16
+
17
+ app.plugin :middleware, handle_result: -> (env, res) do
18
+ if headers = env.delete("rodauth.rails.headers")
19
+ res[1] = headers.merge(res[1])
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -9,10 +9,11 @@ module Rodauth
9
9
  :rails_csrf_param,
10
10
  :rails_csrf_token,
11
11
  :rails_check_csrf!,
12
- :rails_controller_instance,
13
12
  :rails_controller,
14
13
  )
15
14
 
15
+ auth_cached_method :rails_controller_instance
16
+
16
17
  # Renders templates with layout. First tries to render a user-defined
17
18
  # template, otherwise falls back to Rodauth's template.
18
19
  def view(page, *)
@@ -25,7 +26,12 @@ module Rodauth
25
26
  def render(page)
26
27
  rails_render(partial: page.tr("-", "_"), layout: false) ||
27
28
  rails_render(action: page.tr("-", "_"), layout: false) ||
28
- super
29
+ super.html_safe
30
+ end
31
+
32
+ # Render Rails CSRF tags in Rodauth templates.
33
+ def csrf_tag(*)
34
+ rails_csrf_tag
29
35
  end
30
36
 
31
37
  # Verify Rails' authenticity token.
@@ -38,18 +44,77 @@ module Rodauth
38
44
  true
39
45
  end
40
46
 
41
- # Render Rails CSRF tags in Rodauth templates.
42
- def csrf_tag(*)
43
- rails_csrf_tag
44
- end
45
-
46
47
  # Default the flash error key to Rails' default :alert.
47
48
  def flash_error_key
48
49
  :alert
49
50
  end
50
51
 
52
+ # Evaluates the block in context of a Rodauth controller instance.
53
+ def rails_controller_eval(&block)
54
+ rails_controller_instance.instance_exec(&block)
55
+ end
56
+
57
+ def button(*)
58
+ super.html_safe
59
+ end
60
+
51
61
  private
52
62
 
63
+ # Runs controller callbacks and rescue handlers around Rodauth actions.
64
+ def _around_rodauth(&block)
65
+ result = nil
66
+
67
+ rails_controller_rescue do
68
+ rails_controller_callbacks do
69
+ result = catch(:halt) { super(&block) }
70
+ end
71
+ end
72
+
73
+ if rails_controller_instance.performed?
74
+ rails_controller_response
75
+ elsif result
76
+ result[1].merge!(rails_controller_instance.response.headers)
77
+ throw :halt, result
78
+ else
79
+ result
80
+ end
81
+ end
82
+
83
+ # Runs any #(before|around|after)_action controller callbacks.
84
+ def rails_controller_callbacks
85
+ # don't verify CSRF token as part of callbacks, Rodauth will do that
86
+ rails_controller_forgery_protection { false }
87
+
88
+ rails_controller_instance.run_callbacks(:process_action) do
89
+ # turn the setting back to default so that form tags generate CSRF tags
90
+ rails_controller_forgery_protection { rails_controller.allow_forgery_protection }
91
+
92
+ yield
93
+ end
94
+ end
95
+
96
+ # Runs any registered #rescue_from controller handlers.
97
+ def rails_controller_rescue
98
+ yield
99
+ rescue Exception => exception
100
+ rails_controller_instance.rescue_with_handler(exception) || raise
101
+
102
+ unless rails_controller_instance.performed?
103
+ raise Rodauth::Rails::Error, "rescue_from handler didn't write any response"
104
+ end
105
+ end
106
+
107
+ # Returns Roda response from controller response if set.
108
+ def rails_controller_response
109
+ controller_response = rails_controller_instance.response
110
+
111
+ response.status = controller_response.status
112
+ response.headers.merge! controller_response.headers
113
+ response.write controller_response.body
114
+
115
+ request.halt
116
+ end
117
+
53
118
  # Create emails with ActionMailer which uses configured delivery method.
54
119
  def create_email_to(to, subject, body)
55
120
  Mailer.create_email(to: to, from: email_from, subject: "#{email_subject_prefix}#{subject}", body: body)
@@ -62,13 +127,16 @@ module Rodauth
62
127
 
63
128
  # Calls the Rails renderer, returning nil if a template is missing.
64
129
  def rails_render(*args)
65
- return if only_json?
130
+ return if rails_api_controller?
66
131
 
67
- begin
68
- rails_controller_instance.render_to_string(*args)
69
- rescue ActionView::MissingTemplate
70
- nil
71
- end
132
+ rails_controller_instance.render_to_string(*args)
133
+ rescue ActionView::MissingTemplate
134
+ nil
135
+ end
136
+
137
+ # Calls the controller to verify the authenticity token.
138
+ def rails_check_csrf!
139
+ rails_controller_instance.send(:verify_authenticity_token)
72
140
  end
73
141
 
74
142
  # Hidden tag with Rails CSRF token inserted into Rodauth templates.
@@ -86,30 +154,37 @@ module Rodauth
86
154
  rails_controller_instance.send(:form_authenticity_token)
87
155
  end
88
156
 
89
- # Calls the controller to verify the authenticity token.
90
- def rails_check_csrf!
91
- rails_controller_instance.send(:verify_authenticity_token)
157
+ # allows/disables forgery protection
158
+ def rails_controller_forgery_protection(&value)
159
+ return if rails_api_controller?
160
+
161
+ rails_controller_instance.allow_forgery_protection = value.call
92
162
  end
93
163
 
94
164
  # Instances of the configured controller with current request's env hash.
95
- def rails_controller_instance
96
- request = ActionDispatch::Request.new(scope.env)
97
- instance = rails_controller.new
165
+ def _rails_controller_instance
166
+ controller = rails_controller.new
167
+ rails_request = ActionDispatch::Request.new(scope.env)
98
168
 
99
- if ActionPack.version >= Gem::Version.new("5.0")
100
- instance.set_request! request
101
- instance.set_response! rails_controller.make_response!(request)
102
- else
103
- instance.send(:set_response!, request)
104
- instance.instance_variable_set(:@_request, request)
105
- end
169
+ prepare_rails_controller(controller, rails_request)
170
+
171
+ controller
172
+ end
106
173
 
107
- instance
174
+ if ActionPack.version >= Gem::Version.new("5.0")
175
+ def prepare_rails_controller(controller, rails_request)
176
+ controller.set_request! rails_request
177
+ controller.set_response! rails_controller.make_response!(rails_request)
178
+ end
179
+ else
180
+ def prepare_rails_controller(controller, rails_request)
181
+ controller.send(:set_response!, rails_request)
182
+ controller.instance_variable_set(:@_request, rails_request)
183
+ end
108
184
  end
109
185
 
110
- # Controller class to use for rendering and CSRF protection.
111
- def rails_controller
112
- ActionController::Base
186
+ def rails_api_controller?
187
+ defined?(ActionController::API) && rails_controller <= ActionController::API
113
188
  end
114
189
 
115
190
  # ActionMailer subclass for correct email delivering.
@@ -3,19 +3,16 @@ namespace :rodauth do
3
3
  app = Rodauth::Rails.app
4
4
 
5
5
  puts "Routes handled by #{app}:"
6
- puts
7
6
 
8
- app.opts[:rodauths].each do |rodauth_name, rodauth_class|
9
- route_names = rodauth_class.routes
10
- .map { |handle_method| handle_method.to_s.sub(/\Ahandle_/, "") }
11
- .uniq
7
+ app.opts[:rodauths].each_key do |rodauth_name|
8
+ rodauth = Rodauth::Rails.rodauth(rodauth_name)
12
9
 
13
- rodauth = rodauth_class.allocate
10
+ routes = rodauth.class.routes.map do |handle_method|
11
+ path_method = "#{handle_method.to_s.sub(/\Ahandle_/, "")}_path"
14
12
 
15
- routes = route_names.map do |name|
16
13
  [
17
- rodauth.public_send(:"#{name}_path"),
18
- "rodauth#{rodauth_name && "(:#{rodauth_name})"}.#{name}_path",
14
+ rodauth.public_send(path_method),
15
+ "rodauth#{rodauth_name && "(:#{rodauth_name})"}.#{path_method}",
19
16
  ]
20
17
  end
21
18
 
@@ -25,8 +22,7 @@ namespace :rodauth do
25
22
  "#{path.ljust(padding)} #{code}"
26
23
  end
27
24
 
28
- puts " #{route_lines.join("\n ")}"
29
- puts
25
+ puts "\n #{route_lines.join("\n ")}" unless route_lines.empty?
30
26
  end
31
27
  end
32
28
  end
@@ -1,5 +1,5 @@
1
1
  module Rodauth
2
2
  module Rails
3
- VERSION = "0.5.0"
3
+ VERSION = "0.8.1"
4
4
  end
5
5
  end
@@ -17,8 +17,10 @@ Gem::Specification.new do |spec|
17
17
  spec.require_paths = ["lib"]
18
18
 
19
19
  spec.add_dependency "railties", ">= 4.2", "< 7"
20
- spec.add_dependency "rodauth", "~> 2.1"
20
+ spec.add_dependency "rodauth", "~> 2.7"
21
21
  spec.add_dependency "sequel-activerecord_connection", "~> 1.1"
22
22
  spec.add_dependency "tilt"
23
23
  spec.add_dependency "bcrypt"
24
+
25
+ spec.add_development_dependency "jwt"
24
26
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rodauth-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Janko Marohnić
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-11-16 00:00:00.000000000 Z
11
+ date: 2021-01-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
@@ -36,14 +36,14 @@ dependencies:
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: '2.1'
39
+ version: '2.7'
40
40
  type: :runtime
41
41
  prerelease: false
42
42
  version_requirements: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: '2.1'
46
+ version: '2.7'
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: sequel-activerecord_connection
49
49
  requirement: !ruby/object:Gem::Requirement
@@ -86,6 +86,20 @@ dependencies:
86
86
  - - ">="
87
87
  - !ruby/object:Gem::Version
88
88
  version: '0'
89
+ - !ruby/object:Gem::Dependency
90
+ name: jwt
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
89
103
  description: Provides Rails integration for Rodauth.
90
104
  email:
91
105
  - janko.marohnic@gmail.com
@@ -187,10 +201,10 @@ files:
187
201
  - lib/generators/rodauth/templates/db/migrate/create_rodauth.rb
188
202
  - lib/generators/rodauth/views_generator.rb
189
203
  - lib/rodauth-rails.rb
190
- - lib/rodauth/features/rails.rb
191
204
  - lib/rodauth/rails.rb
192
205
  - lib/rodauth/rails/app.rb
193
206
  - lib/rodauth/rails/app/flash.rb
207
+ - lib/rodauth/rails/app/middleware.rb
194
208
  - lib/rodauth/rails/controller_methods.rb
195
209
  - lib/rodauth/rails/feature.rb
196
210
  - lib/rodauth/rails/middleware.rb
@@ -1 +0,0 @@
1
- require "rodauth/rails/feature"