devise-passwordless 0.7.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8276d8f566314087d30d8e15676a95b77bbfeede3d6802a4b7dbe4a9cfb6e963
4
- data.tar.gz: 4a8d4051f14c72db6d741cd9d83f1ba016e7097ab80864cbd42e6fcef81c58af
3
+ metadata.gz: 8c2ccc142bc114ca58125c72ea17c9646a6bd602d2a70e99de49b6412df12950
4
+ data.tar.gz: 7682b97e852e56559ef5babf8bf4f9e0c323083a32b3e16f8d932976e3204aea
5
5
  SHA512:
6
- metadata.gz: 84bb670d576c6f44a2f86dc8b398fdf544cc9ed8c5d9fc6d770fd3c64696865fcc31db39a86a0a76d05d8cd9fbf032595be3fe00c13325403eafbdf8abfcb07e
7
- data.tar.gz: 8ac08c85c508ca556525005bbcde0a170edb3656fc09ba5b63015778a49eb5e247d629b7b411792f0dfbb5da583c3ddf9e3519869e5eca2d62c56f54aff2a85a
6
+ metadata.gz: 4ac233d2eff38815cca3fadbe0c5b84e919b6129e7e763f30fb18cb5e75b095d10362126e8448fed612dad292463a997d82b36c8ffcb8b95252aacc20db5e46d
7
+ data.tar.gz: 4feb6345e80a8f2a0ea37527f7926cbff412e70a6bfda8a35166cc686f7eef0bb28d8a63a3702c59ac5ccbba308ca2386ec86e083d748e57bd6fdcdd176f49b1
data/CHANGELOG.md ADDED
@@ -0,0 +1,44 @@
1
+ ## 1.0.0
2
+
3
+ ### Enhancements
4
+
5
+ * Tokenization encoding/decoding is now fully customizable
6
+ * Tokenizer encoding now supports extra metadata ([#27] - thanks [@fastjames] and [@elucid]!)
7
+ * Tokenizer encoding now supports `:expires_at` option ([#19], [#21] - thanks [@joeyparis] / [@JoeyLeadJig] and [@bvsatyaram]!)
8
+ * Turbo is now properly supported ([#23], [#33] - thanks [@iainbeeston] and [@til]!)
9
+ * Signed GlobalID tokenization supported ([#22])
10
+ * Concurrent use of password auth (`:database_authenticatable` strategy) now supported ([#13] - thanks [@fschwahn]!)
11
+ * `Devise.paranoid` is now respected with new ambiguous messaging i18n option `:magic_link_sent_paranoid` following ([#36] - thanks [@cbldev]!)
12
+ * More thorough integration testing using a dummy Rails app
13
+ * Added a Rails engine to solve loading issues and tidy up file structuring
14
+ * `Passwordless::SessionsController` now uses gem source instead of needing to be generated from a template
15
+ * `MagicLinksController` no longer requires a weird `routes.rb` entry to work
16
+ * `MagicLinksController` now uses gem source instead of needing to be generated from a template
17
+ * `magic_link_(path|url)` view helpers are now implemented for all resources (cleans up mailer view template)
18
+ * Users will be redirected after magic link is sent (customized using `after_magic_link_sent_path_for`)
19
+ * A warning will be logged if Rails's `filter_parameters` doesn't filter `:token`s from request logs
20
+
21
+ ### Bugfixes
22
+
23
+ * Autoloading issues related to `uninitialized constant` (e.g.
24
+ `Devise::Passwordless::Mailer`) should now be fixed
25
+
26
+
27
+ [@bvsatyaram]: https://github.com/bvsatyaram
28
+ [@cbldev]: https://github.com/cbldev
29
+ [@fastjames]: https://github.com/fastjames
30
+ [@fschwahn]: https://github.com/fschwahn
31
+ [@elucid]: https://github.com/elucid
32
+ [@iainbeeston]: https://github.com/iainbeeston
33
+ [@joeyparis]: https://github.com/joeyparis
34
+ [@JoeyLeadJig]: https://github.com/JoeyLeadJig
35
+ [@til]: https://github.com/til
36
+
37
+ [#13]: https://github.com/abevoelker/devise-passwordless/issues/13
38
+ [#19]: https://github.com/abevoelker/devise-passwordless/pull/19
39
+ [#21]: https://github.com/abevoelker/devise-passwordless/pull/21
40
+ [#22]: https://github.com/abevoelker/devise-passwordless/issues/22
41
+ [#23]: https://github.com/abevoelker/devise-passwordless/pull/23
42
+ [#27]: https://github.com/abevoelker/devise-passwordless/pull/27
43
+ [#33]: https://github.com/abevoelker/devise-passwordless/pull/33
44
+ [#36]: https://github.com/abevoelker/devise-passwordless/pull/36
data/README.md CHANGED
@@ -1,14 +1,25 @@
1
1
  # Devise::Passwordless
2
2
 
3
- A passwordless a.k.a. "magic link" login strategy for [Devise][]
3
+ A passwordless login strategy for [Devise] using emailed magic links
4
4
 
5
5
  ## Features
6
6
 
7
- * No special database migrations needed - magic links are stateless encrypted tokens
8
- * Magic links are sent from your app - not a mounted Rails engine - so path and URL helpers work as expected
7
+ * No passwords - users receive magic link emails to register / sign-in
8
+ * No database changes needed - magic links are stateless tokens
9
+ * [Choose your token encoding algorithm or easily write your own](#tokenizers)
10
+ * [Can be combined with traditional password authentication in the same model](#combining-password-and-passwordless-auth-in-the-same-model)
9
11
  * [Supports multiple user (resource) types](#multiple-user-resource-types)
10
12
  * All the goodness of Devise!
11
13
 
14
+ ## 0.x to 1.0 Upgrade
15
+
16
+ ⭐ The 1.0 release includes significant breaking changes! ⭐
17
+
18
+ If you're upgrading from 0.x to 1.0, read [the upgrade guide][] for
19
+ a list of changes you'll need to make.
20
+
21
+ [the upgrade guide]: https://github.com/abevoelker/devise-passwordless/blob/master/UPGRADING.md
22
+
12
23
  ## Installation
13
24
 
14
25
  First, install and set up [Devise][].
@@ -35,7 +46,7 @@ See the [customization section](#customization) for details on what gets install
35
46
 
36
47
  ## Usage
37
48
 
38
- This gem adds a `:magic_link_authenticatable` strategy that can be used in your Devise models for passwordless authentication. This strategy plays well with most other Devise strategies (see [*notes on other Devise strategies*](#notes-on-other-devise-strategies)).
49
+ This gem adds a `:magic_link_authenticatable` strategy that can be used in your Devise models for passwordless authentication. This strategy plays well with most other Devise strategies (see [*compatibility with other Devise strategies*](#compatibility-with-other-devise-strategies)).
39
50
 
40
51
  For example, if your Devise model is User, enable the strategy like this:
41
52
 
@@ -46,48 +57,43 @@ class User < ApplicationRecord
46
57
  end
47
58
  ```
48
59
 
49
- Then, you'll need to set up your Devise routes like so to use the passwordless controllers to modify Devise's default session create logic and to handle processing magic links:
60
+ Then, change your route to process sessions using the passwordless sessions controller:
50
61
 
51
62
  ```ruby
52
63
  # config/routes.rb
53
64
  Rails.application.routes.draw do
54
65
  devise_for :users,
55
66
  controllers: { sessions: "devise/passwordless/sessions" }
56
- devise_scope :user do
57
- get "/users/magic_link",
58
- to: "devise/passwordless/magic_links#show",
59
- as: "users_magic_link"
60
- end
61
67
  end
62
68
  ```
63
69
 
64
- Finally, you'll want to update Devise's generated views to remove references to passwords, since you don't need them any more!
70
+ Finally, we need to update Devise's views to remove references to passwords. We will assume you're using the standard Devise views for all your registrations and logins; if you need to support multiple Devise models, some with passwordless login and some with password login, then jump down to the [multiple users section below](#multiple-user-resource-types).
71
+
72
+ First, ensure you have Devise views generated for your project under `app/views/devise`. If not, you can generate them with:
65
73
 
66
- These files/directories can be deleted entirely:
74
+ ```
75
+ rails generate devise:views
76
+ ```
77
+
78
+ Then, delete these files and directories:
67
79
 
68
80
  ```
69
- app/views/devise/passwords
70
- app/views/devise/mailer/password_change.html.erb
71
- app/views/devise/mailer/reset_password_instructions.html.erb
81
+ rm -rf app/views/devise/passwords
82
+ rm -f app/views/devise/mailer/password_change.html.erb
83
+ rm -f app/views/devise/mailer/reset_password_instructions.html.erb
72
84
  ```
73
85
 
74
- And these should be edited to remove password references:
86
+ Then, edit these files to remove password references:
75
87
 
76
- * `app/views/devise/registrations/new.html.erb`
88
+ * app/views/devise/registrations/new.html.erb
77
89
  * Delete fields `:password` and `:password_confirmation`
78
- * `app/views/devise/registrations/edit.html.erb`
90
+ * app/views/devise/registrations/edit.html.erb
79
91
  * Delete fields `:password`, `:password_confirmation`, `:current_password`
80
- * `app/views/devise/sessions/new.html.erb`
92
+ * app/views/devise/sessions/new.html.erb
81
93
  * Delete field `:password`
82
94
 
83
- #### Manually sending magic links
84
-
85
- You can very easily send a magic link at any point like so:
86
-
87
- ```ruby
88
- remember_me = true
89
- User.send_magic_link(remember_me)
90
- ```
95
+ That's it! 🎉 Now check out the customization section so that you
96
+ may change the default configuration to better match your needs.
91
97
 
92
98
  ## Customization
93
99
 
@@ -96,10 +102,14 @@ Configuration options are stored in Devise's initializer at `config/initializers
96
102
  ```ruby
97
103
  # ==> Configuration for :magic_link_authenticatable
98
104
 
99
- # Need to use a custom Devise mailer in order to send magic links
100
- require "devise/passwordless/mailer"
105
+ # Need to use a custom Devise mailer in order to send magic links.
106
+ # If you're already using a custom mailer just have it inherit from
107
+ # Devise::Passwordless::Mailer instead of Devise::Mailer
101
108
  config.mailer = "Devise::Passwordless::Mailer"
102
109
 
110
+ # Which algorithm to use for tokenizing magic links. See README for descriptions
111
+ config.passwordless_tokenizer = "SignedGlobalIDTokenizer"
112
+
103
113
  # Time period after a magic login link is sent out that it will be valid for.
104
114
  # config.passwordless_login_within = 20.minutes
105
115
 
@@ -115,6 +125,32 @@ config.mailer = "Devise::Passwordless::Mailer"
115
125
  # config.passwordless_expire_old_tokens_on_sign_in = false
116
126
  ```
117
127
 
128
+ Most config options can be set on a per-model basis. For instance,
129
+ you can use different tokenizers across different models like so:
130
+
131
+ ```ruby
132
+ # app/models/user.rb
133
+ class User < ApplicationRecord
134
+ devise :magic_link_authenticatable
135
+
136
+ def self.passwordless_tokenizer
137
+ "SignedGlobalIDTokenizer"
138
+ end
139
+ end
140
+
141
+ # app/models/another_user.rb
142
+ class AnotherUser < ApplicationRecord
143
+ devise :magic_link_authenticatable
144
+
145
+ def self.passwordless_tokenizer
146
+ "MessageEncryptorTokenizer"
147
+ end
148
+ def self.passwordless_login_within
149
+ 1.hour
150
+ end
151
+ end
152
+ ```
153
+
118
154
  To customize the magic link email subject line and other status and error messages, modify these values in `config/locales/devise.en.yml`:
119
155
 
120
156
  ```yaml
@@ -123,6 +159,7 @@ en:
123
159
  passwordless:
124
160
  not_found_in_database: "Could not find a user for that email address"
125
161
  magic_link_sent: "A login link has been sent to your email address. Please follow the link to log in to your account."
162
+ magic_link_sent_paranoid: "If your account exists, you will receive an email with a login link. Please follow the link to log in to your account."
126
163
  failure:
127
164
  magic_link_invalid: "Invalid or expired login link."
128
165
  mailer:
@@ -130,9 +167,171 @@ en:
130
167
  subject: "Here's your magic login link ✨"
131
168
  ```
132
169
 
170
+ **Note**: If [Devise's paranoid mode][] is enabled in your Devise initializer, the
171
+ `:magic_link_sent_paranoid` message will be used both when a user account exists
172
+ and when it does not exist to prevent account enumeration vulnerabilities. If
173
+ paranoid mode is disabled, then `:magic_link_sent` will be used for existing
174
+ accounts, and `:not_found_in_database` when no account was found.
175
+
176
+ [Devise's paranoid mode]: https://github.com/heartcombo/devise/wiki/How-To:-Using-paranoid-mode,-avoid-user-enumeration-on-registerable
177
+
133
178
  To customize the magic link email body, edit `app/views/devise/mailer/magic_link.html.erb`
134
179
 
135
- ### Multiple user (resource) types
180
+ ## Manually creating and sending magic links
181
+
182
+ Magic links are created and sent normally using Devise's views for sign-in and registration, but you can create them manually as well.
183
+
184
+ To send a magic link email, do this:
185
+
186
+ ```ruby
187
+ user = User.last
188
+ user.send_magic_link
189
+ # additional options are passed through to Devise's mailer logic
190
+ user.send_magic_link(remember_me: true, subject: "Custom email subject", "X-Entity-Ref-ID": SecureRandom.uuid)
191
+ ```
192
+
193
+ If you only need to generate the token portion of a magic link, you can do this:
194
+
195
+ ```ruby
196
+ # see the tokenizer's #encode method for all supported keyword options
197
+ token = user.encode_passwordless_token(expires_at: 2.hours.from_now)
198
+ ```
199
+
200
+ Or, to generate the full magic link URL, use this URL view helper:
201
+
202
+ ```ruby
203
+ user_magic_link_url(
204
+ user: {
205
+ email: user.email,
206
+ token: token,
207
+ remember_me: true
208
+ }
209
+ )
210
+ ```
211
+
212
+ ## Redirecting after magic link is sent
213
+
214
+ After a user enters their email on the sign-in page, and a magic link is sent, the user
215
+ will be redirected back to the `:root` path of the application.
216
+
217
+ To provide a custom redirect location, you can write a custom
218
+ `after_magic_link_sent_path_for` helper, similar to
219
+ [how Devise's `after_sign_in_path_for` helper works][after_sign_in_path_for]:
220
+
221
+ ```ruby
222
+ class ApplicationController < ActionController::Base
223
+ def after_magic_link_sent_path_for(resource_or_scope)
224
+ "/foo"
225
+ end
226
+ end
227
+ ```
228
+
229
+ [after_sign_in_path_for]: https://github.com/heartcombo/devise/wiki/How-To:-Redirect-back-to-current-page-after-sign-in,-sign-out,-sign-up,-update
230
+
231
+ If you need to have different paths for multiple different types of resources,
232
+ you can write something like this:
233
+
234
+ ```ruby
235
+ class ApplicationController < ActionController::Base
236
+ def after_magic_link_sent_path_for(resource)
237
+ case resource.class
238
+ when FooUser
239
+ happy_path
240
+ when BarUser
241
+ sad_path
242
+ end
243
+ end
244
+ end
245
+ ```
246
+
247
+ And, if you need more complex behavior, you can always write a custom sessions
248
+ controller for each resource:
249
+
250
+ ```ruby
251
+ # app/controllers/custom_sessions_controller.rb
252
+ class CustomSessionsController < Devise::Passwordless::SessionsController
253
+ def create
254
+ # your custom logic
255
+ end
256
+ end
257
+
258
+ # config/routes.rb
259
+ Rails.application.routes.draw do
260
+ devise_for :users,
261
+ controllers: { sessions: "custom_sessions" }
262
+ end
263
+ ```
264
+
265
+ ## Tokenizers
266
+
267
+ Tokenizers handle encoding and decoding of magic link tokens. There are multiple
268
+ pre-built ones to choose from, or [you can write your own](#your-own-custom-tokenizer).
269
+
270
+ Set the default tokenizer in your Devise initializer (`config.passwordless_tokenizer`),
271
+ which will be the global default. If you want a model to have a different tokenizer
272
+ than the default, you can define a class method `::passwordless_tokenizer` on your
273
+ model and that will be used instead. Models can have different tokenizers from
274
+ each other in this way.
275
+
276
+ ### SignedGlobalIDTokenizer
277
+
278
+ ```ruby
279
+ config.passwordless_tokenizer = "SignedGlobalIDTokenizer"
280
+ ```
281
+
282
+ Tokens are [Rails signed Global IDs][globalid]. This is the default for new installs.
283
+
284
+ Reasons to use or not use:
285
+
286
+ * The implementation is short and simple, so less likely to be buggy
287
+ * Should work with all ORMs that implement GlobalID support
288
+ * Cannot add arbitrary metadata to generated tokens
289
+ * Tokens are signed, not encrypted, so some data will be visible when base64-decoded
290
+ * Tokens tend to be a little longer (~30 chars IME) than MessageEncryptors'
291
+
292
+ [globalid]: https://github.com/rails/globalid
293
+
294
+ ### MessageEncryptorTokenizer
295
+
296
+ ```ruby
297
+ config.passwordless_tokenizer = "MessageEncryptorTokenizer"
298
+ ```
299
+
300
+ Tokens are encrypted using Rails's [MessageEncryptor][].
301
+
302
+ [MessageEncryptor]: https://api.rubyonrails.org/classes/ActiveSupport/MessageEncryptor.html
303
+
304
+ Reasons to use or not use:
305
+
306
+ * This was the only tokenizer in previous library versions
307
+ * The implementation is longer and more involved than SignedGlobalID
308
+ * Written with ActiveRecord in mind but may work with other ORMs
309
+ * Can add arbitrary extra metadata to tokens
310
+ * Tokens are opaque, due to being encrypted - no data visible when base64-decoded
311
+ * Tokens tend to be a little shorter than SignedGlobalID IME
312
+
313
+ ### Your own custom tokenizer
314
+
315
+ It's straightforward to write your own tokenizer class; it just needs to respond to
316
+ `::encode` and `::decode`:
317
+
318
+ ```ruby
319
+ class LuckyUserTokenizer
320
+ def self.encode(resource, *args)
321
+ "8" * 88 # our token is always lucky!
322
+ end
323
+
324
+ def self.decode(token, resource_class, *args)
325
+ # ignore token and retrieve a random user
326
+ [resource_class.order("RANDOM()").limit(1).first, extra_data={}]
327
+ end
328
+ end
329
+
330
+ # config/initializers/devise.rb
331
+ config.passwordless_tokenizer = "::LuckyUserTokenizer"
332
+ ```
333
+
334
+ ## Multiple user (resource) types
136
335
 
137
336
  Devise supports multiple resource types, so we do too.
138
337
 
@@ -157,18 +356,8 @@ Then just set up your routes like this:
157
356
  Rails.application.routes.draw do
158
357
  devise_for :users,
159
358
  controllers: { sessions: "devise/passwordless/sessions" }
160
- devise_scope :user do
161
- get "/users/magic_link",
162
- to: "devise/passwordless/magic_links#show",
163
- as: "users_magic_link"
164
- end
165
359
  devise_for :admins,
166
360
  controllers: { sessions: "devise/passwordless/sessions" }
167
- devise_scope :admin do
168
- get "/admins/magic_link",
169
- to: "devise/passwordless/magic_links#show",
170
- as: "admins_magic_link"
171
- end
172
361
  end
173
362
  ```
174
363
 
@@ -183,9 +372,11 @@ en:
183
372
  user:
184
373
  not_found_in_database: "Could not find a USER for that email address"
185
374
  magic_link_sent: "A USER login link has been sent to your email address. Please follow the link to log in to your account."
375
+ magic_link_sent_paranoid: "If your USER account exists, you will receive an email with a login link. Please follow the link to log in to your account."
186
376
  admin:
187
377
  not_found_in_database: "Could not find an ADMIN for that email address"
188
378
  magic_link_sent: "An ADMIN login link has been sent to your email address. Please follow the link to log in to your account."
379
+ magic_link_sent_paranoid: "If your ADMIN account exists, you will receive an email with a login link. Please follow the link to log in to your account."
189
380
  failure:
190
381
  user:
191
382
  magic_link_invalid: "Invalid or expired USER login link."
@@ -197,7 +388,7 @@ en:
197
388
  admin_subject: "Here's your ADMIN magic login link ✨"
198
389
  ```
199
390
 
200
- #### Scoped views
391
+ ### Scoped views
201
392
 
202
393
  If you have multiple Devise models, some that are passwordless and some that aren't, you will probably want to enable [Devise's `scoped_views` setting](https://henrytabima.github.io/rails-setup/docs/devise/configuring-views) so that the models have different signup and login pages (since some models will need password fields and others won't).
203
394
 
@@ -215,7 +406,123 @@ app/views/users/
215
406
  app/views/admins/
216
407
  ```
217
408
 
218
- ### Notes on other Devise strategies
409
+ ## Combining password and passwordless auth in the same model
410
+
411
+ It is possible to use both traditional password authentication (i.e. the
412
+ `:database_authenticatable` strategy) alongside magic link authentication in
413
+ the same model:
414
+
415
+ ```ruby
416
+ # app/models/user.rb
417
+ class User < ApplicationRecord
418
+ devise :database_authenticatable, :magic_link_authenticatable, :registerable,
419
+ :recoverable, :rememberable, :validatable
420
+ end
421
+ ```
422
+
423
+ How you end up implementing it will be highly dependent on your use case. By
424
+ default, all password validations will still run - so on registration, users
425
+ will have to provide passwords - but they'll be able to log in via either
426
+ password OR magic link (you'll have to customize your routes and views to
427
+ make the separate paths accessible).
428
+
429
+ Here's an example routes file of that scenario (a separate namespace is
430
+ needed because the password vs. passwordless paths use different sessions
431
+ controllers):
432
+
433
+ ```ruby
434
+ devise_for :users
435
+ namespace "passwordless" do
436
+ devise_for :users,
437
+ controllers: { sessions: "devise/passwordless/sessions" }
438
+ end
439
+ ```
440
+
441
+ Visiting `/users/sign_in` will lead to a password sign in, while
442
+ `/passwordless/users/sign_in` will lead to the magic link sign in flow
443
+ (you'll need to [generate the necessary Devise views](#scoped-views)
444
+ to support the different sign-in forms).
445
+
446
+ ### Disabling password authentication or magic link authentication
447
+
448
+ Rather than *all* your users having access to *both* authentication methods,
449
+ it may be the case that you want *some* users to use magic links, *some*
450
+ to use passwords, or some combination between the two.
451
+
452
+ This can be managed by defining some methods that disable the relevant
453
+ authentication strategy and determine the failure message. Here are
454
+ examples for both:
455
+
456
+ ### Disabling password authentication
457
+
458
+ Let's say you want to disable password authentication for everyone except
459
+ people named Bob:
460
+
461
+ ```ruby
462
+ class User < ApplicationRecord
463
+ # devise :database_authenticatable, :magic_link_authenticatable, ...
464
+
465
+ def first_name_bob?
466
+ self.first_name.downcase == "bob"
467
+ end
468
+
469
+ # The `super` is important in the following two methods as other
470
+ # auth strategies chain onto these methods:
471
+
472
+ def active_for_authentication?
473
+ super && first_name_bob?
474
+ end
475
+
476
+ def inactive_message
477
+ first_name_bob? ? super : :first_name_not_bob
478
+ end
479
+ end
480
+ ```
481
+
482
+ Then, you add this to your `devise.yml` to customize the error message:
483
+
484
+ ```yaml
485
+ devise:
486
+ failure:
487
+ first_name_not_bob: "Sorry, only Bobs may log in using their password. Try magic link login instead."
488
+ ```
489
+
490
+ Now, when users not named Bob try to log in with their password, it'll fail with
491
+ your custom failure message.
492
+
493
+ ### Disabling passwordless / magic link authentication
494
+
495
+ Disabling magic link authentication is a similar process, just with different
496
+ method names:
497
+
498
+ ```ruby
499
+ class User < ApplicationRecord
500
+ # devise :database_authenticatable, :magic_link_authenticatable, ...
501
+
502
+ def first_name_alice?
503
+ self.first_name.downcase == "alice"
504
+ end
505
+
506
+ # The `super` is actually not important at the moment for these, but if
507
+ # any future Devise strategies were to extend this one, they will be.
508
+
509
+ def active_for_magic_link_authentication?
510
+ super && first_name_alice?
511
+ end
512
+
513
+ def magic_link_inactive_message
514
+ first_name_alice? ? super : :first_name_not_alice_magic_link
515
+ end
516
+ end
517
+ ```
518
+
519
+ ```yaml
520
+ devise:
521
+ failure:
522
+ first_name_not_alice_magic_link: "Sorry, only Alices may log in using magic links. Try password login instead."
523
+ ```
524
+
525
+ ## Compatibility with other Devise strategies
219
526
 
220
527
  If using the `:rememberable` strategy for "remember me" functionality, you'll need to add a `remember_token` column to your resource, as by default that strategy assumes you're using a password auth strategy and relies on comparing the password's salt to validate cookies:
221
528
 
@@ -227,6 +534,54 @@ end
227
534
 
228
535
  If using the `:confirmable` strategy, you may want to override the default Devise behavior of requiring a fresh login after email confirmation (e.g. [this](https://stackoverflow.com/a/39010334/215168) or [this](https://stackoverflow.com/a/25865526/215168) approach). Otherwise, users will have to get a fresh login link after confirming their email, which makes little sense if they just confirmed they own the email address.
229
536
 
537
+ ## Hotwire/Turbo support
538
+
539
+ If you're using Hotwire/Turbo, be sure that you're on Devise >= 4.9 and that you're
540
+ setting the `config.responder` config value in your Devise initializer to appropriate
541
+ values.
542
+
543
+ See the [Devise 4.9 Turbo upgrade guide][] for more info.
544
+
545
+ [Devise 4.9 Turbo upgrade guide]: https://github.com/heartcombo/devise/wiki/How-To:-Upgrade-to-Devise-4.9.0-%5BHotwire-Turbo-integration%5D
546
+
547
+ ## ActiveJob support
548
+
549
+ If you want to use ActiveJob to send magic link emails asynchronously through
550
+ a queuing backend, you can accomplish it the same way you
551
+ [enable this functionality in any Devise install][devise-activejob]:
552
+
553
+ ```ruby
554
+ class User
555
+ def send_devise_notification(notification, *args)
556
+ devise_mailer.send(notification, self, *args).deliver_later
557
+ end
558
+ end
559
+ ```
560
+
561
+ [devise-activejob]: https://github.com/heartcombo/devise/blob/main/README.md#activejob-integration
562
+
563
+ ## Rails logs security
564
+
565
+ Rails's default configuration filters `:token` parameters out of request logs (and
566
+ `Devise::Passwordless` will issue a warning if it detects the configuration doesn't). So request
567
+ logs shouldn't link magic link tokens.
568
+
569
+ However, there are some other default Rails logging behaviors that may cause plaintext magic
570
+ link tokens to leak into log files:
571
+
572
+ 1. Action Mailer logs the entire contents of all outgoing emails to the DEBUG level. Magic link tokens delivered to users in email will be leaked.
573
+ 2. Active Job logs all arguments to every enqueued job at the INFO level. If you configure Devise to use `deliver_later` to send passwordless emails, magic link tokens will be leaked.
574
+
575
+ Rails sets the production logger level to INFO by default. Consider changing your production logger level to WARN if you wish to prevent tokens from being leaked into your logs. In `config/environments/production.rb`:
576
+
577
+ ```ruby
578
+ config.log_level = :warn
579
+ ```
580
+
581
+ (Partially adapted from the [Devise guide on password reset tokens][], which this section also applies to)
582
+
583
+ [Devise guide on password reset tokens]: https://github.com/heartcombo/devise/blob/main/README.md#password-reset-tokens-and-rails-logs
584
+
230
585
  ## Alternatives
231
586
 
232
587
  Other Ruby libraries that offer passwordless authentication: