passwordless 0.10.0 → 0.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/README.md +64 -8
- data/app/controllers/passwordless/sessions_controller.rb +10 -8
- data/app/mailers/passwordless/mailer.rb +1 -2
- data/app/models/passwordless/session.rb +12 -6
- data/app/views/passwordless/sessions/new.html.erb +2 -2
- data/config/routes.rb +4 -4
- data/db/migrate/20171104221735_create_passwordless_sessions.rb +8 -7
- data/lib/generators/passwordless/views_generator.rb +15 -0
- data/lib/passwordless/controller_helpers.rb +16 -10
- data/lib/passwordless/engine.rb +4 -2
- data/lib/passwordless/errors.rb +4 -2
- data/lib/passwordless/model_helpers.rb +3 -1
- data/lib/passwordless/router_helpers.rb +4 -2
- data/lib/passwordless/test_helpers.rb +43 -0
- data/lib/passwordless/version.rb +2 -1
- data/lib/passwordless.rb +1 -0
- metadata +7 -6
- data/app/views/passwordless/sessions/create.html.erb +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b90e8f97825d92f0728154737c428d39cfecbedc9e02bbe6948d0861dd5e9c39
|
4
|
+
data.tar.gz: 2a5b288bf8c16004c6ec8fe7b8937e9f72496475e6e9af4e6a70c32a7a1d05dc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2350958cc2cb4628a6242a6c86ef08b3962fef2ba12fd9ed5a1bf8727f9254fc61fb77e9fa946bb015ebfc98eb81563d1e56aeb1c40d1cbc2ef96e66af512de9
|
7
|
+
data.tar.gz: 5fed7a3d7541a302fa9d6fc802bd530002846ea8cc4dae2057d03fdd799d66263752c766578bd96a441ab584d539739fbe55a63f7e258c5a7e1b16fdecb9eb7a
|
data/README.md
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
<br />
|
5
5
|
</p>
|
6
6
|
|
7
|
-
[](https://github.com/mikker/passwordless/actions/workflows/ci.yml) [](https://rubygems.org/gems/passwordless) [](https://codecov.io/gh/mikker/passwordless)
|
8
8
|
|
9
9
|
Add authentication to your Rails app without all the icky-ness of passwords.
|
10
10
|
|
@@ -26,6 +26,8 @@ Add authentication to your Rails app without all the icky-ness of passwords.
|
|
26
26
|
* [Token and Session Expiry](#token-and-session-expiry)
|
27
27
|
* [Redirecting back after sign-in](#redirecting-back-after-sign-in)
|
28
28
|
* [Claiming tokens](#claiming-tokens)
|
29
|
+
* [Supporting UUID primary keys](#supporting-uuid-primary-keys)
|
30
|
+
* [Testing helpers](#testing-helpers)
|
29
31
|
* [E-mail security](#e-mail-security)
|
30
32
|
* [License](#license)
|
31
33
|
|
@@ -48,7 +50,7 @@ $ bin/rails passwordless:install:migrations
|
|
48
50
|
|
49
51
|
Passwordless creates a single model called `Passwordless::Session`. It doesn't come with its own `User` model, it expects you to create one:
|
50
52
|
|
51
|
-
```
|
53
|
+
```sh
|
52
54
|
$ bin/rails generate model User email
|
53
55
|
```
|
54
56
|
|
@@ -56,7 +58,10 @@ Then specify which field on your `User` record is the email field with:
|
|
56
58
|
|
57
59
|
```ruby
|
58
60
|
class User < ApplicationRecord
|
59
|
-
validates :email,
|
61
|
+
validates :email,
|
62
|
+
presence: true,
|
63
|
+
uniqueness: { case_sensitive: false },
|
64
|
+
format: { with: URI::MailTo::EMAIL_REGEXP }
|
60
65
|
|
61
66
|
passwordless_with :email # <-- here!
|
62
67
|
end
|
@@ -111,7 +116,9 @@ end
|
|
111
116
|
|
112
117
|
### Providing your own templates
|
113
118
|
|
114
|
-
Override `passwordless`' bundled views by adding your own.
|
119
|
+
Override `passwordless`' bundled views by adding your own. You can manually copy the specific views that you need or copy them to your application with `rails generate passwordless:views`.
|
120
|
+
|
121
|
+
`passwordless` has 2 action views and 1 mailer view:
|
115
122
|
|
116
123
|
```sh
|
117
124
|
# the form where the user inputs their email address
|
@@ -131,6 +138,8 @@ If you'd like to let the user know whether or not a record was found, `@resource
|
|
131
138
|
<% end %>
|
132
139
|
```
|
133
140
|
|
141
|
+
Please note that, from a security standpoint, this is a **bad practice** because you'd be giving information about which users are registered on your system. It is recommended to use a single message similar to the default one: "If we found you in the system, we've sent you an email". The **best practice** is to never expose which emails are registered on your system.
|
142
|
+
|
134
143
|
See [the bundled views](https://github.com/mikker/passwordless/tree/master/app/views/passwordless).
|
135
144
|
|
136
145
|
### Registering new users
|
@@ -177,7 +186,7 @@ By default, magic link will send by email. You can customize this method. For ex
|
|
177
186
|
|
178
187
|
config/initializers/passwordless.rb
|
179
188
|
|
180
|
-
```
|
189
|
+
```ruby
|
181
190
|
Passwordless.after_session_save = lambda do |session, request|
|
182
191
|
# Default behavior is
|
183
192
|
# Passwordless::Mailer.magic_link(session).deliver_now
|
@@ -195,7 +204,7 @@ Currently there is not an officially supported way to generate your own magic li
|
|
195
204
|
|
196
205
|
However, you can accomplish this with the following snippet of code.
|
197
206
|
|
198
|
-
```
|
207
|
+
```ruby
|
199
208
|
session = Passwordless::Session.new({
|
200
209
|
authenticatable: @manager,
|
201
210
|
user_agent: 'Command Line',
|
@@ -244,6 +253,9 @@ Passwordless.redirect_back_after_sign_in = true # When enabled the user will be
|
|
244
253
|
Passwordless.expires_at = lambda { 1.year.from_now } # How long until a passwordless session expires.
|
245
254
|
Passwordless.timeout_at = lambda { 1.hour.from_now } # How long until a magic link expires.
|
246
255
|
|
256
|
+
# redirection session behavior
|
257
|
+
Passwordless.redirect_to_response_options = {} # any allowed response_options for redirect_to can go in here
|
258
|
+
|
247
259
|
# Default redirection paths
|
248
260
|
Passwordless.success_redirect_path = '/' # When a user succeeds in logging in.
|
249
261
|
Passwordless.failure_redirect_path = '/' # When a a login is failed for any reason.
|
@@ -319,7 +331,7 @@ config/initializers/passwordless.rb
|
|
319
331
|
Passwordless.restrict_token_reuse = true
|
320
332
|
```
|
321
333
|
|
322
|
-
#### Upgrading an existing Rails app
|
334
|
+
#### Upgrading an existing Rails app to use claim token
|
323
335
|
|
324
336
|
The simplest way to update your sessions table is with a single migration:
|
325
337
|
|
@@ -340,11 +352,55 @@ end
|
|
340
352
|
```
|
341
353
|
</details>
|
342
354
|
|
355
|
+
### Supporting UUID primary keys
|
356
|
+
|
357
|
+
If your `users` table uses UUIDs for its primary keys, you will need to add a migration
|
358
|
+
to change the type of `passwordless`' `authenticatable_id` field to match your primary key type (this will also involve dropping and recreating associated indices).
|
359
|
+
|
360
|
+
Here is an example migration you can use:
|
361
|
+
```ruby
|
362
|
+
class SupportUuidInPasswordlessSessions < ActiveRecord::Migration[6.0]
|
363
|
+
def change
|
364
|
+
remove_index :passwordless_sessions, column: [:authenticatable_type, :authenticatable_id] if index_exists? :authenticatable_type, :authenticatable_id
|
365
|
+
remove_column :passwordless_sessions, :authenticatable_id
|
366
|
+
add_column :passwordless_sessions, :authenticatable_id, :uuid
|
367
|
+
add_index :passwordless_sessions, [:authenticatable_type, :authenticatable_id], name: 'authenticatable'
|
368
|
+
end
|
369
|
+
end
|
370
|
+
```
|
371
|
+
|
372
|
+
Alternatively, you can use `add_reference` with `type: :uuid` in your migration (see docs [here](https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-add_reference)).
|
373
|
+
|
374
|
+
## Testing helpers
|
375
|
+
|
376
|
+
To help with testing, a set of test helpers are provided.
|
377
|
+
|
378
|
+
If you are using RSpec, add the following line to your `spec/rails_helper.rb` or
|
379
|
+
`spec/spec_helper.rb` if `rails_helper.rb` does not exist:
|
380
|
+
|
381
|
+
```ruby
|
382
|
+
require "passwordless/test_helpers"
|
383
|
+
```
|
384
|
+
|
385
|
+
If you are using TestUnit, add this line to your `test/test_helper.rb`:
|
386
|
+
|
387
|
+
```ruby
|
388
|
+
require "passwordless/test_helpers"
|
389
|
+
```
|
390
|
+
|
391
|
+
|
392
|
+
Then in your controller, request, and system tests/specs, you can utilize the following methods:
|
393
|
+
|
394
|
+
```ruby
|
395
|
+
passwordless_sign_in(user) # signs you in as a user
|
396
|
+
passwordless_sign_out # signs out user
|
397
|
+
```
|
398
|
+
|
343
399
|
## E-mail security
|
344
400
|
|
345
401
|
There's no reason that this approach should be less secure than the usual username/password combo. In fact this is most often a more secure option, as users don't get to choose the weak passwords they still use. In a way this is just the same as having each user go through "Forgot password" on every login.
|
346
402
|
|
347
|
-
But be aware that when everyone authenticates via emails you send, the way you send those mails becomes a weak spot. Email services usually provide a log of all the mails you send so if your app's account is compromised, every user in the system is as well. (This is the same for "Forgot password".) [Reddit was compromised](https://thenextweb.com/hardfork/2018/01/05/reddit-bitcoin-cash-hack/) using this method.
|
403
|
+
But be aware that when everyone authenticates via emails you send, the way you send those mails becomes a weak spot. Email services usually provide a log of all the mails you send so if your app's account is compromised, every user in the system is as well. (This is the same for "Forgot password".) [Reddit was compromised](https://thenextweb.com/hardfork/2018/01/05/reddit-bitcoin-cash-stolen-hack/) using this method.
|
348
404
|
|
349
405
|
Ideally you should set up your email provider to not log these mails. And be sure to turn on 2-factor auth if your provider supports it.
|
350
406
|
|
@@ -17,7 +17,7 @@ module Passwordless
|
|
17
17
|
|
18
18
|
# post '/sign_in'
|
19
19
|
# Creates a new Session record then sends the magic link
|
20
|
-
#
|
20
|
+
# redirects to sign in page with generic flash message.
|
21
21
|
# @see Mailer#magic_link Mailer#magic_link
|
22
22
|
def create
|
23
23
|
@resource = find_authenticatable
|
@@ -31,7 +31,8 @@ module Passwordless
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
-
|
34
|
+
flash[:notice] = I18n.t('passwordless.sessions.create.email_sent_if_record_found')
|
35
|
+
redirect_to(sign_in_path)
|
35
36
|
end
|
36
37
|
|
37
38
|
# get '/sign_in/:token'
|
@@ -42,24 +43,25 @@ module Passwordless
|
|
42
43
|
# @see ControllerHelpers#save_passwordless_redirect_location!
|
43
44
|
def show
|
44
45
|
# Make it "slow" on purpose to make brute-force attacks more of a hassle
|
46
|
+
redirect_to_options = Passwordless.redirect_to_response_options.dup
|
45
47
|
BCrypt::Password.create(params[:token])
|
46
|
-
sign_in
|
48
|
+
sign_in(passwordless_session)
|
47
49
|
|
48
|
-
redirect_to
|
50
|
+
redirect_to(passwordless_success_redirect_path, redirect_to_options)
|
49
51
|
rescue Errors::TokenAlreadyClaimedError
|
50
52
|
flash[:error] = I18n.t(".passwordless.sessions.create.token_claimed")
|
51
|
-
redirect_to
|
53
|
+
redirect_to(passwordless_failure_redirect_path, redirect_to_options)
|
52
54
|
rescue Errors::SessionTimedOutError
|
53
55
|
flash[:error] = I18n.t(".passwordless.sessions.create.session_expired")
|
54
|
-
redirect_to
|
56
|
+
redirect_to(passwordless_failure_redirect_path, redirect_to_options)
|
55
57
|
end
|
56
58
|
|
57
59
|
# match '/sign_out', via: %i[get delete].
|
58
60
|
# Signs user out. Redirects to root_path
|
59
61
|
# @see ControllerHelpers#sign_out
|
60
62
|
def destroy
|
61
|
-
sign_out
|
62
|
-
redirect_to
|
63
|
+
sign_out(authenticatable_class)
|
64
|
+
redirect_to(passwordless_sign_out_redirect_path, Passwordless.redirect_to_response_options.dup)
|
63
65
|
end
|
64
66
|
|
65
67
|
protected
|
@@ -10,8 +10,7 @@ module Passwordless
|
|
10
10
|
def magic_link(session)
|
11
11
|
@session = session
|
12
12
|
|
13
|
-
@magic_link = send(Passwordless.mounted_as)
|
14
|
-
.token_sign_in_url(session.token)
|
13
|
+
@magic_link = send(Passwordless.mounted_as).token_sign_in_url(session.token)
|
15
14
|
|
16
15
|
email_field = @session.authenticatable.class.passwordless_email_field
|
17
16
|
mail(
|
@@ -4,10 +4,13 @@ module Passwordless
|
|
4
4
|
# The session responsible for holding the connection between the record
|
5
5
|
# trying to log in and the unique tokens.
|
6
6
|
class Session < ApplicationRecord
|
7
|
-
belongs_to
|
8
|
-
|
7
|
+
belongs_to(
|
8
|
+
:authenticatable,
|
9
|
+
polymorphic: true,
|
10
|
+
inverse_of: :passwordless_sessions
|
11
|
+
)
|
9
12
|
|
10
|
-
validates
|
13
|
+
validates(
|
11
14
|
:authenticatable,
|
12
15
|
:timeout_at,
|
13
16
|
:expires_at,
|
@@ -15,16 +18,19 @@ module Passwordless
|
|
15
18
|
:remote_addr,
|
16
19
|
:token,
|
17
20
|
presence: true
|
21
|
+
)
|
18
22
|
|
19
23
|
before_validation :set_defaults
|
20
24
|
|
21
|
-
scope
|
22
|
-
|
23
|
-
|
25
|
+
scope(
|
26
|
+
:available,
|
27
|
+
lambda { where("expires_at > ?", Time.current) }
|
28
|
+
)
|
24
29
|
|
25
30
|
def self.valid
|
26
31
|
available
|
27
32
|
end
|
33
|
+
|
28
34
|
class << self
|
29
35
|
deprecate :valid, deprecator: SessionValidDeprecation
|
30
36
|
end
|
@@ -1,5 +1,5 @@
|
|
1
|
-
<%=
|
1
|
+
<%= form_with model: @session, url: send(Passwordless.mounted_as).sign_in_path, data: { turbo: 'false' } do |f| %>
|
2
2
|
<% email_field_name = :"passwordless[#{@email_field}]" %>
|
3
|
-
<%= text_field_tag email_field_name, params.fetch(email_field_name, nil) %>
|
3
|
+
<%= text_field_tag email_field_name, params.fetch(email_field_name, nil), required: true %>
|
4
4
|
<%= f.submit I18n.t('passwordless.sessions.new.submit') %>
|
5
5
|
<% end %>
|
data/config/routes.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
Passwordless::Engine.routes.draw do
|
4
|
-
get
|
5
|
-
post
|
6
|
-
get
|
7
|
-
match
|
4
|
+
get("/sign_in", to: "sessions#new", as: :sign_in)
|
5
|
+
post("/sign_in", to: "sessions#create")
|
6
|
+
get("/sign_in/:token", to: "sessions#show", as: :token_sign_in)
|
7
|
+
match("/sign_out", to: "sessions#destroy", via: %i[get delete], as: :sign_out)
|
8
8
|
end
|
@@ -2,18 +2,19 @@
|
|
2
2
|
|
3
3
|
class CreatePasswordlessSessions < ActiveRecord::Migration[5.1]
|
4
4
|
def change
|
5
|
-
create_table
|
5
|
+
create_table(:passwordless_sessions) do |t|
|
6
6
|
t.belongs_to(
|
7
7
|
:authenticatable,
|
8
8
|
polymorphic: true,
|
9
9
|
index: {name: "authenticatable"}
|
10
10
|
)
|
11
|
-
|
12
|
-
t.datetime
|
13
|
-
t.datetime :
|
14
|
-
t.
|
15
|
-
t.
|
16
|
-
t.string
|
11
|
+
|
12
|
+
t.datetime(:timeout_at, null: false)
|
13
|
+
t.datetime(:expires_at, null: false)
|
14
|
+
t.datetime(:claimed_at)
|
15
|
+
t.text(:user_agent, null: false)
|
16
|
+
t.string(:remote_addr, null: false)
|
17
|
+
t.string(:token, null: false)
|
17
18
|
|
18
19
|
t.timestamps
|
19
20
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
|
3
|
+
module Passwordless
|
4
|
+
module Generators
|
5
|
+
class ViewsGenerator < Rails::Generators::Base
|
6
|
+
source_root File.expand_path('../../../app/views/passwordless', __dir__)
|
7
|
+
|
8
|
+
def install
|
9
|
+
copy_file 'mailer/magic_link.text.erb', 'app/views/passwordless/mailer/magic_link.text.erb'
|
10
|
+
copy_file 'sessions/new.html.erb', 'app/views/passwordless/sessions/new.html.erb'
|
11
|
+
copy_file 'sessions/create.html.erb', 'app/views/passwordless/sessions/create.html.erb'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -39,6 +39,7 @@ module Passwordless
|
|
39
39
|
|
40
40
|
authenticate_by_session(authenticatable_class)
|
41
41
|
end
|
42
|
+
|
42
43
|
deprecate :authenticate_by_cookie, deprecator: CookieDeprecation
|
43
44
|
|
44
45
|
def upgrade_passwordless_cookie(authenticatable_class)
|
@@ -51,7 +52,7 @@ module Passwordless
|
|
51
52
|
return unless (record = authenticatable_class.find_by(id: authenticatable_id))
|
52
53
|
new_session = build_passwordless_session(record).tap { |s| s.save! }
|
53
54
|
|
54
|
-
sign_in
|
55
|
+
sign_in(new_session)
|
55
56
|
|
56
57
|
new_session.authenticatable
|
57
58
|
end
|
@@ -73,20 +74,25 @@ module Passwordless
|
|
73
74
|
# to sign in
|
74
75
|
# @return [ActiveRecord::Base] the record that is passed in.
|
75
76
|
def sign_in(record)
|
76
|
-
passwordless_session =
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
77
|
+
passwordless_session = if record.is_a?(Passwordless::Session)
|
78
|
+
record
|
79
|
+
else
|
80
|
+
warn(
|
81
|
+
"Passwordless::ControllerHelpers#sign_in with authenticatable " \
|
81
82
|
"(`#{record.class}') is deprecated. Falling back to creating a " \
|
82
83
|
"new Passwordless::Session"
|
83
|
-
|
84
|
-
|
84
|
+
)
|
85
|
+
build_passwordless_session(record).tap { |s| s.save! }
|
86
|
+
end
|
85
87
|
|
86
88
|
passwordless_session.claim! if Passwordless.restrict_token_reuse
|
87
89
|
|
88
90
|
raise Passwordless::Errors::SessionTimedOutError if passwordless_session.timed_out?
|
89
91
|
|
92
|
+
old_session = session.dup.to_hash
|
93
|
+
reset_session if defined?(reset_session) # allow usage outside controllers
|
94
|
+
old_session.each_pair { |k, v| session[k.to_sym] = v }
|
95
|
+
|
90
96
|
key = session_key(passwordless_session.authenticatable_type)
|
91
97
|
session[key] = passwordless_session.id
|
92
98
|
|
@@ -105,9 +111,9 @@ module Passwordless
|
|
105
111
|
key = cookie_name(authenticatable_class)
|
106
112
|
cookies.encrypted.permanent[key] = {value: nil}
|
107
113
|
cookies.delete(key)
|
108
|
-
# /deprecated
|
109
114
|
|
110
|
-
|
115
|
+
# /deprecated
|
116
|
+
reset_session if defined?(reset_session) # allow usage outside controllers
|
111
117
|
true
|
112
118
|
end
|
113
119
|
|
data/lib/passwordless/engine.rb
CHANGED
@@ -7,15 +7,17 @@ module Passwordless
|
|
7
7
|
|
8
8
|
config.to_prepare do
|
9
9
|
require "passwordless/router_helpers"
|
10
|
+
|
10
11
|
ActionDispatch::Routing::Mapper.include RouterHelpers
|
11
12
|
require "passwordless/model_helpers"
|
13
|
+
|
12
14
|
ActiveRecord::Base.extend ModelHelpers
|
13
15
|
require "passwordless/controller_helpers"
|
16
|
+
|
14
17
|
end
|
15
18
|
|
16
19
|
config.before_initialize do |app|
|
17
|
-
app.config.i18n.load_path +=
|
18
|
-
Dir[Engine.root.join("config", "locales", "*.yml")]
|
20
|
+
app.config.i18n.load_path += Dir[Engine.root.join("config", "locales", "*.yml")]
|
19
21
|
end
|
20
22
|
end
|
21
23
|
end
|
data/lib/passwordless/errors.rb
CHANGED
@@ -3,9 +3,11 @@
|
|
3
3
|
module Passwordless
|
4
4
|
module Errors
|
5
5
|
# Raise this exception when a session is expired.
|
6
|
-
class SessionTimedOutError < StandardError
|
6
|
+
class SessionTimedOutError < StandardError
|
7
|
+
end
|
7
8
|
|
8
9
|
# Raise this exception when the token has been previously claimed
|
9
|
-
class TokenAlreadyClaimedError < StandardError
|
10
|
+
class TokenAlreadyClaimedError < StandardError
|
11
|
+
end
|
10
12
|
end
|
11
13
|
end
|
@@ -8,9 +8,11 @@ module Passwordless
|
|
8
8
|
# field name (e.g. `:email`)
|
9
9
|
# @param field [string] email submitted by user.
|
10
10
|
def passwordless_with(field)
|
11
|
-
has_many
|
11
|
+
has_many(
|
12
|
+
:passwordless_sessions,
|
12
13
|
class_name: "Passwordless::Session",
|
13
14
|
as: :authenticatable
|
15
|
+
)
|
14
16
|
|
15
17
|
define_singleton_method(:passwordless_email_field) { field }
|
16
18
|
end
|
@@ -20,8 +20,10 @@ module Passwordless
|
|
20
20
|
mount_at = at || resource.to_s
|
21
21
|
mount_as = as || resource.to_s
|
22
22
|
mount(
|
23
|
-
Passwordless::Engine,
|
24
|
-
|
23
|
+
Passwordless::Engine,
|
24
|
+
at: mount_at,
|
25
|
+
as: mount_as,
|
26
|
+
defaults: {authenticatable: resource.to_s.singularize}
|
25
27
|
)
|
26
28
|
|
27
29
|
Passwordless.mounted_as = mount_as
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Passwordless
|
2
|
+
module TestHelpers
|
3
|
+
module TestCase
|
4
|
+
def passwordless_sign_out
|
5
|
+
delete Passwordless::Engine.routes.url_helpers.sign_out_path
|
6
|
+
follow_redirect!
|
7
|
+
end
|
8
|
+
|
9
|
+
def passwordless_sign_in(resource)
|
10
|
+
session = Passwordless::Session.create!(authenticatable: resource, user_agent: "TestAgent", remote_addr: "unknown")
|
11
|
+
get Passwordless::Engine.routes.url_helpers.token_sign_in_path(session.token)
|
12
|
+
follow_redirect!
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module SystemTestCase
|
17
|
+
def passwordless_sign_out
|
18
|
+
visit Passwordless::Engine.routes.url_helpers.sign_out_path
|
19
|
+
end
|
20
|
+
|
21
|
+
def passwordless_sign_in(resource)
|
22
|
+
session = Passwordless::Session.create!(authenticatable: resource, user_agent: "TestAgent", remote_addr: "unknown")
|
23
|
+
visit Passwordless::Engine.routes.url_helpers.token_sign_in_path(session.token)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
if defined?(ActiveSupport::TestCase)
|
30
|
+
ActiveSupport::TestCase.send(:include, ::Passwordless::TestHelpers::TestCase)
|
31
|
+
end
|
32
|
+
|
33
|
+
if defined?(ActionDispatch::SystemTestCase)
|
34
|
+
ActionDispatch::SystemTestCase.send(:include, ::Passwordless::TestHelpers::SystemTestCase)
|
35
|
+
end
|
36
|
+
|
37
|
+
if defined?(RSpec)
|
38
|
+
RSpec.configure do |config|
|
39
|
+
config.include ::Passwordless::TestHelpers::TestCase, type: :request
|
40
|
+
config.include ::Passwordless::TestHelpers::TestCase, type: :controller
|
41
|
+
config.include ::Passwordless::TestHelpers::SystemTestCase, type: :system
|
42
|
+
end
|
43
|
+
end
|
data/lib/passwordless/version.rb
CHANGED
data/lib/passwordless.rb
CHANGED
@@ -16,6 +16,7 @@ module Passwordless
|
|
16
16
|
|
17
17
|
mattr_accessor(:expires_at) { lambda { 1.year.from_now } }
|
18
18
|
mattr_accessor(:timeout_at) { lambda { 1.hour.from_now } }
|
19
|
+
mattr_accessor(:redirect_to_response_options) { {} }
|
19
20
|
mattr_accessor(:success_redirect_path) { "/" }
|
20
21
|
mattr_accessor(:failure_redirect_path) { "/" }
|
21
22
|
mattr_accessor(:sign_out_redirect_path) { "/" }
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: passwordless
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.12.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mikkel Malmberg
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-06-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -42,14 +42,14 @@ dependencies:
|
|
42
42
|
name: sqlite3
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - "
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: 1.4.1
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - "
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: 1.4.1
|
55
55
|
- !ruby/object:Gem::Dependency
|
@@ -96,17 +96,18 @@ files:
|
|
96
96
|
- app/models/passwordless/application_record.rb
|
97
97
|
- app/models/passwordless/session.rb
|
98
98
|
- app/views/passwordless/mailer/magic_link.text.erb
|
99
|
-
- app/views/passwordless/sessions/create.html.erb
|
100
99
|
- app/views/passwordless/sessions/new.html.erb
|
101
100
|
- config/locales/en.yml
|
102
101
|
- config/routes.rb
|
103
102
|
- db/migrate/20171104221735_create_passwordless_sessions.rb
|
103
|
+
- lib/generators/passwordless/views_generator.rb
|
104
104
|
- lib/passwordless.rb
|
105
105
|
- lib/passwordless/controller_helpers.rb
|
106
106
|
- lib/passwordless/engine.rb
|
107
107
|
- lib/passwordless/errors.rb
|
108
108
|
- lib/passwordless/model_helpers.rb
|
109
109
|
- lib/passwordless/router_helpers.rb
|
110
|
+
- lib/passwordless/test_helpers.rb
|
110
111
|
- lib/passwordless/url_safe_base_64_generator.rb
|
111
112
|
- lib/passwordless/version.rb
|
112
113
|
homepage: https://github.com/mikker/passwordless
|
@@ -128,7 +129,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
128
129
|
- !ruby/object:Gem::Version
|
129
130
|
version: '0'
|
130
131
|
requirements: []
|
131
|
-
rubygems_version: 3.
|
132
|
+
rubygems_version: 3.4.14
|
132
133
|
signing_key:
|
133
134
|
specification_version: 4
|
134
135
|
summary: Add authentication to your app without all the ickyness of passwords.
|
@@ -1 +0,0 @@
|
|
1
|
-
<p><%= I18n.t('passwordless.sessions.create.email_sent_if_record_found') %></p>
|