rodauth-rails 1.10.0 → 1.12.0
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 +28 -0
- data/README.md +115 -491
- data/lib/generators/rodauth/templates/app/misc/rodauth_main.rb.tt +3 -3
- data/lib/generators/rodauth/templates/app/models/account.rb.tt +2 -2
- data/lib/generators/rodauth/views_generator.rb +1 -0
- data/lib/rodauth/rails/app.rb +10 -5
- data/lib/rodauth/rails/feature/base.rb +1 -1
- data/lib/rodauth/rails/feature/render.rb +6 -0
- data/lib/rodauth/rails/tasks/routes.rb +70 -0
- data/lib/rodauth/rails/tasks.rake +6 -36
- data/lib/rodauth/rails/version.rb +1 -1
- data/lib/rodauth/rails.rb +12 -3
- data/rodauth-rails.gemspec +1 -1
- metadata +6 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1dac2131f831b908d4bfd7a3367b310bd04b4141202b16f5e98d80e769972e32
|
4
|
+
data.tar.gz: 1742fb2bb8fb16c221a4f09d5a5f53bdc898475383f91311100571e4902c8700
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 53dc0c219dc640431b553c8e843cc728ee1aff0c57a759dfebb0254a9e885a193ab1c3dff8d6fba6ba8423abdc49e9d6e38c14125a48a82954c42942b4a16838
|
7
|
+
data.tar.gz: 07ea61d890bb27ae8cbaed4c203366ddb07c152b5f76272e2acec9865e1c7ec72472aef8b20e2a112ad831ab3bc69602f443fc80ffcb0f6b1754f1b3d317cff4
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,31 @@
|
|
1
|
+
## 1.12.0 (2023-10-20)
|
2
|
+
|
3
|
+
* Allow generating view template for `confirm_password` feature (igor-alexandrov)
|
4
|
+
|
5
|
+
* Forward all requests unhandled by the Rodauth app to the Rails router (@janko)
|
6
|
+
|
7
|
+
* Use `Rodauth::Model()` directly for including in generated account model (@janko)
|
8
|
+
|
9
|
+
* Set `{jwt,argon2}_secret` to `hmac_secret` on `rodauth:install` with `--{jwt,argon2}` (@janko)
|
10
|
+
|
11
|
+
* Expose `#turbo_stream` method in `Rodauth::Rails::Auth` when using turbo-rails gem (@janko)
|
12
|
+
|
13
|
+
* Add `#rails_cookies` method for accessing the Action Dispatch cookie jar (@janko)
|
14
|
+
|
15
|
+
## 1.11.0 (2023-08-21)
|
16
|
+
|
17
|
+
* Exclude WebAuthn JS routes in `rodauth:routes`, since those stop being relevant with custom JS (@janko)
|
18
|
+
|
19
|
+
* Separate HTTP verbs with `|` symbol in `rodauth:routes` for consistency with `rails routes` (@janko)
|
20
|
+
|
21
|
+
* Include two factor manage & auth JSON POST routes in `rodauth:routes` task (@janko)
|
22
|
+
|
23
|
+
* Make `rodauth:routes` rake task appear in `rails -T` list (@janko)
|
24
|
+
|
25
|
+
* Accept plugin options in `Rodauth::Rails.lib` (@janko)
|
26
|
+
|
27
|
+
* Support skipping loading Roda `render` plugin by passing `render: false` (@janko)
|
28
|
+
|
1
29
|
## 1.10.0 (2023-07-26)
|
2
30
|
|
3
31
|
* Add `Rodauth::Rails.lib` for easier usage of Rodauth as a library in Rails apps (@janko)
|
data/README.md
CHANGED
@@ -15,6 +15,7 @@ Provides Rails integration for the [Rodauth] authentication framework.
|
|
15
15
|
|
16
16
|
* [Rails Authentication with Rodauth](https://www.youtube.com/watch?v=2hDpNikacf0)
|
17
17
|
* [Multifactor Authentication with Rodauth](https://www.youtube.com/watch?v=9ON-kgXpz2A&list=PLkGQXZLACDTGKsaRWstkHQdm2CUmT3SZ-) ([TOTP](https://youtu.be/9ON-kgXpz2A), [Recovery Codes](https://youtu.be/lkFCcE1Q5-w))
|
18
|
+
* [Add Admin Accounts](https://www.youtube.com/watch?v=N6z7AtKSpNI)
|
18
19
|
|
19
20
|
📚 Articles:
|
20
21
|
|
@@ -44,11 +45,7 @@ of the advantages that stand out for me:
|
|
44
45
|
|
45
46
|
One common concern for people coming from other Rails authentication frameworks
|
46
47
|
is the fact that Rodauth uses [Sequel] for database interaction instead of
|
47
|
-
Active Record.
|
48
|
-
supporting complex SQL expressions, database-agnostic date arithmetic, SQL
|
49
|
-
function calls and more, all without having to drop down to raw SQL.
|
50
|
-
|
51
|
-
For Rails apps using Active Record, rodauth-rails configures Sequel to [reuse
|
48
|
+
Active Record. For Rails apps using Active Record, rodauth-rails configures Sequel to [reuse
|
52
49
|
Active Record's database connection][sequel-activerecord_connection]. This
|
53
50
|
makes it run smoothly alongside Active Record, even allowing calling Active
|
54
51
|
Record code from within Rodauth configuration. So, for all intents and
|
@@ -68,7 +65,28 @@ Next, run the install generator:
|
|
68
65
|
$ rails generate rodauth:install
|
69
66
|
```
|
70
67
|
|
71
|
-
This will
|
68
|
+
This generator will create a Rodauth app and configuration with common
|
69
|
+
authentication features enabled, a database migration with tables required by
|
70
|
+
those features, a mailer with default templates, and a few other files.
|
71
|
+
|
72
|
+
Feel free to remove any features you don't need, along with their corresponding
|
73
|
+
tables. Afterwards, run the migration:
|
74
|
+
|
75
|
+
```sh
|
76
|
+
$ rails db:migrate
|
77
|
+
```
|
78
|
+
|
79
|
+
For your mailer to be able to generate email links, you'll need to set up
|
80
|
+
default URL options in each environment. Here is a possible configuration for
|
81
|
+
`config/environments/development.rb`:
|
82
|
+
|
83
|
+
```rb
|
84
|
+
config.action_mailer.default_url_options = { host: "localhost", port: 3000 }
|
85
|
+
```
|
86
|
+
|
87
|
+
### Install options
|
88
|
+
|
89
|
+
The install generator will use the `accounts` table by default. You can specify a different table name:
|
72
90
|
|
73
91
|
```sh
|
74
92
|
$ rails generate rodauth:install users
|
@@ -90,25 +108,6 @@ $ rails generate rodauth:install --argon2
|
|
90
108
|
$ bundle add argon2
|
91
109
|
```
|
92
110
|
|
93
|
-
This generator will create a Rodauth app and configuration with common
|
94
|
-
authentication features enabled, a database migration with tables required by
|
95
|
-
those features, a mailer with default templates, and a few other files.
|
96
|
-
|
97
|
-
Feel free to remove any features you don't need, along with their corresponding
|
98
|
-
tables. Afterwards, run the migration:
|
99
|
-
|
100
|
-
```sh
|
101
|
-
$ rails db:migrate
|
102
|
-
```
|
103
|
-
|
104
|
-
For your mailer to be able to generate email links, you'll need to set up
|
105
|
-
default URL options in each environment. Here is a possible configuration for
|
106
|
-
`config/environments/development.rb`:
|
107
|
-
|
108
|
-
```rb
|
109
|
-
config.action_mailer.default_url_options = { host: "localhost", port: 3000 }
|
110
|
-
```
|
111
|
-
|
112
111
|
## Usage
|
113
112
|
|
114
113
|
The Rodauth app will be called for each request before it reaches the Rails
|
@@ -134,18 +133,18 @@ $ rails rodauth:routes
|
|
134
133
|
```
|
135
134
|
Routes handled by RodauthApp:
|
136
135
|
|
137
|
-
GET
|
138
|
-
GET
|
139
|
-
GET
|
140
|
-
GET
|
141
|
-
GET
|
142
|
-
GET
|
143
|
-
GET
|
144
|
-
GET
|
145
|
-
GET
|
146
|
-
GET
|
147
|
-
GET
|
148
|
-
GET
|
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
|
149
148
|
```
|
150
149
|
|
151
150
|
Using this information, you can add some basic authentication links to your
|
@@ -153,7 +152,7 @@ navigation header:
|
|
153
152
|
|
154
153
|
```erb
|
155
154
|
<% if rodauth.logged_in? %>
|
156
|
-
<%= link_to "Sign out", rodauth.logout_path,
|
155
|
+
<%= link_to "Sign out", rodauth.logout_path, data: { turbo_method: :post } %>
|
157
156
|
<% else %>
|
158
157
|
<%= link_to "Sign in", rodauth.login_path %>
|
159
158
|
<%= link_to "Sign up", rodauth.create_account_path %>
|
@@ -181,28 +180,18 @@ class ApplicationController < ActionController::Base
|
|
181
180
|
end
|
182
181
|
```
|
183
182
|
|
184
|
-
```rb
|
185
|
-
current_account #=> #<Account id=123 email="user@example.com">
|
186
|
-
current_account.email #=> "user@example.com"
|
187
|
-
```
|
188
|
-
|
189
183
|
### Requiring authentication
|
190
184
|
|
191
|
-
You
|
192
|
-
|
193
|
-
in your Rodauth app's routing block, which helps keep the authentication logic
|
194
|
-
encapsulated:
|
185
|
+
You can require authentication for routes at the middleware level in in your Rodauth
|
186
|
+
app's routing block, which helps keep the authentication logic encapsulated:
|
195
187
|
|
196
188
|
```rb
|
197
189
|
# app/misc/rodauth_app.rb
|
198
190
|
class RodauthApp < Rodauth::Rails::App
|
199
|
-
# ...
|
200
191
|
route do |r|
|
201
|
-
# ...
|
202
192
|
r.rodauth # route rodauth requests
|
203
193
|
|
204
|
-
|
205
|
-
if r.path.start_with?("/dashboard")
|
194
|
+
if r.path.start_with?("/dashboard") # /dashboard/* routes
|
206
195
|
rodauth.require_account # redirect to login page if not authenticated
|
207
196
|
end
|
208
197
|
end
|
@@ -212,7 +201,6 @@ end
|
|
212
201
|
You can also require authentication at the controller layer:
|
213
202
|
|
214
203
|
```rb
|
215
|
-
# app/controllers/application_controller.rb
|
216
204
|
class ApplicationController < ActionController::Base
|
217
205
|
private
|
218
206
|
|
@@ -222,68 +210,46 @@ class ApplicationController < ActionController::Base
|
|
222
210
|
end
|
223
211
|
```
|
224
212
|
```rb
|
225
|
-
# app/controllers/dashboard_controller.rb
|
226
213
|
class DashboardController < ApplicationController
|
227
214
|
before_action :authenticate
|
228
215
|
end
|
229
216
|
```
|
230
217
|
|
231
|
-
|
232
|
-
|
233
|
-
In some cases it makes sense to require authentication at the Rails router
|
234
|
-
level. You can do this via the built-in `authenticated` routing constraint:
|
218
|
+
Additionally, routes can be authenticated at the Rails router level:
|
235
219
|
|
236
220
|
```rb
|
237
221
|
# config/routes.rb
|
238
222
|
Rails.application.routes.draw do
|
239
|
-
constraints Rodauth::Rails.
|
240
|
-
# ...
|
223
|
+
constraints Rodauth::Rails.authenticate do
|
224
|
+
# ... these routes will require authentication ...
|
241
225
|
end
|
242
|
-
end
|
243
|
-
```
|
244
226
|
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
```rb
|
249
|
-
# config/routes.rb
|
250
|
-
Rails.application.routes.draw do
|
251
|
-
# require multifactor authentication to be setup
|
252
|
-
constraints Rodauth::Rails.authenticated { |rodauth| rodauth.uses_two_factor_authentication? } do
|
253
|
-
# ...
|
227
|
+
constraints Rodauth::Rails.authenticate { |rodauth| rodauth.uses_two_factor_authentication? } do
|
228
|
+
# ... these routes will be available only if 2FA is setup ...
|
254
229
|
end
|
255
|
-
end
|
256
|
-
```
|
257
|
-
|
258
|
-
You can specify a different Rodauth configuration by passing the configuration name:
|
259
230
|
|
260
|
-
|
261
|
-
#
|
262
|
-
Rails.application.routes.draw do
|
263
|
-
constraints Rodauth::Rails.authenticated(:admin) do
|
264
|
-
# ...
|
231
|
+
constraints Rodauth::Rails.authenticate(:admin) do
|
232
|
+
# ... these routes will be authenticated with secondary "admin" configuration ...
|
265
233
|
end
|
266
|
-
end
|
267
|
-
```
|
268
|
-
|
269
|
-
If you need something more custom, you can always create the routing constraint
|
270
|
-
manually:
|
271
234
|
|
272
|
-
```rb
|
273
|
-
# config/routes.rb
|
274
|
-
Rails.application.routes.draw do
|
275
235
|
constraints -> (r) { !r.env["rodauth"].logged_in? } do # or env["rodauth.admin"]
|
276
|
-
# routes
|
236
|
+
# ... these routes will be available only if not authenticated ...
|
277
237
|
end
|
278
238
|
end
|
279
239
|
```
|
280
240
|
|
281
241
|
### Controller
|
282
242
|
|
283
|
-
Your Rodauth configuration is
|
284
|
-
it automatically executes any callbacks and rescue handlers defined on it (or the parent controller)
|
285
|
-
around Rodauth endpoints.
|
243
|
+
Your Rodauth configuration is linked to a Rails controller, which is primarily used to render views and handle CSRF protection, but will also execute any callbacks and rescue handlers defined on it around Rodauth endpoints.
|
286
244
|
|
245
|
+
```rb
|
246
|
+
# app/misc/rodauth_main.rb
|
247
|
+
class RodauthMain < Rodauth::Rails::Auth
|
248
|
+
configure do
|
249
|
+
rails_controller { RodauthController }
|
250
|
+
end
|
251
|
+
end
|
252
|
+
```
|
287
253
|
```rb
|
288
254
|
class RodauthController < ApplicationController
|
289
255
|
before_action :set_locale # executes before Rodauth endpoints
|
@@ -291,45 +257,31 @@ class RodauthController < ApplicationController
|
|
291
257
|
end
|
292
258
|
```
|
293
259
|
|
294
|
-
|
295
|
-
|
296
|
-
You can call any controller methods from your Rodauth configuration via `rails_controller_eval`:
|
260
|
+
Various methods are available in your Rodauth configuration to bridge the gap with the controller:
|
297
261
|
|
298
262
|
```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
263
|
class RodauthMain < Rodauth::Rails::Auth
|
310
264
|
configure do
|
265
|
+
# calling methods on the controller:
|
311
266
|
after_create_account do
|
312
|
-
rails_controller_eval {
|
267
|
+
rails_controller_eval { some_controller_method(account_id) }
|
313
268
|
end
|
314
|
-
end
|
315
|
-
end
|
316
|
-
```
|
317
269
|
|
318
|
-
|
270
|
+
# accessing Rails URL helpers:
|
271
|
+
login_redirect { rails_routes.dashboard_path }
|
319
272
|
|
320
|
-
|
321
|
-
|
273
|
+
# accessing Rails request object:
|
274
|
+
after_change_password do
|
275
|
+
if rails_request.format.turbo_stream?
|
276
|
+
return_response rails_render(turbo_stream: [turbo_stream.replace(...)])
|
277
|
+
end
|
278
|
+
end
|
322
279
|
|
323
|
-
|
324
|
-
|
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 }
|
280
|
+
# accessing Rails cookies:
|
281
|
+
after_login { rails_cookies.permanent[:last_account_id] = account_id }
|
330
282
|
end
|
331
283
|
end
|
332
|
-
```
|
284
|
+
```
|
333
285
|
|
334
286
|
## Views
|
335
287
|
|
@@ -338,97 +290,29 @@ you'll want to start editing the markup. You can run the following command to
|
|
338
290
|
copy Rodauth templates into your Rails app:
|
339
291
|
|
340
292
|
```sh
|
341
|
-
$ rails generate rodauth:views
|
342
|
-
# or
|
343
|
-
$ rails generate rodauth:views --css=tailwind # tailwind views (requires @tailwindcss/forms plugin)
|
293
|
+
$ rails generate rodauth:views
|
344
294
|
```
|
345
295
|
|
346
296
|
This will generate views for Rodauth features you have currently enabled into
|
347
|
-
the `app/views/rodauth` directory
|
348
|
-
the main configuration.
|
297
|
+
the `app/views/rodauth` directory (provided that `RodauthController` is set for
|
298
|
+
the main configuration).
|
349
299
|
|
350
|
-
|
351
|
-
these features (this will not remove any existing views):
|
300
|
+
The generator accepts various options:
|
352
301
|
|
353
302
|
```sh
|
354
|
-
|
355
|
-
|
303
|
+
# generate views with Tailwind markup (requires @tailwindcss/forms plugin)
|
304
|
+
$ rails generate rodauth:views --css=tailwind
|
356
305
|
|
357
|
-
|
306
|
+
# specify Rodauth features to generate views for
|
307
|
+
$ rails generate rodauth:views login create_account lockout otp
|
358
308
|
|
359
|
-
|
309
|
+
# generate views for all Rodauth features
|
360
310
|
$ rails generate rodauth:views --all
|
361
|
-
```
|
362
|
-
|
363
|
-
Use `--name` to generate views for a different Rodauth configuration:
|
364
311
|
|
365
|
-
|
312
|
+
# specify a different Rodauth configuration
|
366
313
|
$ rails generate rodauth:views webauthn two_factor_base --name admin
|
367
314
|
```
|
368
315
|
|
369
|
-
### Page titles
|
370
|
-
|
371
|
-
The generated configuration sets `title_instance_variable` to make page titles
|
372
|
-
available in your views via `@page_title` instance variable, which you can then
|
373
|
-
use in your layout:
|
374
|
-
|
375
|
-
```rb
|
376
|
-
# app/misc/rodauth_main.rb
|
377
|
-
class RodauthMain < Rodauth::Rails::Auth
|
378
|
-
configure do
|
379
|
-
title_instance_variable :@page_title
|
380
|
-
end
|
381
|
-
end
|
382
|
-
```
|
383
|
-
```erb
|
384
|
-
<!-- app/views/layouts/application.html.erb -->
|
385
|
-
<!DOCTYPE html>
|
386
|
-
<html>
|
387
|
-
<head>
|
388
|
-
<title><%= @page_title || "Default title" %></title>
|
389
|
-
<!-- ... -->
|
390
|
-
</head>
|
391
|
-
<!-- ... -->
|
392
|
-
</html>
|
393
|
-
```
|
394
|
-
|
395
|
-
### Layout
|
396
|
-
|
397
|
-
To use different layouts for different Rodauth views, you can compare the
|
398
|
-
request path in the layout method:
|
399
|
-
|
400
|
-
```rb
|
401
|
-
# app/controllers/rodauth_controller.rb
|
402
|
-
class RodauthController < ApplicationController
|
403
|
-
layout :rodauth_layout
|
404
|
-
|
405
|
-
private
|
406
|
-
|
407
|
-
def rodauth_layout
|
408
|
-
case request.path
|
409
|
-
when rodauth.login_path,
|
410
|
-
rodauth.create_account_path,
|
411
|
-
rodauth.verify_account_path,
|
412
|
-
rodauth.verify_account_resend_path,
|
413
|
-
rodauth.reset_password_path,
|
414
|
-
rodauth.reset_password_request_path
|
415
|
-
"authentication"
|
416
|
-
else
|
417
|
-
"dashboard"
|
418
|
-
end
|
419
|
-
end
|
420
|
-
end
|
421
|
-
```
|
422
|
-
|
423
|
-
### Turbo
|
424
|
-
|
425
|
-
[Turbo] has been disabled by default on all built-in and generated view
|
426
|
-
templates, because some Rodauth actions (multi-phase login, adding recovery
|
427
|
-
codes) aren't Turbo-compatible, as they return 200 responses on POST requests.
|
428
|
-
|
429
|
-
That being said, most of Rodauth *is* Turbo-compatible, so feel free to enable
|
430
|
-
Turbo for actions where you want to use it.
|
431
|
-
|
432
316
|
## Mailer
|
433
317
|
|
434
318
|
The install generator will create `RodauthMailer` with default email templates,
|
@@ -492,8 +376,6 @@ class CreateRodauthOtpSmsCodesRecoveryCodes < ActiveRecord::Migration
|
|
492
376
|
end
|
493
377
|
```
|
494
378
|
|
495
|
-
### Table prefix
|
496
|
-
|
497
379
|
If you're storing account records in a table other than `accounts`, you'll want
|
498
380
|
to specify the appropriate table prefix when generating new migrations:
|
499
381
|
|
@@ -516,15 +398,13 @@ class CreateRodauthUserBaseActiveSessions < ActiveRecord::Migration
|
|
516
398
|
end
|
517
399
|
```
|
518
400
|
|
519
|
-
### Custom migration name
|
520
|
-
|
521
401
|
You can change the default migration name:
|
522
402
|
|
523
403
|
```sh
|
524
404
|
$ rails generate rodauth:migration email_auth --name create_account_email_auth_keys
|
525
405
|
```
|
526
406
|
```rb
|
527
|
-
# db/migration/*_create_account_email_auth_keys
|
407
|
+
# db/migration/*_create_account_email_auth_keys.rb
|
528
408
|
class CreateAccountEmailAuthKeys < ActiveRecord::Migration
|
529
409
|
def change
|
530
410
|
create_table :account_email_auth_keys do |t| ... end
|
@@ -540,7 +420,7 @@ tables used by enabled authentication features.
|
|
540
420
|
|
541
421
|
```rb
|
542
422
|
class Account < ActiveRecord::Base # Sequel::Model
|
543
|
-
include Rodauth::
|
423
|
+
include Rodauth::Model(RodauthMain)
|
544
424
|
end
|
545
425
|
```
|
546
426
|
```rb
|
@@ -573,6 +453,10 @@ class RodauthApp < Rodauth::Rails::App
|
|
573
453
|
route do |r|
|
574
454
|
r.rodauth # route primary rodauth requests
|
575
455
|
r.rodauth(:admin) # route secondary rodauth requests
|
456
|
+
|
457
|
+
if request.path.start_with?("/admin")
|
458
|
+
rodauth(:admin).require_account
|
459
|
+
end
|
576
460
|
end
|
577
461
|
end
|
578
462
|
```
|
@@ -599,6 +483,7 @@ end
|
|
599
483
|
Then in your application you can reference the secondary Rodauth instance:
|
600
484
|
|
601
485
|
```rb
|
486
|
+
rodauth(:admin).authenticated? # checks "admin_account_id" session value
|
602
487
|
rodauth(:admin).login_path #=> "/admin/login"
|
603
488
|
```
|
604
489
|
|
@@ -608,66 +493,24 @@ that. Note that you can also [share configuration via inheritance][inheritance].
|
|
608
493
|
|
609
494
|
## Outside of a request
|
610
495
|
|
611
|
-
|
612
|
-
|
613
|
-
In some cases you might need to use Rodauth more programmatically. If you want
|
614
|
-
to perform authentication operations outside of request context, Rodauth ships
|
615
|
-
with the [internal_request] feature just for that.
|
496
|
+
The [internal_request] and [path_class_methods] features are supported, with defaults taken from `config.action_mailer.default_url_options`.
|
616
497
|
|
617
498
|
```rb
|
618
|
-
#
|
619
|
-
class RodauthMain < Rodauth::Rails::Auth
|
620
|
-
configure do
|
621
|
-
enable :internal_request
|
622
|
-
end
|
623
|
-
end
|
624
|
-
```
|
625
|
-
```rb
|
626
|
-
# primary configuration
|
499
|
+
# internal requests
|
627
500
|
RodauthApp.rodauth.create_account(login: "user@example.com", password: "secret123")
|
628
|
-
RodauthApp.rodauth.verify_account(account_login: "
|
629
|
-
|
630
|
-
# secondary configuration
|
631
|
-
RodauthApp.rodauth(:admin).close_account(account_login: "user@example.com")
|
632
|
-
```
|
633
|
-
|
634
|
-
### Generating URLs
|
635
|
-
|
636
|
-
For generating authentication URLs outside of a request use the
|
637
|
-
[path_class_methods] plugin:
|
638
|
-
|
639
|
-
```rb
|
640
|
-
# app/misc/rodauth_main.rb
|
641
|
-
class RodauthMain < Rodauth::Rails::Auth
|
642
|
-
configure do
|
643
|
-
enable :path_class_methods
|
644
|
-
create_account_route "register"
|
645
|
-
end
|
646
|
-
end
|
647
|
-
```
|
648
|
-
```rb
|
649
|
-
# primary configuration
|
650
|
-
RodauthApp.rodauth.create_account_path # => "/register"
|
651
|
-
RodauthApp.rodauth.verify_account_url(key: "abc123") #=> "https://example.com/verify-account?key=abc123"
|
501
|
+
RodauthApp.rodauth(:admin).verify_account(account_login: "admin@example.com")
|
652
502
|
|
653
|
-
#
|
654
|
-
RodauthApp.rodauth
|
503
|
+
# path and URL methods
|
504
|
+
RodauthApp.rodauth.close_account_path #=> "/close-account"
|
505
|
+
RodauthApp.rodauth(:admin).otp_setup_url #=> "http://localhost:3000/admin/otp-setup"
|
655
506
|
```
|
656
507
|
|
657
508
|
### Calling instance methods
|
658
509
|
|
659
510
|
If you need to access Rodauth methods not exposed as internal requests, you can
|
660
|
-
use `Rodauth::Rails.rodauth` to retrieve the Rodauth instance
|
661
|
-
internal_request feature:
|
511
|
+
use `Rodauth::Rails.rodauth` to retrieve the Rodauth instance (this requires enabling
|
512
|
+
the internal_request feature):
|
662
513
|
|
663
|
-
```rb
|
664
|
-
# app/misc/rodauth_main.rb
|
665
|
-
class RodauthMain < Rodauth::Rails::Auth
|
666
|
-
configure do
|
667
|
-
enable :internal_request # this is required
|
668
|
-
end
|
669
|
-
end
|
670
|
-
```
|
671
514
|
```rb
|
672
515
|
account = Account.find_by!(email: "user@example.com")
|
673
516
|
rodauth = Rodauth::Rails.rodauth(account: account) #=> #<RodauthMain::InternalRequest ...>
|
@@ -693,8 +536,12 @@ Rodauth::Rails.rodauth(:admin, params: { "param" => "value" })
|
|
693
536
|
|
694
537
|
### Using as a library
|
695
538
|
|
696
|
-
Rodauth offers a `Rodauth.lib` method for
|
539
|
+
Rodauth offers a [`Rodauth.lib`][library] method for when you want to use it as a library (via [internal requests][internal_request]), as opposed to having it route requests. This gem provides a `Rodauth::Rails.lib` counterpart that does the same but with Rails integration:
|
697
540
|
|
541
|
+
```rb
|
542
|
+
# skip require on boot to avoid inserting Rodauth middleware
|
543
|
+
gem "rodauth-rails", require: false
|
544
|
+
```
|
698
545
|
```rb
|
699
546
|
# app/misc/rodauth_main.rb
|
700
547
|
require "rodauth/rails"
|
@@ -712,13 +559,6 @@ RodauthMain.login(login: "email@example.com", password: "secret123")
|
|
712
559
|
RodauthMain.close_account(account_login: "email@example.com")
|
713
560
|
```
|
714
561
|
|
715
|
-
Note that you'll want to skip requiring `rodauth-rails` on Rails boot, so that it doesn't insert the middleware automatically, and remove the initializer.
|
716
|
-
|
717
|
-
```rb
|
718
|
-
# Gemfile
|
719
|
-
gem "rodauth-rails", require: false
|
720
|
-
```
|
721
|
-
|
722
562
|
## Testing
|
723
563
|
|
724
564
|
For system and integration tests, which run the whole middleware stack,
|
@@ -780,10 +620,7 @@ For more examples and information about testing with rodauth, see
|
|
780
620
|
|
781
621
|
## Configuring
|
782
622
|
|
783
|
-
|
784
|
-
|
785
|
-
The `rails` feature rodauth-rails loads provides the following configuration
|
786
|
-
methods:
|
623
|
+
The `rails` feature rodauth-rails loads provides the following configuration methods:
|
787
624
|
|
788
625
|
| Name | Description |
|
789
626
|
| :---- | :---------- |
|
@@ -796,70 +633,6 @@ methods:
|
|
796
633
|
| `rails_controller` | Controller class to use for rendering and CSRF protection. |
|
797
634
|
| `rails_account_model` | Model class connected with the accounts table. |
|
798
635
|
|
799
|
-
```rb
|
800
|
-
class RodauthMain < Rodauth::Rails::Auth
|
801
|
-
configure do
|
802
|
-
rails_controller { Authentication::RodauthController }
|
803
|
-
rails_account_model { Authentication::Account }
|
804
|
-
end
|
805
|
-
end
|
806
|
-
```
|
807
|
-
|
808
|
-
For the list of configuration methods provided by Rodauth, see the [feature
|
809
|
-
documentation].
|
810
|
-
|
811
|
-
### Defining custom methods
|
812
|
-
|
813
|
-
All Rodauth configuration methods are just syntax sugar for defining instance
|
814
|
-
methods on the auth class. You can also define your own custom methods:
|
815
|
-
|
816
|
-
```rb
|
817
|
-
class RodauthMain < Rodauth::Rails::Auth
|
818
|
-
configure do
|
819
|
-
password_match? { |password| ldap_valid?(password) }
|
820
|
-
end
|
821
|
-
|
822
|
-
def admin?
|
823
|
-
rails_account.admin?
|
824
|
-
end
|
825
|
-
|
826
|
-
private
|
827
|
-
|
828
|
-
def ldap_valid?(password)
|
829
|
-
SimpleLdapAuthenticator.valid?(account[:email], password)
|
830
|
-
end
|
831
|
-
end
|
832
|
-
```
|
833
|
-
```rb
|
834
|
-
rodauth.admin? #=> true
|
835
|
-
```
|
836
|
-
|
837
|
-
### Single-file configuration
|
838
|
-
|
839
|
-
If you would prefer, you can have all your Rodauth logic contained inside the
|
840
|
-
Rodauth app class:
|
841
|
-
|
842
|
-
```rb
|
843
|
-
# app/misc/rodauth_app.rb
|
844
|
-
class RodauthApp < Rodauth::Rails::App
|
845
|
-
# primary configuration
|
846
|
-
configure do
|
847
|
-
enable :login, :logout, :create_account, :verify_account
|
848
|
-
# ...
|
849
|
-
end
|
850
|
-
|
851
|
-
# secondary configuration
|
852
|
-
configure(:admin) do
|
853
|
-
enable :email_auth, :single_session
|
854
|
-
# ...
|
855
|
-
end
|
856
|
-
|
857
|
-
route do |r|
|
858
|
-
# ...
|
859
|
-
end
|
860
|
-
end
|
861
|
-
```
|
862
|
-
|
863
636
|
### Manually inserting middleware
|
864
637
|
|
865
638
|
You can choose to insert the Rodauth middleware somewhere earlier than
|
@@ -878,7 +651,7 @@ Rails.application.config.middleware.insert_before AnotherMiddleware, Rodauth::Ra
|
|
878
651
|
### Rack middleware
|
879
652
|
|
880
653
|
The railtie inserts [`Rodauth::Rails::Middleware`](/lib/rodauth/rails/middleware.rb)
|
881
|
-
at the end of the middleware stack, which
|
654
|
+
at the end of the middleware stack, which is just a wrapper around your Rodauth app.
|
882
655
|
|
883
656
|
```sh
|
884
657
|
$ rails middleware
|
@@ -887,34 +660,15 @@ $ rails middleware
|
|
887
660
|
# run MyApp::Application.routes
|
888
661
|
```
|
889
662
|
|
890
|
-
The middleware retrieves the Rodauth app via `Rodauth::Rails.app`, which is
|
891
|
-
specified as a string to keep the class autoloadable and reloadable in
|
892
|
-
development.
|
893
|
-
|
894
|
-
```rb
|
895
|
-
Rodauth::Rails.configure do |config|
|
896
|
-
config.app = "RodauthApp"
|
897
|
-
end
|
898
|
-
```
|
899
|
-
|
900
|
-
In addition to Zeitwerk compatibility, this extra layer catches Rodauth redirects
|
901
|
-
that happen on the controller level (e.g. when calling
|
902
|
-
`rodauth.require_account` in a `before_action` filter).
|
903
|
-
|
904
663
|
### Roda app
|
905
664
|
|
906
665
|
The [`Rodauth::Rails::App`](/lib/rodauth/rails/app.rb) class is a [Roda]
|
907
|
-
subclass that provides a convenience layer
|
908
|
-
|
909
|
-
* uses Action Dispatch flash messages
|
910
|
-
* provides syntax sugar for loading the rodauth plugin
|
911
|
-
* saves Rodauth object(s) to Rack env hash
|
912
|
-
* propagates edited headers to Rails responses
|
666
|
+
subclass that provides a convenience layer over Rodauth.
|
913
667
|
|
914
668
|
#### Configure block
|
915
669
|
|
916
|
-
The `configure` call
|
917
|
-
auth class and configuration name as positional arguments (
|
670
|
+
The `configure` call is a wrapper around `plugin :rodauth`. By convention, it receives an
|
671
|
+
auth class and configuration name as positional arguments (which get converted into
|
918
672
|
`:auth_class` and `:name` plugin options), a block for anonymous auth classes,
|
919
673
|
and also accepts any additional plugin options.
|
920
674
|
|
@@ -929,7 +683,7 @@ class RodauthApp < Rodauth::Rails::App
|
|
929
683
|
configure(:admin) { ... }
|
930
684
|
|
931
685
|
# plugin options
|
932
|
-
configure(RodauthMain, json: :only)
|
686
|
+
configure(RodauthMain, json: :only, render: false)
|
933
687
|
end
|
934
688
|
```
|
935
689
|
|
@@ -946,29 +700,21 @@ class RodauthApp < Rodauth::Rails::App
|
|
946
700
|
end
|
947
701
|
```
|
948
702
|
|
949
|
-
####
|
703
|
+
#### Rack env
|
950
704
|
|
951
|
-
|
952
|
-
|
953
|
-
prefix.
|
705
|
+
The app sets Rodauth objects for each registered configuration in the Rack env,
|
706
|
+
so that they're accessible downstream by the Rails router, controllers and views:
|
954
707
|
|
955
708
|
```rb
|
956
|
-
|
957
|
-
|
958
|
-
prefix "/user"
|
959
|
-
end
|
960
|
-
|
961
|
-
route do |r|
|
962
|
-
r.rodauth # no need to wrap with `r.on("user") { ... }`
|
963
|
-
end
|
964
|
-
end
|
709
|
+
request.env["rodauth"] #=> #<RodauthMain>
|
710
|
+
request.env["rodauth.admin"] #=> #<RodauthAdmin> (if using multiple configurations)
|
965
711
|
```
|
966
712
|
|
967
713
|
### Auth class
|
968
714
|
|
969
715
|
The [`Rodauth::Rails::Auth`](/lib/rodauth/rails/auth.rb) class is a subclass of
|
970
716
|
`Rodauth::Auth`, which preloads the `rails` rodauth feature, sets [HMAC] secret to
|
971
|
-
Rails' secret key base, and modifies some [configuration defaults]
|
717
|
+
Rails' secret key base, and modifies some [configuration defaults][restoring defaults].
|
972
718
|
|
973
719
|
```rb
|
974
720
|
class RodauthMain < Rodauth::Rails::Auth
|
@@ -990,128 +736,6 @@ The [`rails`](/lib/rodauth/rails/feature.rb) Rodauth feature loaded by
|
|
990
736
|
* uses Action Controller instrumentation around Rodauth requests
|
991
737
|
* uses Action Mailer's default URL options when calling Rodauth outside of a request
|
992
738
|
|
993
|
-
### Controller
|
994
|
-
|
995
|
-
The Rodauth app stores the `Rodauth::Rails::Auth` instances in the Rack env
|
996
|
-
hash, which is then available in your Rails app:
|
997
|
-
|
998
|
-
```rb
|
999
|
-
request.env["rodauth"] #=> #<RodauthMain>
|
1000
|
-
request.env["rodauth.admin"] #=> #<RodauthAdmin> (if using multiple configurations)
|
1001
|
-
```
|
1002
|
-
|
1003
|
-
For convenience, this object can be accessed via the `#rodauth` method in views
|
1004
|
-
and controllers:
|
1005
|
-
|
1006
|
-
```rb
|
1007
|
-
class MyController < ApplicationController
|
1008
|
-
def my_action
|
1009
|
-
rodauth #=> #<RodauthMain>
|
1010
|
-
rodauth(:admin) #=> #<RodauthAdmin> (if using multiple configurations)
|
1011
|
-
end
|
1012
|
-
end
|
1013
|
-
```
|
1014
|
-
```erb
|
1015
|
-
<% rodauth #=> #<RodauthMain> %>
|
1016
|
-
<% rodauth(:admin) #=> #<RodauthAdmin> (if using multiple configurations) %>
|
1017
|
-
```
|
1018
|
-
|
1019
|
-
## Rodauth defaults
|
1020
|
-
|
1021
|
-
rodauth-rails changes some of the default Rodauth settings for easier setup:
|
1022
|
-
|
1023
|
-
### Database functions
|
1024
|
-
|
1025
|
-
By default, on PostgreSQL, MySQL, and Microsoft SQL Server Rodauth uses
|
1026
|
-
database functions to access password hashes, with the user running the
|
1027
|
-
application unable to get direct access to password hashes. This reduces the
|
1028
|
-
risk of an attacker being able to access password hashes and use them to attack
|
1029
|
-
other sites.
|
1030
|
-
|
1031
|
-
While this is useful additional security, it is also more complex to set up and
|
1032
|
-
to reason about, as it requires having two different database users and making
|
1033
|
-
sure the correct migration is run for the correct user.
|
1034
|
-
|
1035
|
-
To keep with Rails' "convention over configuration" doctrine, rodauth-rails
|
1036
|
-
disables the use of database functions, though you can always turn it back on.
|
1037
|
-
|
1038
|
-
```rb
|
1039
|
-
use_database_authentication_functions? true
|
1040
|
-
```
|
1041
|
-
|
1042
|
-
To create the database functions, pass the Sequel database object into the
|
1043
|
-
Rodauth method for creating database functions:
|
1044
|
-
|
1045
|
-
```rb
|
1046
|
-
# db/migrate/*_create_rodauth_database_functions.rb
|
1047
|
-
require "rodauth/migrations"
|
1048
|
-
|
1049
|
-
class CreateRodauthDatabaseFunctions < ActiveRecord::Migration
|
1050
|
-
def up
|
1051
|
-
Rodauth.create_database_authentication_functions(db)
|
1052
|
-
end
|
1053
|
-
|
1054
|
-
def down
|
1055
|
-
Rodauth.drop_database_authentication_functions(db)
|
1056
|
-
end
|
1057
|
-
|
1058
|
-
private
|
1059
|
-
|
1060
|
-
def db
|
1061
|
-
RodauthMain.allocate.db
|
1062
|
-
end
|
1063
|
-
end
|
1064
|
-
```
|
1065
|
-
|
1066
|
-
### Account statuses
|
1067
|
-
|
1068
|
-
The recommended [Rodauth migration] stores possible account status values in a
|
1069
|
-
separate table, and creates a foreign key on the accounts table, which ensures
|
1070
|
-
only a valid status value will be persisted. Unfortunately, this doesn't work
|
1071
|
-
when the database is restored from the schema file, in which case the account
|
1072
|
-
statuses table will be empty. This happens in tests by default, but it's also
|
1073
|
-
not unusual to do it in development.
|
1074
|
-
|
1075
|
-
To address this, rodauth-rails uses a `status` column without a separate table.
|
1076
|
-
If you're worried about invalid status values creeping in, you may use enums
|
1077
|
-
instead. Alternatively, you can always go back to the setup recommended by
|
1078
|
-
Rodauth.
|
1079
|
-
|
1080
|
-
```rb
|
1081
|
-
# in the migration:
|
1082
|
-
create_table :account_statuses do |t|
|
1083
|
-
t.string :name, null: false, unique: true
|
1084
|
-
end
|
1085
|
-
execute "INSERT INTO account_statuses (id, name) VALUES (1, 'Unverified'), (2, 'Verified'), (3, 'Closed')"
|
1086
|
-
|
1087
|
-
create_table :accounts do |t|
|
1088
|
-
# ...
|
1089
|
-
t.references :status, foreign_key: { to_table: :account_statuses }, null: false, default: 1
|
1090
|
-
# ...
|
1091
|
-
end
|
1092
|
-
```
|
1093
|
-
```diff
|
1094
|
-
class RodauthMain < Rodauth::Rails::Auth
|
1095
|
-
configure do
|
1096
|
-
# ...
|
1097
|
-
- account_status_column :status
|
1098
|
-
# ...
|
1099
|
-
end
|
1100
|
-
end
|
1101
|
-
```
|
1102
|
-
|
1103
|
-
### Deadline values
|
1104
|
-
|
1105
|
-
To simplify changes to the database schema, rodauth-rails configures Rodauth
|
1106
|
-
to set deadline values for various features in Ruby, instead of relying on
|
1107
|
-
the database to set default column values.
|
1108
|
-
|
1109
|
-
You can easily change this back:
|
1110
|
-
|
1111
|
-
```rb
|
1112
|
-
set_deadline_values? false
|
1113
|
-
```
|
1114
|
-
|
1115
739
|
## License
|
1116
740
|
|
1117
741
|
The gem is available as open source under the terms of the [MIT
|
@@ -1125,12 +749,10 @@ conduct](CODE_OF_CONDUCT.md).
|
|
1125
749
|
|
1126
750
|
[Rodauth]: https://github.com/jeremyevans/rodauth
|
1127
751
|
[Sequel]: https://github.com/jeremyevans/sequel
|
1128
|
-
[feature documentation]: http://rodauth.jeremyevans.net/documentation.html
|
1129
752
|
[Bootstrap]: https://getbootstrap.com/
|
1130
753
|
[Roda]: http://roda.jeremyevans.net/
|
1131
754
|
[HMAC]: http://rodauth.jeremyevans.net/rdoc/files/README_rdoc.html#label-HMAC
|
1132
755
|
[database authentication functions]: http://rodauth.jeremyevans.net/rdoc/files/README_rdoc.html#label-Password+Hash+Access+Via+Database+Functions
|
1133
|
-
[Rodauth migration]: http://rodauth.jeremyevans.net/rdoc/files/README_rdoc.html#label-Creating+tables
|
1134
756
|
[sequel-activerecord_connection]: https://github.com/janko/sequel-activerecord_connection
|
1135
757
|
[plugin options]: http://rodauth.jeremyevans.net/rdoc/files/README_rdoc.html#label-Plugin+Options
|
1136
758
|
[hmac]: http://rodauth.jeremyevans.net/rdoc/files/README_rdoc.html#label-HMAC
|
@@ -1159,3 +781,5 @@ conduct](CODE_OF_CONDUCT.md).
|
|
1159
781
|
[rodauth-model]: https://github.com/janko/rodauth-model
|
1160
782
|
[JSON API]: https://github.com/janko/rodauth-rails/wiki/JSON-API
|
1161
783
|
[inheritance]: http://rodauth.jeremyevans.net/rdoc/files/doc/guides/share_configuration_rdoc.html
|
784
|
+
[library]: https://github.com/jeremyevans/rodauth#label-Using+Rodauth+as+a+Library
|
785
|
+
[restoring defaults]: https://github.com/janko/rodauth-rails/wiki/Restoring-Rodauth-Defaults
|
@@ -46,7 +46,7 @@ class RodauthMain < Rodauth::Rails::Auth
|
|
46
46
|
<% if argon2? -%>
|
47
47
|
|
48
48
|
# Use a rotatable password pepper when hashing passwords with Argon2.
|
49
|
-
# argon2_secret
|
49
|
+
# argon2_secret { hmac_secret }
|
50
50
|
|
51
51
|
# Since we're using argon2, prevent loading the bcrypt gem to save memory.
|
52
52
|
require_bcrypt? false
|
@@ -54,7 +54,7 @@ class RodauthMain < Rodauth::Rails::Auth
|
|
54
54
|
<% if jwt? -%>
|
55
55
|
|
56
56
|
# Set JWT secret, which is used to cryptographically protect the token.
|
57
|
-
jwt_secret
|
57
|
+
jwt_secret { hmac_secret }
|
58
58
|
<% end -%>
|
59
59
|
<% if json? || jwt? -%>
|
60
60
|
|
@@ -72,7 +72,7 @@ class RodauthMain < Rodauth::Rails::Auth
|
|
72
72
|
# Specify the controller used for view rendering, CSRF, and callbacks.
|
73
73
|
rails_controller { RodauthController }
|
74
74
|
|
75
|
-
#
|
75
|
+
# Make built-in page titles accessible in your views via an instance variable.
|
76
76
|
title_instance_variable :@page_title
|
77
77
|
|
78
78
|
# Store account status in an integer column without foreign key constraint.
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<% if defined?(ActiveRecord::Railtie) -%>
|
2
2
|
class <%= table_prefix.camelize %> < ApplicationRecord
|
3
|
-
include Rodauth::
|
3
|
+
include Rodauth::Model(RodauthMain)
|
4
4
|
<% if ActiveRecord.version >= Gem::Version.new("7.0") -%>
|
5
5
|
enum :status, unverified: 1, verified: 2, closed: 3
|
6
6
|
<% else -%>
|
@@ -9,7 +9,7 @@ class <%= table_prefix.camelize %> < ApplicationRecord
|
|
9
9
|
end
|
10
10
|
<% else -%>
|
11
11
|
class <%= table_prefix.camelize %> < Sequel::Model
|
12
|
-
include Rodauth::
|
12
|
+
include Rodauth::Model(RodauthMain)
|
13
13
|
plugin :enum
|
14
14
|
enum :status, unverified: 1, verified: 2, closed: 3
|
15
15
|
end
|
@@ -41,6 +41,7 @@ module Rodauth
|
|
41
41
|
recovery_codes: %w[recovery_codes add_recovery_codes recovery_auth],
|
42
42
|
webauthn: %w[webauthn_setup webauthn_auth webauthn_remove],
|
43
43
|
webauthn_autofill: %w[webauthn_autofill],
|
44
|
+
confirm_password: %w[confirm_password],
|
44
45
|
}
|
45
46
|
|
46
47
|
def create_views
|
data/lib/rodauth/rails/app.rb
CHANGED
@@ -5,7 +5,7 @@ module Rodauth
|
|
5
5
|
module Rails
|
6
6
|
# The superclass for creating a Rodauth middleware.
|
7
7
|
class App < Roda
|
8
|
-
plugin :middleware, forward_response_headers: true do |middleware|
|
8
|
+
plugin :middleware, forward_response_headers: true, next_if_not_found: true do |middleware|
|
9
9
|
middleware.class_eval do
|
10
10
|
def self.inspect
|
11
11
|
"#{superclass}::Middleware"
|
@@ -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
|
-
|
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
|
-
|
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
|
|
@@ -53,6 +54,10 @@ module Rodauth
|
|
53
54
|
::Rails.application.routes.url_helpers
|
54
55
|
end
|
55
56
|
|
57
|
+
def rails_cookies
|
58
|
+
rails_request.cookie_jar
|
59
|
+
end
|
60
|
+
|
56
61
|
def rails_request
|
57
62
|
ActionDispatch::Request.new(env)
|
58
63
|
end
|
@@ -76,7 +81,7 @@ module Rodauth
|
|
76
81
|
if prefix.present? && remaining_path == path_info
|
77
82
|
on prefix[1..-1] do
|
78
83
|
super
|
79
|
-
pass
|
84
|
+
pass
|
80
85
|
end
|
81
86
|
else
|
82
87
|
super
|
@@ -47,7 +47,7 @@ module Rodauth
|
|
47
47
|
raise Error, "cannot infer account model, please set `rails_account_model` in your rodauth configuration"
|
48
48
|
end
|
49
49
|
|
50
|
-
delegate :rails_routes, :rails_request, to: :scope
|
50
|
+
delegate :rails_routes, :rails_cookies, :rails_request, to: :scope
|
51
51
|
|
52
52
|
private
|
53
53
|
|
@@ -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
|
-
|
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
|
-
|
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
|
data/lib/rodauth/rails.rb
CHANGED
@@ -16,9 +16,9 @@ module Rodauth
|
|
16
16
|
@middleware = true
|
17
17
|
|
18
18
|
class << self
|
19
|
-
def lib(&block)
|
19
|
+
def lib(**options, &block)
|
20
20
|
c = Class.new(Rodauth::Rails::App)
|
21
|
-
c.configure(json: false) do
|
21
|
+
c.configure(json: false, **options) do
|
22
22
|
enable :internal_request
|
23
23
|
instance_exec(&block)
|
24
24
|
end
|
@@ -57,8 +57,17 @@ module Rodauth
|
|
57
57
|
Rodauth::Model.new(app.rodauth!(name), **options)
|
58
58
|
end
|
59
59
|
|
60
|
-
#
|
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
|
+
|
61
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."
|
62
71
|
lambda do |request|
|
63
72
|
rodauth = request.env.fetch ["rodauth", *name].join(".")
|
64
73
|
rodauth.require_authentication
|
data/rodauth-rails.gemspec
CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
|
|
18
18
|
|
19
19
|
spec.add_dependency "railties", ">= 5.0", "< 8"
|
20
20
|
spec.add_dependency "rodauth", "~> 2.30"
|
21
|
-
spec.add_dependency "roda", "~> 3.
|
21
|
+
spec.add_dependency "roda", "~> 3.73"
|
22
22
|
spec.add_dependency "sequel-activerecord_connection", "~> 1.1"
|
23
23
|
spec.add_dependency "rodauth-model", "~> 0.2"
|
24
24
|
spec.add_dependency "tilt"
|
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.
|
4
|
+
version: 1.12.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-
|
11
|
+
date: 2023-10-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: railties
|
@@ -50,14 +50,14 @@ dependencies:
|
|
50
50
|
requirements:
|
51
51
|
- - "~>"
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
version: '3.
|
53
|
+
version: '3.73'
|
54
54
|
type: :runtime
|
55
55
|
prerelease: false
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
57
57
|
requirements:
|
58
58
|
- - "~>"
|
59
59
|
- !ruby/object:Gem::Version
|
60
|
-
version: '3.
|
60
|
+
version: '3.73'
|
61
61
|
- !ruby/object:Gem::Dependency
|
62
62
|
name: sequel-activerecord_connection
|
63
63
|
requirement: !ruby/object:Gem::Requirement
|
@@ -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.
|
355
|
+
rubygems_version: 3.4.10
|
355
356
|
signing_key:
|
356
357
|
specification_version: 4
|
357
358
|
summary: Provides Rails integration for Rodauth.
|