passwordless 1.2.0 → 1.3.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: b88192b582d0e4f8cb601b00f2cf5d51250dbe9f747d6413728cf2e5da7ddd1b
4
- data.tar.gz: 054c733891aa4e4f98a1f684b6d6107414b4214589f76ec5aa20c7337a5b2098
3
+ metadata.gz: 2242a4b95f1a99d5be1b889539dc6dd9f1eda711ce616f3b090f12dd68337254
4
+ data.tar.gz: afd9ea1fd2d3b3f15f10a4772d231c53adfd524e22de00d833a9183b34d69397
5
5
  SHA512:
6
- metadata.gz: a760c9c2ade52b80be4a482abb17a2ff9c71579d07bc55eb1268980a39f69540d354bf1988bb4a7ea35d08e801042d9ff266cc2c98913b7ea5c331a1556b882c
7
- data.tar.gz: f66c443aa783a9f490dac97e4a9720ce077594e4d170ad10053bd42168033ca5c57e1b4f20288e0b5a7bd63eaac67b90b8cd52ba80ac8b9a290d2de2bfb2078e
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`. It doesn't come with its own `User` model, it expects you to create one:
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
- ```sh
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
- validates :email,
39
- presence: true,
40
- uniqueness: { case_sensitive: false },
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
- passwordless_with :email # <-- here!
38
+ # more of your code..
44
39
  end
45
40
  ```
46
41
 
47
- Finally, mount the engine in your routes:
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 < ApplicationController
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
- if Passwordless.config.after_session_save.arity == 2
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(email)
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
- authenticatable_class.where("lower(#{email_field}) = ?", email).first
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 from: Passwordless.config.default_from_address
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
- t("passwordless.sessions.new.email.label"),
5
- for: "passwordless_#{email_field}" %>
4
+ t("passwordless.sessions.new.email.label"),
5
+ for: "passwordless_#{email_field}" %>
6
6
  <%= email_field_tag email_field_name,
7
- params.fetch(email_field_name, nil),
8
- required: true,
9
- autofocus: true,
10
- placeholder: t("passwordless.sessions.new.email.placeholder") %>
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, autocomplete: "off" %>
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 %>
@@ -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)
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Passwordless
4
4
  # :nodoc:
5
- VERSION = "1.2.0"
5
+ VERSION = "1.3.0"
6
6
  end
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.2.0
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: 2023-12-05 00:00:00.000000000 Z
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.4.22
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.