rodauth-rails 1.10.0 → 1.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +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.
|