passwordless 1.2.0 → 1.3.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 +10 -24
- data/app/controllers/passwordless/sessions_controller.rb +33 -17
- data/app/mailers/passwordless/mailer.rb +3 -2
- data/app/views/passwordless/sessions/new.html.erb +6 -6
- data/app/views/passwordless/sessions/show.html.erb +5 -2
- data/lib/passwordless/config.rb +3 -0
- data/lib/passwordless/test_helpers.rb +5 -4
- data/lib/passwordless/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2242a4b95f1a99d5be1b889539dc6dd9f1eda711ce616f3b090f12dd68337254
|
4
|
+
data.tar.gz: afd9ea1fd2d3b3f15f10a4772d231c53adfd524e22de00d833a9183b34d69397
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9b2ceb4c68972744e10ac6dd56e2e863b5e229bb31053b7159a16c020a2cfa55ffe1affcbef92ca52ee03de79d4d675cb5b3f4340e1c888e064e9ab246f3e455
|
7
|
+
data.tar.gz: 7dd102d25dd4cbb60ab73d30732f78a6cd1d6757e345e3b304c9b1abe4394b6b1d883c1368bd7eaa12ecee986ecdfabf3d82cea081048694807388fc5a1bfd69
|
data/README.md
CHANGED
@@ -25,26 +25,21 @@ See [Upgrading to Passwordless 1.0](docs/upgrading_to_1_0.md) for more details.
|
|
25
25
|
|
26
26
|
## Usage
|
27
27
|
|
28
|
-
Passwordless creates a single model called `Passwordless::Session
|
28
|
+
Passwordless creates a single model called `Passwordless::Session`, so it doesn't come with its own user model. Instead, it expects you to provide one, with an email field in place. If you don't yet have a user model, check out the wiki on [creating the user model](https://github.com/mikker/passwordless/wiki/Creating-the-user-model).
|
29
29
|
|
30
|
-
|
31
|
-
$ bin/rails generate model User email
|
32
|
-
```
|
33
|
-
|
34
|
-
Then specify which field on your `User` record is the email field with:
|
30
|
+
Enable Passwordless on your user model by pointing it to the email field:
|
35
31
|
|
36
32
|
```ruby
|
37
33
|
class User < ApplicationRecord
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
format: { with: URI::MailTo::EMAIL_REGEXP }
|
34
|
+
# your other code..
|
35
|
+
|
36
|
+
passwordless_with :email # <-- here! this needs to be a column in `users` table
|
42
37
|
|
43
|
-
|
38
|
+
# more of your code..
|
44
39
|
end
|
45
40
|
```
|
46
41
|
|
47
|
-
|
42
|
+
Then mount the engine in your routes:
|
48
43
|
|
49
44
|
```ruby
|
50
45
|
Rails.application.routes.draw do
|
@@ -163,6 +158,7 @@ The default values are shown below. It's recommended to only include the ones th
|
|
163
158
|
```ruby
|
164
159
|
Passwordless.configure do |config|
|
165
160
|
config.default_from_address = "CHANGE_ME@example.com"
|
161
|
+
config.parent_controller = "ApplicationController"
|
166
162
|
config.parent_mailer = "ActionMailer::Base"
|
167
163
|
config.restrict_token_reuse = false # Can a token/link be used multiple times?
|
168
164
|
config.token_generator = Passwordless::ShortTokenGenerator.new # Used to generate magic link tokens.
|
@@ -175,6 +171,8 @@ Passwordless.configure do |config|
|
|
175
171
|
config.success_redirect_path = '/' # After a user successfully signs in
|
176
172
|
config.failure_redirect_path = '/' # After a sign in fails
|
177
173
|
config.sign_out_redirect_path = '/' # After a user signs out
|
174
|
+
|
175
|
+
config.paranoid = false # Display email sent notice even when the resource is not found.
|
178
176
|
end
|
179
177
|
```
|
180
178
|
|
@@ -260,18 +258,6 @@ class User < ApplicationRecord
|
|
260
258
|
end
|
261
259
|
```
|
262
260
|
|
263
|
-
### Claiming tokens
|
264
|
-
|
265
|
-
By default, a token/magic link **can** be used more than once.
|
266
|
-
|
267
|
-
To change, in `config/initializers/passwordless.rb`:
|
268
|
-
|
269
|
-
```ruby
|
270
|
-
Passwordless.configure do |config|
|
271
|
-
config.restrict_token_reuse = true
|
272
|
-
end
|
273
|
-
```
|
274
|
-
|
275
261
|
## Test helpers
|
276
262
|
|
277
263
|
To help with testing, a set of test helpers are provided.
|
@@ -4,7 +4,7 @@ require "bcrypt"
|
|
4
4
|
|
5
5
|
module Passwordless
|
6
6
|
# Controller for managing Passwordless sessions
|
7
|
-
class SessionsController <
|
7
|
+
class SessionsController < Passwordless.config.parent_controller.constantize
|
8
8
|
include ControllerHelpers
|
9
9
|
|
10
10
|
helper_method :email_field
|
@@ -20,21 +20,11 @@ module Passwordless
|
|
20
20
|
# Creates a new Session record then sends the magic link
|
21
21
|
# redirects to sign in page with generic flash message.
|
22
22
|
def create
|
23
|
-
unless @resource = find_authenticatable
|
24
|
-
raise(
|
25
|
-
ActiveRecord::RecordNotFound,
|
26
|
-
"Couldn't find #{authenticatable_type} with email #{passwordless_session_params[email_field]}"
|
27
|
-
)
|
28
|
-
end
|
29
|
-
|
23
|
+
handle_resource_not_found unless @resource = find_authenticatable
|
30
24
|
@session = build_passwordless_session(@resource)
|
31
25
|
|
32
26
|
if @session.save
|
33
|
-
|
34
|
-
Passwordless.config.after_session_save.call(@session, request)
|
35
|
-
else
|
36
|
-
Passwordless.config.after_session_save.call(@session)
|
37
|
-
end
|
27
|
+
call_after_session_save
|
38
28
|
|
39
29
|
redirect_to(
|
40
30
|
Passwordless.context.path_for(
|
@@ -50,6 +40,8 @@ module Passwordless
|
|
50
40
|
end
|
51
41
|
|
52
42
|
rescue ActiveRecord::RecordNotFound
|
43
|
+
@session = Session.new
|
44
|
+
|
53
45
|
flash[:error] = I18n.t("passwordless.sessions.create.not_found")
|
54
46
|
render(:new, status: :not_found)
|
55
47
|
end
|
@@ -173,12 +165,36 @@ module Passwordless
|
|
173
165
|
end
|
174
166
|
|
175
167
|
def find_authenticatable
|
176
|
-
email = passwordless_session_params[email_field].downcase.strip
|
177
|
-
|
178
168
|
if authenticatable_class.respond_to?(:fetch_resource_for_passwordless)
|
179
|
-
authenticatable_class.fetch_resource_for_passwordless(
|
169
|
+
authenticatable_class.fetch_resource_for_passwordless(normalized_email_param)
|
170
|
+
else
|
171
|
+
authenticatable_class.where("lower(#{email_field}) = ?", normalized_email_param).first
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def normalized_email_param
|
176
|
+
passwordless_session_params[email_field].downcase.strip
|
177
|
+
end
|
178
|
+
|
179
|
+
def handle_resource_not_found
|
180
|
+
if Passwordless.config.paranoid
|
181
|
+
@resource = authenticatable_class.new(email: normalized_email_param)
|
182
|
+
@skip_after_session_save_callback = true
|
183
|
+
else
|
184
|
+
raise(
|
185
|
+
ActiveRecord::RecordNotFound,
|
186
|
+
"Couldn't find #{authenticatable_type} with email #{normalized_email_param}"
|
187
|
+
)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def call_after_session_save
|
192
|
+
return if @skip_after_session_save_callback
|
193
|
+
|
194
|
+
if Passwordless.config.after_session_save.arity == 2
|
195
|
+
Passwordless.config.after_session_save.call(@session, request)
|
180
196
|
else
|
181
|
-
|
197
|
+
Passwordless.config.after_session_save.call(@session)
|
182
198
|
end
|
183
199
|
end
|
184
200
|
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module Passwordless
|
4
4
|
# The mailer responsible for sending Passwordless' mails.
|
5
5
|
class Mailer < Passwordless.config.parent_mailer.constantize
|
6
|
-
default
|
6
|
+
default(from: Passwordless.config.default_from_address) if Passwordless.config.default_from_address
|
7
7
|
|
8
8
|
# Sends a token and a magic link
|
9
9
|
#
|
@@ -17,7 +17,8 @@ module Passwordless
|
|
17
17
|
session,
|
18
18
|
action: "confirm",
|
19
19
|
id: session.to_param,
|
20
|
-
token: @token
|
20
|
+
token: @token,
|
21
|
+
**default_url_options
|
21
22
|
)
|
22
23
|
|
23
24
|
email_field = session.authenticatable.class.passwordless_email_field
|
@@ -1,12 +1,12 @@
|
|
1
1
|
<%= form_with(model: @session, url: url_for(action: 'new'), data: { turbo: 'false' }) do |f| %>
|
2
2
|
<% email_field_name = :"passwordless[#{email_field}]" %>
|
3
3
|
<%= f.label email_field_name,
|
4
|
-
|
5
|
-
|
4
|
+
t("passwordless.sessions.new.email.label"),
|
5
|
+
for: "passwordless_#{email_field}" %>
|
6
6
|
<%= email_field_tag email_field_name,
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
params.fetch(email_field_name, nil),
|
8
|
+
required: true,
|
9
|
+
autofocus: true,
|
10
|
+
placeholder: t("passwordless.sessions.new.email.placeholder") %>
|
11
11
|
<%= f.submit t("passwordless.sessions.new.submit") %>
|
12
12
|
<% end %>
|
@@ -1,5 +1,8 @@
|
|
1
1
|
<%= form_with(model: @session, url: url_for(action: 'update'), scope: 'passwordless', method: 'patch', data: { turbo: false }) do |f| %>
|
2
|
-
<%= f.label :token
|
3
|
-
<%= f.text_field :token
|
2
|
+
<%= f.label :token %>
|
3
|
+
<%= f.text_field :token,
|
4
|
+
required: true,
|
5
|
+
autofocus: true,
|
6
|
+
autocomplete: "one-time-code" %>
|
4
7
|
<%= f.submit t(".confirm") %>
|
5
8
|
<% end %>
|
data/lib/passwordless/config.rb
CHANGED
@@ -28,6 +28,7 @@ module Passwordless
|
|
28
28
|
include Options
|
29
29
|
|
30
30
|
option :default_from_address, default: "CHANGE_ME@example.com"
|
31
|
+
option :parent_controller, default: "ApplicationController"
|
31
32
|
option :parent_mailer, default: "ActionMailer::Base"
|
32
33
|
option :restrict_token_reuse, default: true
|
33
34
|
option :token_generator, default: ShortTokenGenerator.new
|
@@ -48,6 +49,8 @@ module Passwordless
|
|
48
49
|
end
|
49
50
|
)
|
50
51
|
|
52
|
+
option :paranoid, default: false
|
53
|
+
|
51
54
|
def initialize
|
52
55
|
set_defaults!
|
53
56
|
end
|
@@ -43,21 +43,22 @@ module Passwordless
|
|
43
43
|
end
|
44
44
|
|
45
45
|
module SystemTestCase
|
46
|
-
def passwordless_sign_out(cls = nil)
|
46
|
+
def passwordless_sign_out(cls = nil, only_path: false)
|
47
47
|
cls ||= "User".constantize
|
48
48
|
resource = cls.model_name.to_s.tableize
|
49
49
|
|
50
|
-
visit(Passwordless.context.url_for(resource, action: "destroy"))
|
50
|
+
visit(Passwordless.context.url_for(resource, action: "destroy", only_path: only_path))
|
51
51
|
end
|
52
52
|
|
53
|
-
def passwordless_sign_in(resource)
|
53
|
+
def passwordless_sign_in(resource, only_path: false)
|
54
54
|
session = Passwordless::Session.create!(authenticatable: resource)
|
55
55
|
|
56
56
|
magic_link = Passwordless.context.url_for(
|
57
57
|
session,
|
58
58
|
action: "confirm",
|
59
59
|
id: session.to_param,
|
60
|
-
token: session.token
|
60
|
+
token: session.token,
|
61
|
+
only_path: only_path
|
61
62
|
)
|
62
63
|
|
63
64
|
visit(magic_link)
|
data/lib/passwordless/version.rb
CHANGED
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: 1.
|
4
|
+
version: 1.3.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: 2024-01-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -91,7 +91,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
91
91
|
- !ruby/object:Gem::Version
|
92
92
|
version: '0'
|
93
93
|
requirements: []
|
94
|
-
rubygems_version: 3.
|
94
|
+
rubygems_version: 3.5.5
|
95
95
|
signing_key:
|
96
96
|
specification_version: 4
|
97
97
|
summary: Add authentication to your app without all the ickyness of passwords.
|