rodauth-rails 1.9.0 → 1.11.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8f73ab003a11054b2534802c0de9224c867b7a173d8d553888918fa792ee3da4
4
- data.tar.gz: 07136b1748df5970d5d263d5d9439596c3c2e083ccd88269ab7145a8cd73fefc
3
+ metadata.gz: 8f902ae05472454543304221221b9681c7dab3d231d6551c7e82b6c4c1570dc8
4
+ data.tar.gz: b3a1948fb603be978bddea73a44b8109f4e3c76e132b2da3a8e6aad1b36f3d14
5
5
  SHA512:
6
- metadata.gz: bc8c1a0ef2cd7d9c49f9fed9dd6423e92ec4112ddaa65b5a8fa63134f35d5fa9b5f2f1679c4d4d7f4d9f3cd5a71acf840d4d59ce2731b52d82914c200744eb03
7
- data.tar.gz: 41f707560dd3ce232f54c80bc83f2210d4667d715c5f25aab812382a54f4115cab03d3e8e23d1c8c8b1e5e572404c91b332b040b741648c2577499779c0bf6a1
6
+ metadata.gz: 8547335032c3e0851406932463cd62e2377c16db2145e0da6abb8a7f20745c20f9e8d54bd2b57599e335a4f9b37078cf7f1c17caa53b4dab60105c410a401469
7
+ data.tar.gz: dd84bd6d57a4e8e78a6412a7ac38befa7d75ef693a6788014d383d3800a4535af71fa24239f8b0521cecc7c598766f48b2034a016a2a15235cfa9f9d2dd52761
data/CHANGELOG.md CHANGED
@@ -1,3 +1,21 @@
1
+ ## 1.11.0 (2023-08-21)
2
+
3
+ * Exclude WebAuthn JS routes in `rodauth:routes`, since those stop being relevant with custom JS (@janko)
4
+
5
+ * Separate HTTP verbs with `|` symbol in `rodauth:routes` for consistency with `rails routes` (@janko)
6
+
7
+ * Include two factor manage & auth JSON POST routes in `rodauth:routes` task (@janko)
8
+
9
+ * Make `rodauth:routes` rake task appear in `rails -T` list (@janko)
10
+
11
+ * Accept plugin options in `Rodauth::Rails.lib` (@janko)
12
+
13
+ * Support skipping loading Roda `render` plugin by passing `render: false` (@janko)
14
+
15
+ ## 1.10.0 (2023-07-26)
16
+
17
+ * Add `Rodauth::Rails.lib` for easier usage of Rodauth as a library in Rails apps (@janko)
18
+
1
19
  ## 1.9.0 (2023-05-22)
2
20
 
3
21
  * Add support for webauthn_autofill feature to the views generator (@janko)
data/README.md CHANGED
@@ -24,6 +24,7 @@ Provides Rails integration for the [Rodauth] authentication framework.
24
24
  * [How to build an OIDC provider using rodauth-oauth on Rails](https://honeyryderchuck.gitlab.io/httpx/2021/03/15/oidc-provider-on-rails-using-rodauth-oauth.html)
25
25
  * [What It Took to Build a Rails Integration for Rodauth](https://janko.io/what-it-took-to-build-a-rails-integration-for-rodauth/)
26
26
  * [Social Login in Rails with Rodauth](https://janko.io/social-login-in-rails-with-rodauth/)
27
+ * [Passkey Authentication with Rodauth](https://janko.io/passkey-authentication-with-rodauth/)
27
28
 
28
29
  ## Why Rodauth?
29
30
 
@@ -133,18 +134,18 @@ $ rails rodauth:routes
133
134
  ```
134
135
  Routes handled by RodauthApp:
135
136
 
136
- GET/POST /login rodauth.login_path
137
- GET/POST /create-account rodauth.create_account_path
138
- GET/POST /verify-account-resend rodauth.verify_account_resend_path
139
- GET/POST /verify-account rodauth.verify_account_path
140
- GET/POST /change-password rodauth.change_password_path
141
- GET/POST /change-login rodauth.change_login_path
142
- GET/POST /logout rodauth.logout_path
143
- GET/POST /remember rodauth.remember_path
144
- GET/POST /reset-password-request rodauth.reset_password_request_path
145
- GET/POST /reset-password rodauth.reset_password_path
146
- GET/POST /verify-login-change rodauth.verify_login_change_path
147
- GET/POST /close-account rodauth.close_account_path
137
+ GET|POST /login rodauth.login_path
138
+ GET|POST /create-account rodauth.create_account_path
139
+ GET|POST /verify-account-resend rodauth.verify_account_resend_path
140
+ GET|POST /verify-account rodauth.verify_account_path
141
+ GET|POST /change-password rodauth.change_password_path
142
+ GET|POST /change-login rodauth.change_login_path
143
+ GET|POST /logout rodauth.logout_path
144
+ GET|POST /remember rodauth.remember_path
145
+ GET|POST /reset-password-request rodauth.reset_password_request_path
146
+ GET|POST /reset-password rodauth.reset_password_path
147
+ GET|POST /verify-login-change rodauth.verify_login_change_path
148
+ GET|POST /close-account rodauth.close_account_path
148
149
  ```
149
150
 
150
151
  Using this information, you can add some basic authentication links to your
@@ -185,25 +186,6 @@ current_account #=> #<Account id=123 email="user@example.com">
185
186
  current_account.email #=> "user@example.com"
186
187
  ```
187
188
 
188
- If the session is logged in, but the account doesn't exist in the database, the
189
- session will be reset.
190
-
191
- #### Custom account model
192
-
193
- The `#rails_account` method will try to infer the account model class from the
194
- configured accounts table name. However, if the model class cannot be inferred
195
- from the table name, you can configure it manually:
196
-
197
- ```rb
198
- # app/misc/rodauth_main.rb
199
- class RodauthMain < Rodauth::Rails::Auth
200
- configure do
201
- # ...
202
- rails_account_model { Authentication::Account } # custom model name
203
- end
204
- end
205
- ```
206
-
207
189
  ### Requiring authentication
208
190
 
209
191
  You'll likely want to require authentication for certain parts of your app,
@@ -219,8 +201,8 @@ class RodauthApp < Rodauth::Rails::App
219
201
  # ...
220
202
  r.rodauth # route rodauth requests
221
203
 
222
- # require authentication for /dashboard/* and /account/* routes
223
- if r.path.start_with?("/dashboard") || r.path.start_with?("/account")
204
+ # require authentication for /dashboard/* routes
205
+ if r.path.start_with?("/dashboard")
224
206
  rodauth.require_account # redirect to login page if not authenticated
225
207
  end
226
208
  end
@@ -245,12 +227,6 @@ class DashboardController < ApplicationController
245
227
  before_action :authenticate
246
228
  end
247
229
  ```
248
- ```rb
249
- # app/controllers/posts_controller.rb
250
- class PostsController < ApplicationController
251
- before_action :authenticate, except: [:index, :show]
252
- end
253
- ```
254
230
 
255
231
  #### Routing constraints
256
232
 
@@ -260,7 +236,7 @@ level. You can do this via the built-in `authenticated` routing constraint:
260
236
  ```rb
261
237
  # config/routes.rb
262
238
  Rails.application.routes.draw do
263
- constraints Rodauth::Rails.authenticated do
239
+ constraints Rodauth::Rails.authenticate do
264
240
  # ... authenticated routes ...
265
241
  end
266
242
  end
@@ -273,48 +249,89 @@ called with the Rodauth instance:
273
249
  # config/routes.rb
274
250
  Rails.application.routes.draw do
275
251
  # require multifactor authentication to be setup
276
- constraints Rodauth::Rails.authenticated { |rodauth| rodauth.uses_two_factor_authentication? } do
252
+ constraints Rodauth::Rails.authenticate { |rodauth| rodauth.uses_two_factor_authentication? } do
277
253
  # ...
278
254
  end
279
255
  end
280
256
  ```
281
257
 
282
- The current account can be retrieved via the `#rails_account` method:
258
+ You can specify a different Rodauth configuration by passing the configuration name:
283
259
 
284
260
  ```rb
285
261
  # config/routes.rb
286
262
  Rails.application.routes.draw do
287
- # require user to be admin
288
- constraints Rodauth::Rails.authenticated { |rodauth| rodauth.rails_account.admin? } do
263
+ constraints Rodauth::Rails.authenticate(:admin) do
289
264
  # ...
290
265
  end
291
266
  end
292
267
  ```
293
268
 
294
- You can specify the Rodauth configuration by passing the configuration name:
269
+ If you need something more custom, you can always create the routing constraint
270
+ manually:
295
271
 
296
272
  ```rb
297
273
  # config/routes.rb
298
274
  Rails.application.routes.draw do
299
- constraints Rodauth::Rails.authenticated(:admin) do
300
- # ...
275
+ constraints -> (r) { !r.env["rodauth"].logged_in? } do # or env["rodauth.admin"]
276
+ # routes when the user is not logged in
301
277
  end
302
278
  end
303
279
  ```
304
280
 
305
- If you need something more custom, you can always create the routing constraint
306
- manually:
281
+ ### Controller
282
+
283
+ Your Rodauth configuration is connected to a Rails controller (`RodauthController` by default), and
284
+ it automatically executes any callbacks and rescue handlers defined on it (or the parent controller)
285
+ around Rodauth endpoints.
307
286
 
308
287
  ```rb
309
- # config/routes.rb
310
- Rails.application.routes.draw do
311
- constraints -> (r) { !r.env["rodauth"].logged_in? } do # or "rodauth.admin"
312
- # routes when the user is not logged in
288
+ class RodauthController < ApplicationController
289
+ before_action :set_locale # executes before Rodauth endpoints
290
+ rescue_from("MyApp::SomeError") { |exception| ... } # rescues around Rodauth endpoints
291
+ end
292
+ ```
293
+
294
+ #### Calling controller methods
295
+
296
+ You can call any controller methods from your Rodauth configuration via `rails_controller_eval`:
297
+
298
+ ```rb
299
+ # app/controllers/application_controller.rb
300
+ class ApplicationController < ActionController::Base
301
+ private
302
+ def setup_tracking(account_id)
303
+ # ... some implementation ...
304
+ end
305
+ end
306
+ ```
307
+ ```rb
308
+ # app/misc/rodauth_main.rb
309
+ class RodauthMain < Rodauth::Rails::Auth
310
+ configure do
311
+ after_create_account do
312
+ rails_controller_eval { setup_tracking(account_id) }
313
+ end
314
+ end
315
+ end
316
+ ```
317
+
318
+ ### Rails URL helpers
319
+
320
+ Inside Rodauth configuration and the `route` block you can access Rails route
321
+ helpers through `#rails_routes`:
322
+
323
+ ```rb
324
+ # app/misc/rodauth_main.rb
325
+ class RodauthMain < Rodauth::Rails::Auth
326
+ configure do
327
+ login_redirect { rails_routes.activity_path }
328
+ change_password_redirect { rails_routes.profile_path }
329
+ change_login_redirect { rails_routes.profile_path }
313
330
  end
314
331
  end
315
332
  ```
316
333
 
317
- ### Views
334
+ ## Views
318
335
 
319
336
  The templates built into Rodauth are useful when getting started, but soon
320
337
  you'll want to start editing the markup. You can run the following command to
@@ -349,7 +366,7 @@ Use `--name` to generate views for a different Rodauth configuration:
349
366
  $ rails generate rodauth:views webauthn two_factor_base --name admin
350
367
  ```
351
368
 
352
- #### Page titles
369
+ ### Page titles
353
370
 
354
371
  The generated configuration sets `title_instance_variable` to make page titles
355
372
  available in your views via `@page_title` instance variable, which you can then
@@ -359,9 +376,7 @@ use in your layout:
359
376
  # app/misc/rodauth_main.rb
360
377
  class RodauthMain < Rodauth::Rails::Auth
361
378
  configure do
362
- # ...
363
379
  title_instance_variable :@page_title
364
- # ...
365
380
  end
366
381
  end
367
382
  ```
@@ -373,28 +388,11 @@ end
373
388
  <title><%= @page_title || "Default title" %></title>
374
389
  <!-- ... -->
375
390
  </head>
376
- <body>
377
- <!-- ... -->
378
- </body>
391
+ <!-- ... -->
379
392
  </html>
380
393
  ```
381
394
 
382
- If you're already setting page titles via `content_for`, you can use it in
383
- generated Rodauth views, giving it the result of the corresponding
384
- `*_page_title` method:
385
-
386
- ```erb
387
- <!-- app/views/rodauth/login.html.erb -->
388
- <%= content_for :page_title, rodauth.login_page_title %>
389
- <!-- ... -->
390
- ```
391
- ```erb
392
- <!-- app/views/rodauth/change_password.html.erb -->
393
- <%= content_for :page_title, rodauth.change_password_page_title %>
394
- <!-- ... -->
395
- ```
396
-
397
- #### Layout
395
+ ### Layout
398
396
 
399
397
  To use different layouts for different Rodauth views, you can compare the
400
398
  request path in the layout method:
@@ -422,7 +420,7 @@ class RodauthController < ApplicationController
422
420
  end
423
421
  ```
424
422
 
425
- #### Turbo
423
+ ### Turbo
426
424
 
427
425
  [Turbo] has been disabled by default on all built-in and generated view
428
426
  templates, because some Rodauth actions (multi-phase login, adding recovery
@@ -431,7 +429,7 @@ codes) aren't Turbo-compatible, as they return 200 responses on POST requests.
431
429
  That being said, most of Rodauth *is* Turbo-compatible, so feel free to enable
432
430
  Turbo for actions where you want to use it.
433
431
 
434
- ### Mailer
432
+ ## Mailer
435
433
 
436
434
  The install generator will create `RodauthMailer` with default email templates,
437
435
  and configure Rodauth features that send emails as part of the authentication
@@ -440,73 +438,45 @@ flow to use it.
440
438
  ```rb
441
439
  # app/mailers/rodauth_mailer.rb
442
440
  class RodauthMailer < ApplicationMailer
443
- def verify_account(account_id, key)
444
- # ...
445
- end
446
- def reset_password(account_id, key)
447
- # ...
448
- end
449
- def verify_login_change(account_id, old_login, new_login, key)
450
- # ...
451
- end
452
- def password_changed(account_id)
453
- # ...
454
- end
455
- # def email_auth(account_id, key)
456
- # ...
457
- # end
458
- # def unlock_account(account_id, key)
459
- # ...
460
- # end
441
+ def verify_account(account_id, key) ... end
442
+ def reset_password(account_id, key) ... end
443
+ def verify_login_change(account_id, key) ... end
444
+ def password_changed(account_id) ... end
445
+ # def email_auth(account_id, key) ... end
446
+ # def unlock_account(account_id, key) ... end
461
447
  end
462
448
  ```
463
449
  ```rb
464
450
  # app/misc/rodauth_main.rb
465
451
  class RodauthMain < Rodauth::Rails::Auth
466
452
  configure do
467
- # ...
468
- create_reset_password_email do
469
- RodauthMailer.reset_password(account_id, reset_password_key_value)
470
- end
471
- create_verify_account_email do
472
- RodauthMailer.verify_account(account_id, verify_account_key_value)
473
- end
474
- create_verify_login_change_email do |_login|
475
- RodauthMailer.verify_login_change(account_id, verify_login_change_old_login, verify_login_change_new_login, verify_login_change_key_value)
476
- end
477
- create_password_changed_email do
478
- RodauthMailer.password_changed(account_id)
479
- end
480
- # create_email_auth_email do
481
- # RodauthMailer.email_auth(account_id, email_auth_key_value)
482
- # end
483
- # create_unlock_account_email do
484
- # RodauthMailer.unlock_account(account_id, unlock_account_key_value)
485
- # end
453
+ create_reset_password_email { RodauthMailer.reset_password(account_id, reset_password_key_value) }
454
+ create_verify_account_email { RodauthMailer.verify_account(account_id, verify_account_key_value) }
455
+ create_verify_login_change_email { |_login| RodauthMailer.verify_login_change(account_id, verify_login_change_key_value) }
456
+ create_password_changed_email { RodauthMailer.password_changed(account_id) }
457
+ # create_email_auth_email { RodauthMailer.email_auth(account_id, email_auth_key_value) }
458
+ # create_unlock_account_email { RodauthMailer.unlock_account(account_id, unlock_account_key_value) }
486
459
  send_email do |email|
487
460
  # queue email delivery on the mailer after the transaction commits
488
461
  db.after_commit { email.deliver_later }
489
462
  end
490
- # ...
491
463
  end
492
464
  end
493
465
  ```
494
466
 
495
467
  This configuration calls `#deliver_later`, which uses Active Job to deliver
496
- emails in a background job. It's generally recommended to send emails
497
- asynchronously for better request throughput and the ability to retry
498
- deliveries. However, if you want to send emails synchronously, you can modify
499
- the configuration to call `#deliver_now` instead.
468
+ emails in a background job. If you want to send emails synchronously, you can
469
+ modify the configuration to call `#deliver_now` instead.
500
470
 
501
471
  If you're using a background processing library without an Active Job adapter,
502
472
  or a 3rd-party service for sending transactional emails, see [this wiki
503
473
  page][custom mailer worker] on how to set it up.
504
474
 
505
- ### Migrations
475
+ ## Migrations
506
476
 
507
477
  The install generator will create a migration for tables used by the Rodauth
508
478
  features enabled by default. For any additional features, you can use the
509
- migration generator for creating the required tables:
479
+ migration generator to create the required tables:
510
480
 
511
481
  ```sh
512
482
  $ rails generate rodauth:migration otp sms_codes recovery_codes
@@ -522,10 +492,10 @@ class CreateRodauthOtpSmsCodesRecoveryCodes < ActiveRecord::Migration
522
492
  end
523
493
  ```
524
494
 
525
- #### Table prefix
495
+ ### Table prefix
526
496
 
527
497
  If you're storing account records in a table other than `accounts`, you'll want
528
- to specify the correct table prefix when generating new migrations:
498
+ to specify the appropriate table prefix when generating new migrations:
529
499
 
530
500
  ```sh
531
501
  $ rails generate rodauth:migration base active_sessions --prefix user
@@ -546,7 +516,7 @@ class CreateRodauthUserBaseActiveSessions < ActiveRecord::Migration
546
516
  end
547
517
  ```
548
518
 
549
- #### Custom migration name
519
+ ### Custom migration name
550
520
 
551
521
  You can change the default migration name:
552
522
 
@@ -570,37 +540,23 @@ tables used by enabled authentication features.
570
540
 
571
541
  ```rb
572
542
  class Account < ActiveRecord::Base # Sequel::Model
573
- include Rodauth::Rails.model # or `Rodauth::Rails.model(:admin)`
543
+ include Rodauth::Rails.model # or Rodauth::Rails.model(:admin)
574
544
  end
575
545
  ```
576
-
577
- The password attribute can be used to set or clear the password hash. It
578
- handles both storing the password hash in a column on the accounts table, or in
579
- a separate table.
580
-
581
546
  ```rb
547
+ # setting password hash
582
548
  account = Account.create!(email: "user@example.com", password: "secret123")
583
-
584
- # when password hash is stored in a column on the accounts table
585
549
  account.password_hash #=> "$2a$12$k/Ub1I2iomi84RacqY89Hu4.M0vK7klRnRtzorDyvOkVI.hKhkNw."
586
550
 
587
- # when password hash is stored in a separate table
588
- account.password_hash #=> #<Account::PasswordHash...> (record from `account_password_hashes` table)
589
- account.password_hash.password_hash #=> "$2a$12$k/Ub1..." (inaccessible when using database authentication functions)
590
-
591
- account.password = nil # clears password hash
551
+ # clearing password hash
552
+ account.password = nil
592
553
  account.password_hash #=> nil
593
- ```
594
-
595
- The associations are defined for tables used by enabled authentication features:
596
554
 
597
- ```rb
555
+ # associations
598
556
  account.remember_key #=> #<Account::RememberKey> (record from `account_remember_keys` table)
599
557
  account.active_session_keys #=> [#<Account::ActiveSessionKey>,...] (records from `account_active_session_keys` table)
600
558
  ```
601
559
 
602
- See the [rodauth-model] documentation for more details.
603
-
604
560
  ## Multiple configurations
605
561
 
606
562
  If you need to handle multiple types of accounts that require different
@@ -646,43 +602,9 @@ Then in your application you can reference the secondary Rodauth instance:
646
602
  rodauth(:admin).login_path #=> "/admin/login"
647
603
  ```
648
604
 
649
- *NOTE: You'll likely want to save the information of which account belongs to which
650
- configuration to the database. See [this guide][account types] on how you can do
651
- that.*
652
-
653
- ### Sharing configuration
654
-
655
- If there are common settings that you want to share between Rodauth
656
- configurations, you can do so via inheritance:
657
-
658
- ```rb
659
- # app/misc/rodauth_base.rb
660
- class RodauthBase < Rodauth::Rails::Auth
661
- # common settings that are shared between multiple configurations
662
- configure do
663
- enable :login, :logout
664
- login_return_to_requested_location? true
665
- logout_redirect "/"
666
- # ...
667
- end
668
- end
669
- ```
670
- ```rb
671
- # app/misc/rodauth_main.rb
672
- class RodauthMain < RodauthBase # inherit common settings
673
- configure do
674
- # ... customize main ...
675
- end
676
- end
677
- ```
678
- ```rb
679
- # app/misc/rodauth_admin.rb
680
- class RodauthAdmin < RodauthBase # inherit common settings
681
- configure do
682
- # ... customize admin ...
683
- end
684
- end
685
- ```
605
+ You'll likely want to save the information of which account belongs to which
606
+ configuration to the database, see [this guide][account types] on how you can do
607
+ that. Note that you can also [share configuration via inheritance][inheritance].
686
608
 
687
609
  ## Outside of a request
688
610
 
@@ -769,6 +691,44 @@ Rodauth::Rails.rodauth(session: { two_factor_auth_setup: true })
769
691
  Rodauth::Rails.rodauth(:admin, params: { "param" => "value" })
770
692
  ```
771
693
 
694
+ ### Using as a library
695
+
696
+ Rodauth offers a `Rodauth.lib` method for configuring Rodauth so that it can be used as a library, instead of routing requests (see [internal_request] feature). This gem provides a `Rodauth::Rails.lib` counterpart that does the same but with Rails integration:
697
+
698
+ ```rb
699
+ # app/misc/rodauth_main.rb
700
+ require "rodauth/rails"
701
+ require "sequel/core"
702
+
703
+ RodauthMain = Rodauth::Rails.lib do
704
+ enable :create_account, :login, :close_account
705
+ db Sequel.postgres(extensions: :activerecord_connection, keep_reference: false)
706
+ # ...
707
+ end
708
+ ```
709
+ ```rb
710
+ RodauthMain.create_account(login: "email@example.com", password: "secret123")
711
+ RodauthMain.login(login: "email@example.com", password: "secret123")
712
+ RodauthMain.close_account(account_login: "email@example.com")
713
+ ```
714
+
715
+ Note that you'll want to skip requiring `rodauth-rails` on Rails boot, to avoid it automatically inserting the Rodauth middleware, and remove some unnecessary files generated by the install generator.
716
+
717
+ ```rb
718
+ # Gemfile
719
+ gem "rodauth-rails", require: false
720
+ ```
721
+ ```sh
722
+ $ rm config/initializers/rodauth.rb app/misc/rodauth_app.rb app/controllers/rodauth_controller.rb
723
+ ```
724
+
725
+ The `Rodauth::Rails.lib` call will forward any Rodauth [plugin options] passed to it:
726
+
727
+ ```rb
728
+ # skips loading Roda render plugin and Tilt gem (used for rendering built-in templates)
729
+ Rodauth::Rails.lib(render: false) { ... }
730
+ ```
731
+
772
732
  ## Testing
773
733
 
774
734
  For system and integration tests, which run the whole middleware stack,
@@ -788,13 +748,13 @@ end
788
748
  ```
789
749
 
790
750
  One can write `ActionDispatch::IntegrationTest` test helpers for `login` and
791
- `logout` by making requests to the rodauth endpoints
751
+ `logout` by making requests to the Rodauth endpoints:
792
752
 
793
753
  ```rb
794
754
  # test/controllers/articles_controller_test.rb
795
755
  class ArticlesControllerTest < ActionDispatch::IntegrationTest
796
- def login(login, password)
797
- post "/login", params: { login: login, password: password }
756
+ def login(email, password)
757
+ post "/login", params: { email: email, password: password }
798
758
  assert_redirected_to "/"
799
759
  end
800
760
 
@@ -846,6 +806,15 @@ methods:
846
806
  | `rails_controller` | Controller class to use for rendering and CSRF protection. |
847
807
  | `rails_account_model` | Model class connected with the accounts table. |
848
808
 
809
+ ```rb
810
+ class RodauthMain < Rodauth::Rails::Auth
811
+ configure do
812
+ rails_controller { Authentication::RodauthController }
813
+ rails_account_model { Authentication::Account }
814
+ end
815
+ end
816
+ ```
817
+
849
818
  For the list of configuration methods provided by Rodauth, see the [feature
850
819
  documentation].
851
820
 
@@ -875,53 +844,10 @@ end
875
844
  rodauth.admin? #=> true
876
845
  ```
877
846
 
878
- ### Rails URL helpers
879
-
880
- Inside Rodauth configuration and the `route` block you can access Rails route
881
- helpers through `#rails_routes`:
882
-
883
- ```rb
884
- # app/misc/rodauth_main.rb
885
- class RodauthMain < Rodauth::Rails::Auth
886
- configure do
887
- login_redirect { rails_routes.activity_path }
888
- change_password_redirect { rails_routes.profile_path }
889
- change_login_redirect { rails_routes.profile_path }
890
- end
891
- end
892
- ```
893
-
894
- ### Calling controller methods
895
-
896
- When using Rodauth before/after hooks or generally overriding your Rodauth
897
- configuration, in some cases you might want to call methods defined on your
898
- controllers. You can do so with `rails_controller_eval`, for example:
899
-
900
- ```rb
901
- # app/controllers/application_controller.rb
902
- class ApplicationController < ActionController::Base
903
- private
904
- def setup_tracking(account_id)
905
- # ... some implementation ...
906
- end
907
- end
908
- ```
909
- ```rb
910
- # app/misc/rodauth_main.rb
911
- class RodauthMain < Rodauth::Rails::Auth
912
- configure do
913
- after_create_account do
914
- rails_controller_eval { setup_tracking(account_id) }
915
- end
916
- end
917
- end
918
- ```
919
-
920
847
  ### Single-file configuration
921
848
 
922
- If you would prefer to have all Rodauth logic contained inside a single file,
923
- you call `Rodauth::Rails::App.configure` with a block, which will create an
924
- anonymous auth class.
849
+ If you would prefer, you can have all your Rodauth logic contained inside the
850
+ Rodauth app class:
925
851
 
926
852
  ```rb
927
853
  # app/misc/rodauth_app.rb
@@ -944,6 +870,19 @@ class RodauthApp < Rodauth::Rails::App
944
870
  end
945
871
  ```
946
872
 
873
+ ### Manually inserting middleware
874
+
875
+ You can choose to insert the Rodauth middleware somewhere earlier than
876
+ in front of the Rails router:
877
+
878
+ ```rb
879
+ Rodauth::Rails.configure do |config|
880
+ config.middleware = false # disable auto-insertion
881
+ end
882
+
883
+ Rails.application.config.middleware.insert_before AnotherMiddleware, Rodauth::Rails::Middleware
884
+ ```
885
+
947
886
  ## How it works
948
887
 
949
888
  ### Rack middleware
@@ -958,16 +897,6 @@ $ rails middleware
958
897
  # run MyApp::Application.routes
959
898
  ```
960
899
 
961
- It can be inserted at any point in the middleware stack:
962
-
963
- ```rb
964
- Rodauth::Rails.configure do |config|
965
- config.middleware = false # disable auto-insertion
966
- end
967
-
968
- Rails.application.config.middleware.insert_before AnotherMiddleware, Rodauth::Rails::Middleware
969
- ```
970
-
971
900
  The middleware retrieves the Rodauth app via `Rodauth::Rails.app`, which is
972
901
  specified as a string to keep the class autoloadable and reloadable in
973
902
  development.
@@ -1010,7 +939,7 @@ class RodauthApp < Rodauth::Rails::App
1010
939
  configure(:admin) { ... }
1011
940
 
1012
941
  # plugin options
1013
- configure(RodauthMain, json: :only)
942
+ configure(RodauthMain, json: :only, render: false)
1014
943
  end
1015
944
  ```
1016
945
 
@@ -1239,3 +1168,4 @@ conduct](CODE_OF_CONDUCT.md).
1239
1168
  [Turbo]: https://turbo.hotwired.dev/
1240
1169
  [rodauth-model]: https://github.com/janko/rodauth-model
1241
1170
  [JSON API]: https://github.com/janko/rodauth-rails/wiki/JSON-API
1171
+ [inheritance]: http://rodauth.jeremyevans.net/rdoc/files/doc/guides/share_configuration_rdoc.html
@@ -18,16 +18,17 @@ module Rodauth
18
18
  end
19
19
 
20
20
  plugin :hooks
21
- plugin :render, layout: false
22
21
  plugin :pass
23
22
 
24
23
  def self.configure(*args, **options, &block)
25
24
  auth_class = args.shift if args[0].is_a?(Class)
26
- name = args.shift if args[0].is_a?(Symbol)
25
+ auth_class ||= Class.new(Rodauth::Rails::Auth)
26
+ name = args.shift if args[0].is_a?(Symbol)
27
27
 
28
28
  fail ArgumentError, "need to pass optional Rodauth::Auth subclass and optional configuration name" if args.any?
29
29
 
30
- auth_class ||= Class.new(Rodauth::Rails::Auth)
30
+ # we'll render Rodauth's built-in view templates within Rails layouts
31
+ plugin :render, layout: false unless options[:render] == false
31
32
 
32
33
  plugin :rodauth, auth_class: auth_class, name: name, csrf: false, flash: false, json: true, **options, &block
33
34
 
@@ -0,0 +1,70 @@
1
+ module Rodauth
2
+ module Rails
3
+ module Tasks
4
+ class Routes
5
+ IGNORE = [:webauthn_setup_js, :webauthn_auth_js, :webauthn_autofill_js]
6
+ JSON_POST = [:two_factor_manage, :two_factor_auth]
7
+
8
+ attr_reader :auth_class
9
+
10
+ def initialize(auth_class)
11
+ @auth_class = auth_class
12
+ end
13
+
14
+ def call
15
+ routes = auth_class.route_hash.map do |path, handle_method|
16
+ route_name = handle_method.to_s.sub(/\Ahandle_/, "").to_sym
17
+ next if IGNORE.include?(route_name)
18
+ verbs = route_verbs(route_name)
19
+
20
+ [
21
+ verbs.join("|"),
22
+ "#{rodauth.prefix}#{path}",
23
+ "rodauth#{configuration_name && "(:#{configuration_name})"}.#{route_name}_path",
24
+ ]
25
+ end
26
+
27
+ routes.compact!
28
+ padding = routes.transpose.map { |string| string.map(&:length).max }
29
+
30
+ output_lines = routes.map do |columns|
31
+ [columns[0].ljust(padding[0]), columns[1].ljust(padding[1]), columns[2]].join(" ")
32
+ end
33
+
34
+ puts "\n #{output_lines.join("\n ")}"
35
+ end
36
+
37
+ private
38
+
39
+ def route_verbs(route_name)
40
+ file_path, start_line = rodauth.method(:"_handle_#{route_name}").source_location
41
+ lines = File.foreach(file_path).to_a
42
+ indentation = lines[start_line - 1][/^\s+/]
43
+ verbs = []
44
+
45
+ lines[start_line..-1].each do |code|
46
+ verbs << :GET if code.include?("r.get") && !rodauth.only_json?
47
+ verbs << :POST if code.include?("r.post")
48
+ break if code.start_with?("#{indentation}end")
49
+ end
50
+
51
+ verbs << :POST if rodauth.features.include?(:json) && JSON_POST.include?(route_name)
52
+ verbs
53
+ end
54
+
55
+ def rodauth
56
+ auth_class.new(scope)
57
+ end
58
+
59
+ def scope
60
+ auth_class.roda_class.new({})
61
+ end
62
+
63
+ def configuration_name
64
+ auth_class.configuration_name
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+
@@ -1,42 +1,12 @@
1
+ require "rodauth/rails/tasks/routes"
2
+
1
3
  namespace :rodauth do
4
+ desc "Lists endpoints that will be routed by your Rodauth app"
2
5
  task routes: :environment do
3
- app = Rodauth::Rails.app
4
-
5
- puts "Routes handled by #{app}:"
6
-
7
- app.opts[:rodauths].each do |configuration_name, auth_class|
8
- rodauth = auth_class.allocate
9
- only_json = rodauth.method(:only_json?).owner != Rodauth::Base && rodauth.only_json?
10
-
11
- routes = auth_class.route_hash.map do |path, handle_method|
12
- file_path, start_line = rodauth.method(:"_#{handle_method}").source_location
13
- lines = File.foreach(file_path).to_a
14
- indentation = lines[start_line - 1][/^\s+/]
15
- verbs = []
16
-
17
- lines[start_line..-1].each do |code|
18
- verbs << :GET if code.include?("r.get") && !only_json
19
- verbs << :POST if code.include?("r.post")
20
- break if code.start_with?("#{indentation}end")
21
- end
22
-
23
- path_method = "#{handle_method.to_s.sub(/\Ahandle_/, "")}_path"
24
-
25
- [
26
- verbs.join("/"),
27
- "#{rodauth.prefix}#{path}",
28
- "rodauth#{configuration_name && "(:#{configuration_name})"}.#{path_method}",
29
- ]
30
- end
31
-
32
- verbs_padding = routes.map { |verbs, _, _| verbs.length }.max
33
- path_padding = routes.map { |_, path, _| path.length }.max
34
-
35
- route_lines = routes.map do |verbs, path, code|
36
- "#{verbs.ljust(verbs_padding)} #{path.ljust(path_padding)} #{code}"
37
- end
6
+ puts "Routes handled by #{Rodauth::Rails.app}:"
38
7
 
39
- puts "\n #{route_lines.join("\n ")}" unless route_lines.empty?
8
+ Rodauth::Rails.app.opts[:rodauths].each_value do |auth_class|
9
+ Rodauth::Rails::Tasks::Routes.new(auth_class).call
40
10
  end
41
11
  end
42
12
  end
@@ -1,5 +1,5 @@
1
1
  module Rodauth
2
2
  module Rails
3
- VERSION = "1.9.0"
3
+ VERSION = "1.11.0"
4
4
  end
5
5
  end
data/lib/rodauth/rails.rb CHANGED
@@ -16,6 +16,16 @@ module Rodauth
16
16
  @middleware = true
17
17
 
18
18
  class << self
19
+ def lib(**options, &block)
20
+ c = Class.new(Rodauth::Rails::App)
21
+ c.configure(json: false, **options) do
22
+ enable :internal_request
23
+ instance_exec(&block)
24
+ end
25
+ c.freeze
26
+ c.rodauth
27
+ end
28
+
19
29
  def rodauth(name = nil, account: nil, **options)
20
30
  auth_class = app.rodauth!(name)
21
31
 
@@ -47,8 +57,17 @@ module Rodauth
47
57
  Rodauth::Model.new(app.rodauth!(name), **options)
48
58
  end
49
59
 
50
- # routing constraint that requires authentication
60
+ # Routing constraint that requires authenticated account.
61
+ def authenticate(name = nil, &condition)
62
+ lambda do |request|
63
+ rodauth = request.env.fetch ["rodauth", *name].join(".")
64
+ rodauth.require_account
65
+ condition.nil? || condition.call(rodauth)
66
+ end
67
+ end
68
+
51
69
  def authenticated(name = nil, &condition)
70
+ warn "Rodauth::Rails.authenticated has been deprecated in favor of Rodauth::Rails.authenticate, which additionally requires existence of the account record."
52
71
  lambda do |request|
53
72
  rodauth = request.env.fetch ["rodauth", *name].join(".")
54
73
  rodauth.require_authentication
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: 1.9.0
4
+ version: 1.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Janko Marohnić
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-05-22 00:00:00.000000000 Z
11
+ date: 2023-08-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
@@ -328,6 +328,7 @@ files:
328
328
  - lib/rodauth/rails/model.rb
329
329
  - lib/rodauth/rails/railtie.rb
330
330
  - lib/rodauth/rails/tasks.rake
331
+ - lib/rodauth/rails/tasks/routes.rb
331
332
  - lib/rodauth/rails/test.rb
332
333
  - lib/rodauth/rails/test/controller.rb
333
334
  - lib/rodauth/rails/version.rb
@@ -351,7 +352,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
351
352
  - !ruby/object:Gem::Version
352
353
  version: '0'
353
354
  requirements: []
354
- rubygems_version: 3.4.12
355
+ rubygems_version: 3.4.10
355
356
  signing_key:
356
357
  specification_version: 4
357
358
  summary: Provides Rails integration for Rodauth.