passwordless 0.12.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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