passwordless 0.12.0 → 1.0.0.beta1

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.
@@ -7,71 +7,108 @@ module Passwordless
7
7
  class SessionsController < ApplicationController
8
8
  include ControllerHelpers
9
9
 
10
- # get '/sign_in'
10
+ helper_method :email_field
11
+
12
+ # get '/:resource/sign_in'
11
13
  # Assigns an email_field and new Session to be used by new view.
12
14
  # renders sessions/new.html.erb.
13
15
  def new
14
- @email_field = email_field
15
16
  @session = Session.new
16
17
  end
17
18
 
18
- # post '/sign_in'
19
+ # post '/:resource/sign_in'
19
20
  # Creates a new Session record then sends the magic link
20
21
  # redirects to sign in page with generic flash message.
21
- # @see Mailer#magic_link Mailer#magic_link
22
22
  def create
23
- @resource = find_authenticatable
24
- session = build_passwordless_session(@resource)
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
+
30
+ @session = build_passwordless_session(@resource)
25
31
 
26
- if session.save
27
- if Passwordless.after_session_save.arity == 2
28
- Passwordless.after_session_save.call(session, request)
32
+ if @session.save
33
+ if Passwordless.config.after_session_save.arity == 2
34
+ Passwordless.config.after_session_save.call(@session, request)
29
35
  else
30
- Passwordless.after_session_save.call(session)
36
+ Passwordless.config.after_session_save.call(@session)
31
37
  end
38
+
39
+ redirect_to(
40
+ url_for(id: @session.id, action: "show"),
41
+ flash: {notice: I18n.t("passwordless.sessions.create.email_sent")}
42
+ )
43
+ else
44
+ flash[:error] = I18n.t("passwordless.sessions.create.error")
45
+ render(:new, status: :unprocessable_entity)
32
46
  end
33
47
 
34
- flash[:notice] = I18n.t('passwordless.sessions.create.email_sent_if_record_found')
35
- redirect_to(sign_in_path)
48
+ rescue ActiveRecord::RecordNotFound
49
+ flash[:error] = I18n.t("passwordless.sessions.create.not_found")
50
+ render(:new, status: :not_found)
36
51
  end
37
52
 
38
- # get '/sign_in/:token'
53
+ # get "/:resource/sign_in/:id"
54
+ # Shows the form for confirming a Session record.
55
+ # renders sessions/show.html.erb.
56
+ def show
57
+ @session = find_session
58
+ end
59
+
60
+ # patch "/:resource/sign_in/:id"
61
+ # User submits the form for confirming a Session record.
39
62
  # Looks up session record by provided token. Signs in user if a match
40
63
  # is found. Redirects to either the user's original destination
41
- # or _root_path_
64
+ # or _Passwordless.config.success_redirect_path_.
65
+ #
42
66
  # @see ControllerHelpers#sign_in
43
67
  # @see ControllerHelpers#save_passwordless_redirect_location!
44
- def show
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
47
- BCrypt::Password.create(params[:token])
48
- sign_in(passwordless_session)
68
+ def update
69
+ @session = find_session
49
70
 
50
- redirect_to(passwordless_success_redirect_path, redirect_to_options)
51
- rescue Errors::TokenAlreadyClaimedError
52
- flash[:error] = I18n.t(".passwordless.sessions.create.token_claimed")
53
- redirect_to(passwordless_failure_redirect_path, redirect_to_options)
54
- rescue Errors::SessionTimedOutError
55
- flash[:error] = I18n.t(".passwordless.sessions.create.session_expired")
56
- redirect_to(passwordless_failure_redirect_path, redirect_to_options)
71
+ artificially_slow_down_brute_force_attacks(passwordless_session_params[:token])
72
+
73
+ authenticate_and_sign_in(@session, passwordless_session_params[:token])
74
+ end
75
+
76
+ # get "/:resource/sign_in/:id/:token"
77
+ # User visits the link sent to them via email.
78
+ # Looks up session record by provided token. Signs in user if a match
79
+ # is found. Redirects to either the user's original destination
80
+ # or _Passwordless.config.success_redirect_path_.
81
+ #
82
+ # @see ControllerHelpers#sign_in
83
+ # @see ControllerHelpers#save_passwordless_redirect_location!
84
+ def confirm
85
+ # Some email clients will visit links in emails to check if they are
86
+ # safe. We don't want to sign in the user in that case.
87
+ return head(:ok) if request.head?
88
+
89
+ @session = find_session
90
+
91
+ artificially_slow_down_brute_force_attacks(params[:token])
92
+
93
+ authenticate_and_sign_in(@session, params[:token])
57
94
  end
58
95
 
59
- # match '/sign_out', via: %i[get delete].
96
+ # match '/:resource/sign_out', via: %i[get delete].
60
97
  # Signs user out. Redirects to root_path
61
98
  # @see ControllerHelpers#sign_out
62
99
  def destroy
63
100
  sign_out(authenticatable_class)
64
- redirect_to(passwordless_sign_out_redirect_path, Passwordless.redirect_to_response_options.dup)
101
+ redirect_to(passwordless_sign_out_redirect_path, Passwordless.config.redirect_to_response_options.dup)
65
102
  end
66
103
 
67
104
  protected
68
105
 
69
106
  def passwordless_sign_out_redirect_path
70
- Passwordless.sign_out_redirect_path
107
+ Passwordless.config.sign_out_redirect_path
71
108
  end
72
109
 
73
110
  def passwordless_failure_redirect_path
74
- Passwordless.failure_redirect_path
111
+ Passwordless.config.failure_redirect_path
75
112
  end
76
113
 
77
114
  def passwordless_query_redirect_path
@@ -82,32 +119,54 @@ module Passwordless
82
119
  end
83
120
 
84
121
  def passwordless_success_redirect_path
85
- return Passwordless.success_redirect_path unless Passwordless.redirect_back_after_sign_in
122
+ return Passwordless.config.success_redirect_path unless Passwordless.config.redirect_back_after_sign_in
86
123
 
87
124
  session_redirect_url = reset_passwordless_redirect_location!(authenticatable_class)
88
- passwordless_query_redirect_path || session_redirect_url || Passwordless.success_redirect_path
125
+ passwordless_query_redirect_path || session_redirect_url || Passwordless.config.success_redirect_path
89
126
  end
90
127
 
91
128
  private
92
129
 
130
+ def artificially_slow_down_brute_force_attacks(token)
131
+ # Make it "slow" on purpose to make brute-force attacks more of a hassle
132
+ BCrypt::Password.create(token)
133
+ end
134
+
135
+ def authenticate_and_sign_in(session, token)
136
+ if session.authenticate(token)
137
+ sign_in(session)
138
+ redirect_to(passwordless_success_redirect_path, status: :see_other, **redirect_to_options)
139
+ else
140
+ flash[:error] = I18n.t("passwordless.sessions.errors.invalid_token")
141
+ render(status: :forbidden, action: "show")
142
+ end
143
+
144
+ rescue Errors::TokenAlreadyClaimedError
145
+ flash[:error] = I18n.t("passwordless.sessions.errors.token_claimed")
146
+ redirect_to(passwordless_failure_redirect_path, status: :see_other, **redirect_to_options)
147
+ rescue Errors::SessionTimedOutError
148
+ flash[:error] = I18n.t("passwordless.sessions.errors.session_expired")
149
+ redirect_to(passwordless_failure_redirect_path, status: :see_other, **redirect_to_options)
150
+ end
151
+
93
152
  def authenticatable
94
153
  params.fetch(:authenticatable)
95
154
  end
96
155
 
97
- def authenticatable_classname
156
+ def authenticatable_type
98
157
  authenticatable.to_s.camelize
99
158
  end
100
159
 
101
160
  def authenticatable_class
102
- authenticatable_classname.constantize
161
+ authenticatable_type.constantize
103
162
  end
104
163
 
105
- def email_field
106
- authenticatable_class.passwordless_email_field
164
+ def find_session
165
+ Session.find_by!(id: params[:id], authenticatable_type: authenticatable_type)
107
166
  end
108
167
 
109
168
  def find_authenticatable
110
- email = params[:passwordless][email_field].downcase.strip
169
+ email = passwordless_session_params[email_field].downcase.strip
111
170
 
112
171
  if authenticatable_class.respond_to?(:fetch_resource_for_passwordless)
113
172
  authenticatable_class.fetch_resource_for_passwordless(email)
@@ -116,11 +175,34 @@ module Passwordless
116
175
  end
117
176
  end
118
177
 
178
+ def email_field
179
+ authenticatable_class.passwordless_email_field
180
+ rescue NoMethodError => e
181
+ raise(
182
+ MissingEmailFieldError,
183
+ <<~MSG
184
+ undefined method `passwordless_email_field' for #{authenticatable_type}
185
+
186
+ Remember to add something like `passwordless_with :email` to you model
187
+ MSG
188
+ .strip_heredoc,
189
+ caller[1..-1]
190
+ )
191
+ end
192
+
193
+ def redirect_to_options
194
+ @redirect_to_options ||= (Passwordless.config.redirect_to_response_options.dup || {})
195
+ end
196
+
119
197
  def passwordless_session
120
198
  @passwordless_session ||= Session.find_by!(
121
- authenticatable_type: authenticatable_classname,
122
- token: params[:token]
199
+ id: params[:id],
200
+ authenticatable_type: authenticatable_type
123
201
  )
124
202
  end
203
+
204
+ def passwordless_session_params
205
+ params.require(:passwordless).permit(:token, authenticatable_class.passwordless_email_field)
206
+ end
125
207
  end
126
208
  end
@@ -2,20 +2,22 @@
2
2
 
3
3
  module Passwordless
4
4
  # The mailer responsible for sending Passwordless' mails.
5
- class Mailer < Passwordless.parent_mailer.constantize
6
- default from: Passwordless.default_from_address
5
+ class Mailer < Passwordless.config.parent_mailer.constantize
6
+ default from: Passwordless.config.default_from_address
7
7
 
8
- # Sends a magic link (secret token) email.
9
- # @param session [Session] A Passwordless Session
10
- def magic_link(session)
11
- @session = session
8
+ # Sends a token and a magic link
9
+ #
10
+ # @param session [Session] An instance of Passwordless::Session
11
+ # @param token [String] The token in plaintext. Falls back to `session.token` hoping it
12
+ # is still in memory (optional)
13
+ def sign_in(session, token = nil)
14
+ @token = token || session.token
15
+ @magic_link = send(:"confirm_#{session.authenticatable_type.tableize}_sign_in_url", session, token)
16
+ email_field = session.authenticatable.class.passwordless_email_field
12
17
 
13
- @magic_link = send(Passwordless.mounted_as).token_sign_in_url(session.token)
14
-
15
- email_field = @session.authenticatable.class.passwordless_email_field
16
18
  mail(
17
- to: @session.authenticatable.send(email_field),
18
- subject: I18n.t("passwordless.mailer.subject")
19
+ to: session.authenticatable.send(email_field),
20
+ subject: I18n.t("passwordless.mailer.sign_in.subject")
19
21
  )
20
22
  end
21
23
  end
@@ -4,6 +4,8 @@ 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
+ self.table_name = "passwordless_sessions"
8
+
7
9
  belongs_to(
8
10
  :authenticatable,
9
11
  polymorphic: true,
@@ -14,9 +16,7 @@ module Passwordless
14
16
  :authenticatable,
15
17
  :timeout_at,
16
18
  :expires_at,
17
- :user_agent,
18
- :remote_addr,
19
- :token,
19
+ :token_digest,
20
20
  presence: true
21
21
  )
22
22
 
@@ -27,12 +27,17 @@ module Passwordless
27
27
  lambda { where("expires_at > ?", Time.current) }
28
28
  )
29
29
 
30
- def self.valid
31
- available
30
+ # save the token in memory so we can put it in emails but only save the
31
+ # hashed version in the database
32
+ attr_reader :token
33
+
34
+ def token=(plaintext)
35
+ self.token_digest = Passwordless.digest(plaintext)
36
+ @token = (plaintext)
32
37
  end
33
38
 
34
- class << self
35
- deprecate :valid, deprecator: SessionValidDeprecation
39
+ def authenticate(token)
40
+ token_digest == Passwordless.digest(token)
36
41
  end
37
42
 
38
43
  def expired?
@@ -58,12 +63,20 @@ module Passwordless
58
63
 
59
64
  private
60
65
 
66
+ def token_digest_available?(token_digest)
67
+ Session.available.where(token_digest: token_digest).none?
68
+ end
69
+
61
70
  def set_defaults
62
- self.expires_at ||= Passwordless.expires_at.call
63
- self.timeout_at ||= Passwordless.timeout_at.call
64
- self.token ||= loop {
65
- token = Passwordless.token_generator.call(self)
66
- break token unless Session.find_by(token: token)
71
+ self.expires_at ||= Passwordless.config.expires_at.call
72
+ self.timeout_at ||= Passwordless.config.timeout_at.call
73
+
74
+ return if self.token_digest
75
+
76
+ self.token, self.token_digest = loop {
77
+ token = Passwordless.config.token_generator.call(self)
78
+ digest = Passwordless.digest(token)
79
+ break [token, digest] if token_digest_available?(digest)
67
80
  }
68
81
  end
69
82
  end
@@ -0,0 +1 @@
1
+ <%= t("passwordless.mailer.sign_in.body", token: @token, magic_link: @magic_link) %>
@@ -1,5 +1,9 @@
1
- <%= form_with model: @session, url: send(Passwordless.mounted_as).sign_in_path, data: { turbo: 'false' } do |f| %>
2
- <% email_field_name = :"passwordless[#{@email_field}]" %>
3
- <%= text_field_tag email_field_name, params.fetch(email_field_name, nil), required: true %>
4
- <%= f.submit I18n.t('passwordless.sessions.new.submit') %>
1
+ <%= form_with(model: @session, url: url_for(action: 'new'), data: { turbo: 'false' }) do |f| %>
2
+ <% email_field_name = :"passwordless[#{email_field}]" %>
3
+ <%= f.label email_field_name, t("passwordless.sessions.new.email.label") %>
4
+ <%= text_field_tag email_field_name,
5
+ params.fetch(email_field_name, nil),
6
+ required: true,
7
+ placeholder: t("passwordless.sessions.new.email.placeholder") %>
8
+ <%= f.submit t("passwordless.sessions.new.submit") %>
5
9
  <% end %>
@@ -0,0 +1,5 @@
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 %>
4
+ <%= f.submit "Confirm" %>
5
+ <% end %>
@@ -2,12 +2,24 @@
2
2
  en:
3
3
  passwordless:
4
4
  sessions:
5
+ new:
6
+ email:
7
+ label: "E-mail address"
8
+ placeholder: "user@example.com"
9
+ submit: "Sign in"
5
10
  create:
6
- session_expired: 'Your session has expired, please sign in again.'
7
- email_sent_if_record_found: "If we found you in the system, we've sent you an email."
11
+ email_sent: "We've sent you an email with a secret token"
12
+ not_found: "We couldn't find a user with that email address"
13
+ error: "An error occured"
14
+ errors:
15
+ invalid_token: "Token is invalid"
16
+ session_expired: "Your session has expired, please sign in again."
8
17
  token_claimed: "This link has already been used, try requesting the link again"
9
- new:
10
- submit: 'Send magic link'
11
18
  mailer:
12
- subject: "Your magic link ✨"
13
- magic_link: "Here's your link: %{link}"
19
+ sign_in:
20
+ subject: "Signing in "
21
+ body: |-
22
+ Use this token to complete your sign in: %{token}
23
+
24
+ Alternatively you can use this link to sign in directly:
25
+ %{magic_link}
data/config/routes.rb CHANGED
@@ -1,8 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  Passwordless::Engine.routes.draw do
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
4
  end
@@ -12,9 +12,7 @@ class CreatePasswordlessSessions < ActiveRecord::Migration[5.1]
12
12
  t.datetime(:timeout_at, null: false)
13
13
  t.datetime(:expires_at, null: false)
14
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)
15
+ t.string(:token_digest, null: false)
18
16
 
19
17
  t.timestamps
20
18
  end
@@ -1,14 +1,14 @@
1
- require 'rails/generators'
1
+ require "rails/generators"
2
2
 
3
3
  module Passwordless
4
4
  module Generators
5
5
  class ViewsGenerator < Rails::Generators::Base
6
- source_root File.expand_path('../../../app/views/passwordless', __dir__)
6
+ source_root File.expand_path("../../../app/views/passwordless", __dir__)
7
7
 
8
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'
9
+ copy_file("mailer/sign_in.text.erb", "app/views/passwordless/mailer/sign_in.text.erb")
10
+ copy_file("sessions/new.html.erb", "app/views/passwordless/sessions/new.html.erb")
11
+ copy_file("sessions/show.html.erb", "app/views/passwordless/sessions/show.html.erb")
12
12
  end
13
13
  end
14
14
  end
@@ -0,0 +1,71 @@
1
+ require "passwordless/short_token_generator"
2
+
3
+ module Passwordless
4
+ module Options
5
+ module ClassMethods
6
+ def option(name, default: nil)
7
+ attr_accessor(name)
8
+ schema[name] = default
9
+ end
10
+
11
+ def schema
12
+ @schema ||= {}
13
+ end
14
+ end
15
+
16
+ def set_defaults!
17
+ self.class.schema.each do |name, default|
18
+ instance_variable_set("@#{name}", default)
19
+ end
20
+ end
21
+
22
+ def self.included(cls)
23
+ cls.extend(ClassMethods)
24
+ end
25
+ end
26
+
27
+ class Configuration
28
+ include Options
29
+
30
+ option :default_from_address, default: "CHANGE_ME@example.com"
31
+ option :parent_mailer, default: "ActionMailer::Base"
32
+ option :restrict_token_reuse, default: true
33
+ option :token_generator, default: ShortTokenGenerator.new
34
+
35
+ option :expires_at, default: lambda { 1.year.from_now }
36
+ option :timeout_at, default: lambda { 10.minutes.from_now }
37
+
38
+ option :redirect_back_after_sign_in, default: true
39
+ option :redirect_to_response_options, default: {}
40
+ option :success_redirect_path, default: "/"
41
+ option :failure_redirect_path, default: "/"
42
+ option :sign_out_redirect_path, default: "/"
43
+ option(
44
+ :after_session_save,
45
+ default: lambda do |session, _request|
46
+ Mailer.sign_in(session, session.token).deliver_now
47
+ end
48
+ )
49
+
50
+ def initialize
51
+ set_defaults!
52
+ end
53
+ end
54
+
55
+ module Configurable
56
+ attr_writer :config
57
+
58
+ def config
59
+ @config ||= Configuration.new
60
+ end
61
+
62
+ def configure
63
+ yield(config)
64
+ end
65
+
66
+ def reset_config!
67
+ @config = Configuration.new
68
+ end
69
+ end
70
+
71
+ end
@@ -10,51 +10,33 @@ module Passwordless
10
10
  end
11
11
 
12
12
  # Build a new Passwordless::Session from an _authenticatable_ record.
13
- # Set's `user_agent` and `remote_addr` from Rails' `request`.
14
13
  # @param authenticatable [ActiveRecord::Base] Instance of an
15
14
  # authenticatable Rails model
16
15
  # @return [Session] the new Session object
17
16
  # @see ModelHelpers#passwordless_with
18
17
  def build_passwordless_session(authenticatable)
19
- Session.new.tap do |us|
20
- us.remote_addr = request.remote_addr
21
- us.user_agent = request.env["HTTP_USER_AGENT"]
22
- us.authenticatable = authenticatable
23
- end
18
+ Session.new(authenticatable: authenticatable)
24
19
  end
25
20
 
26
- # @deprecated Use {ControllerHelpers#authenticate_by_session}
27
- # Authenticate a record using cookies. Looks for a cookie corresponding to
28
- # the _authenticatable_class_. If found try to find it in the database.
29
- # @param authenticatable_class [ActiveRecord::Base] any Model connected to
30
- # passwordless. (e.g - _User_ or _Admin_).
31
- # @return [ActiveRecord::Base|nil] an instance of Model found by id stored
32
- # in cookies.encrypted or nil if nothing is found.
21
+ # Create a new Passwordless::Session from an _authenticatable_ record.
22
+ # @param authenticatable [ActiveRecord::Base] Instance of an
23
+ # authenticatable Rails model
24
+ # @return [Session] the new Session object
25
+ # @raise [ActiveRecord::RecordInvalid] if the Session is invalid
33
26
  # @see ModelHelpers#passwordless_with
34
- def authenticate_by_cookie(authenticatable_class)
35
- key = cookie_name(authenticatable_class)
36
- authenticatable_id = cookies.encrypted[key]
37
-
38
- return authenticatable_class.find_by(id: authenticatable_id) if authenticatable_id
39
-
40
- authenticate_by_session(authenticatable_class)
27
+ def create_passwordless_session!(authenticatable)
28
+ Session.create!(authenticatable: authenticatable)
41
29
  end
42
30
 
43
- deprecate :authenticate_by_cookie, deprecator: CookieDeprecation
44
-
45
- def upgrade_passwordless_cookie(authenticatable_class)
46
- key = cookie_name(authenticatable_class)
47
-
48
- return unless (authenticatable_id = cookies.encrypted[key])
49
- cookies.encrypted.permanent[key] = {value: nil}
50
- cookies.delete(key)
51
-
52
- return unless (record = authenticatable_class.find_by(id: authenticatable_id))
53
- new_session = build_passwordless_session(record).tap { |s| s.save! }
54
-
55
- sign_in(new_session)
56
-
57
- new_session.authenticatable
31
+ # Create a new Passwordless::Session from an _authenticatable_ record.
32
+ # @param authenticatable [ActiveRecord::Base] Instance of an
33
+ # authenticatable Rails model
34
+ # @return [Session, nil] the new Session object or nil
35
+ # @see ModelHelpers#passwordless_with
36
+ def create_passwordless_session(authenticatable)
37
+ create_passwordless_session!(authenticatable)
38
+ rescue ActiveRecord::RecordInvalid
39
+ nil
58
40
  end
59
41
 
60
42
  # Authenticate a record using the session. Looks for a session key corresponding to
@@ -65,55 +47,40 @@ module Passwordless
65
47
  # in cookies.encrypted or nil if nothing is found.
66
48
  # @see ModelHelpers#passwordless_with
67
49
  def authenticate_by_session(authenticatable_class)
68
- return unless find_passwordless_session_for(authenticatable_class)&.available?
69
- find_passwordless_session_for(authenticatable_class).authenticatable
50
+ pwless_session = find_passwordless_session_for(authenticatable_class)
51
+ return unless pwless_session&.available?
52
+
53
+ pwless_session.authenticatable
70
54
  end
71
55
 
72
56
  # Signs in session
73
57
  # @param authenticatable [Passwordless::Session] Instance of {Passwordless::Session}
74
58
  # to sign in
75
59
  # @return [ActiveRecord::Base] the record that is passed in.
76
- def sign_in(record)
77
- passwordless_session = if record.is_a?(Passwordless::Session)
78
- record
79
- else
80
- warn(
81
- "Passwordless::ControllerHelpers#sign_in with authenticatable " \
82
- "(`#{record.class}') is deprecated. Falling back to creating a " \
83
- "new Passwordless::Session"
84
- )
85
- build_passwordless_session(record).tap { |s| s.save! }
86
- end
87
-
88
- passwordless_session.claim! if Passwordless.restrict_token_reuse
60
+ def sign_in(passwordless_session)
61
+ passwordless_session.claim! if Passwordless.config.restrict_token_reuse
89
62
 
90
63
  raise Passwordless::Errors::SessionTimedOutError if passwordless_session.timed_out?
91
64
 
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 }
65
+ if defined?(reset_session)
66
+ old_session = session.dup.to_hash
67
+ # allow usage outside controllers
68
+ reset_session
69
+ old_session.each_pair { |k, v| session[k.to_sym] = v }
70
+ end
95
71
 
96
72
  key = session_key(passwordless_session.authenticatable_type)
97
73
  session[key] = passwordless_session.id
98
74
 
99
- if record.is_a?(Passwordless::Session)
100
- passwordless_session
101
- else
102
- passwordless_session.authenticatable
103
- end
75
+ passwordless_session
104
76
  end
105
77
 
106
78
  # Signs out user by deleting the session key.
107
79
  # @param (see #authenticate_by_session)
108
80
  # @return [boolean] Always true
109
81
  def sign_out(authenticatable_class)
110
- # Deprecated - cookies
111
- key = cookie_name(authenticatable_class)
112
- cookies.encrypted.permanent[key] = {value: nil}
113
- cookies.delete(key)
114
-
115
- # /deprecated
116
- reset_session if defined?(reset_session) # allow usage outside controllers
82
+ session.delete(session_key(authenticatable_class))
83
+ reset_session
117
84
  true
118
85
  end
119
86
 
@@ -151,10 +118,5 @@ module Passwordless
151
118
 
152
119
  authenticatable_class.base_class.to_s.parameterize
153
120
  end
154
-
155
- # Deprecated
156
- def cookie_name(authenticatable_class)
157
- :"#{authenticatable_class.base_class.to_s.underscore}_id"
158
- end
159
121
  end
160
122
  end