Module: Clearance::User

Extended by:
ActiveSupport::Concern
Defined in:
lib/clearance/user.rb

Overview

Required to be included in your configued user class, which is User by default, but can be changed with Configuration#user_model=.

class User
  include Clearance::User

  # ...
end

This will also include methods exposed by your password strategy, which can be configured with Configuration#password_strategy=. By default, this is PasswordStrategies::BCrypt.

Validations

These validations are added to the class that the User module is mixed into.

Callbacks

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#confirmation_tokenString

Returns The value used to identify this user in the password reset link.

Returns:

  • (String)

    The value used to identify this user in the password reset link.



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/clearance/user.rb', line 97

module User
  extend ActiveSupport::Concern

  included do
    attr_reader :password

    include Validations
    include Callbacks
    include password_strategy

    def password=(value)
      encrypted_password_will_change!
      super
    end
  end

  # @api private
  module ClassMethods
    def authenticate(email, password)
      if user = find_by_normalized_email(email)
        if password.present? && user.authenticated?(password)
          user
        end
      else
        prevent_timing_attack
      end
    end

    def find_by_normalized_email(email)
      find_by(email: normalize_email(email))
    end

    def normalize_email(email)
      email.to_s.downcase.gsub(/\s+/, "")
    end

    private

    DUMMY_PASSWORD = "*"

    def prevent_timing_attack
      new(password: DUMMY_PASSWORD)
      nil
    end

    def password_strategy
      Clearance.configuration.password_strategy || PasswordStrategies::BCrypt
    end
  end

  # @api private
  module Validations
    extend ActiveSupport::Concern

    included do
      validates :email,
        email: { mode: :strict },
        presence: true,
        uniqueness: { allow_blank: true, case_sensitive: true },
        unless: :email_optional?

      validates :password, presence: true, unless: :skip_password_validation?
    end
  end

  # @api private
  module Callbacks
    extend ActiveSupport::Concern

    included do
      before_validation :normalize_email
      before_create :generate_remember_token
    end
  end

  # Generates a {#confirmation_token} for the user, which allows them to reset
  # their password via an email link.
  #
  # Calling `forgot_password!` will cause the user model to be saved without
  # validations. Any other changes you made to this user instance will also
  # be persisted, without validation. It is inteded to be called on an
  # instance with no changes (`dirty? == false`).
  #
  # @return [Boolean] Was the save successful?
  def forgot_password!
    generate_confirmation_token
    save validate: false
  end

  # Generates a new {#remember_token} for the user, which will have the effect
  # of signing all of the user's current sessions out. This is called
  # internally by {Session#sign_out}.
  #
  # Calling `reset_remember_token!` will cause the user model to be saved
  # without validations. Any other changes you made to this user instance will
  # also be persisted, without validation. It is inteded to be called on an
  # instance with no changes (`dirty? == false`).
  #
  # @return [Boolean] Was the save successful?
  def reset_remember_token!
    generate_remember_token
    save validate: false
  end

  # Sets the user's password to the new value, using the `password=` method on
  # the configured password strategy. By default, this is
  # {PasswordStrategies::BCrypt#password=}.
  #
  # This also has the side-effect of blanking the {#confirmation_token} and
  # rotating the `#remember_token`.
  #
  # Validations will be run as part of this update. If the user instance is
  # not valid, the password change will not be persisted, and this method will
  # return `false`.
  #
  # @return [Boolean] Was the save successful?
  def update_password(new_password)
    self.password = new_password

    if valid?
      self.confirmation_token = nil
      generate_remember_token
    end

    save
  end

  private

  # Sets the email on this instance to the value returned by
  # {.normalize_email}
  #
  # @return [String]
  def normalize_email
    self.email = self.class.normalize_email(email)
  end

  # Always false. Override this method in your user model to allow for other
  # forms of user authentication (username, Facebook, etc).
  #
  # @return [Boolean]
  def email_optional?
    false
  end

  # Always false. Override this method in your user model to allow for other
  # forms of user authentication (username, Facebook, etc).
  #
  # @return [Boolean]
  def password_optional?
    false
  end

  # True if {#password_optional?} is true or if the user already has an
  # {#encrypted_password} that is not changing.
  #
  # @return [Boolean]
  def skip_password_validation?
    password_optional? ||
      (encrypted_password.present? && !encrypted_password_changed?)
  end

  # Sets the {#confirmation_token} on the instance to a new value generated by
  # {Token.new}. The change is not automatically persisted. If you would like
  # to generate and save in a single method call, use {#forgot_password!}.
  #
  # @return [String] The new confirmation token
  def generate_confirmation_token
    self.confirmation_token = Clearance::Token.new
  end

  # Sets the {#remember_token} on the instance to a new value generated by
  # {Token.new}. The change is not automatically persisted. If you would like
  # to generate and save in a single method call, use
  # {#reset_remember_token!}.
  #
  # @return [String] The new remember token
  def generate_remember_token
    self.remember_token = Clearance::Token.new
  end

  private_constant :Callbacks, :ClassMethods, :Validations
end

#emailString

Returns The user's email.

Returns:

  • (String)

    The user's email.



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/clearance/user.rb', line 97

module User
  extend ActiveSupport::Concern

  included do
    attr_reader :password

    include Validations
    include Callbacks
    include password_strategy

    def password=(value)
      encrypted_password_will_change!
      super
    end
  end

  # @api private
  module ClassMethods
    def authenticate(email, password)
      if user = find_by_normalized_email(email)
        if password.present? && user.authenticated?(password)
          user
        end
      else
        prevent_timing_attack
      end
    end

    def find_by_normalized_email(email)
      find_by(email: normalize_email(email))
    end

    def normalize_email(email)
      email.to_s.downcase.gsub(/\s+/, "")
    end

    private

    DUMMY_PASSWORD = "*"

    def prevent_timing_attack
      new(password: DUMMY_PASSWORD)
      nil
    end

    def password_strategy
      Clearance.configuration.password_strategy || PasswordStrategies::BCrypt
    end
  end

  # @api private
  module Validations
    extend ActiveSupport::Concern

    included do
      validates :email,
        email: { mode: :strict },
        presence: true,
        uniqueness: { allow_blank: true, case_sensitive: true },
        unless: :email_optional?

      validates :password, presence: true, unless: :skip_password_validation?
    end
  end

  # @api private
  module Callbacks
    extend ActiveSupport::Concern

    included do
      before_validation :normalize_email
      before_create :generate_remember_token
    end
  end

  # Generates a {#confirmation_token} for the user, which allows them to reset
  # their password via an email link.
  #
  # Calling `forgot_password!` will cause the user model to be saved without
  # validations. Any other changes you made to this user instance will also
  # be persisted, without validation. It is inteded to be called on an
  # instance with no changes (`dirty? == false`).
  #
  # @return [Boolean] Was the save successful?
  def forgot_password!
    generate_confirmation_token
    save validate: false
  end

  # Generates a new {#remember_token} for the user, which will have the effect
  # of signing all of the user's current sessions out. This is called
  # internally by {Session#sign_out}.
  #
  # Calling `reset_remember_token!` will cause the user model to be saved
  # without validations. Any other changes you made to this user instance will
  # also be persisted, without validation. It is inteded to be called on an
  # instance with no changes (`dirty? == false`).
  #
  # @return [Boolean] Was the save successful?
  def reset_remember_token!
    generate_remember_token
    save validate: false
  end

  # Sets the user's password to the new value, using the `password=` method on
  # the configured password strategy. By default, this is
  # {PasswordStrategies::BCrypt#password=}.
  #
  # This also has the side-effect of blanking the {#confirmation_token} and
  # rotating the `#remember_token`.
  #
  # Validations will be run as part of this update. If the user instance is
  # not valid, the password change will not be persisted, and this method will
  # return `false`.
  #
  # @return [Boolean] Was the save successful?
  def update_password(new_password)
    self.password = new_password

    if valid?
      self.confirmation_token = nil
      generate_remember_token
    end

    save
  end

  private

  # Sets the email on this instance to the value returned by
  # {.normalize_email}
  #
  # @return [String]
  def normalize_email
    self.email = self.class.normalize_email(email)
  end

  # Always false. Override this method in your user model to allow for other
  # forms of user authentication (username, Facebook, etc).
  #
  # @return [Boolean]
  def email_optional?
    false
  end

  # Always false. Override this method in your user model to allow for other
  # forms of user authentication (username, Facebook, etc).
  #
  # @return [Boolean]
  def password_optional?
    false
  end

  # True if {#password_optional?} is true or if the user already has an
  # {#encrypted_password} that is not changing.
  #
  # @return [Boolean]
  def skip_password_validation?
    password_optional? ||
      (encrypted_password.present? && !encrypted_password_changed?)
  end

  # Sets the {#confirmation_token} on the instance to a new value generated by
  # {Token.new}. The change is not automatically persisted. If you would like
  # to generate and save in a single method call, use {#forgot_password!}.
  #
  # @return [String] The new confirmation token
  def generate_confirmation_token
    self.confirmation_token = Clearance::Token.new
  end

  # Sets the {#remember_token} on the instance to a new value generated by
  # {Token.new}. The change is not automatically persisted. If you would like
  # to generate and save in a single method call, use
  # {#reset_remember_token!}.
  #
  # @return [String] The new remember token
  def generate_remember_token
    self.remember_token = Clearance::Token.new
  end

  private_constant :Callbacks, :ClassMethods, :Validations
end

#encrypted_passwordString

Returns The user's encrypted password.

Returns:

  • (String)

    The user's encrypted password.



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/clearance/user.rb', line 97

module User
  extend ActiveSupport::Concern

  included do
    attr_reader :password

    include Validations
    include Callbacks
    include password_strategy

    def password=(value)
      encrypted_password_will_change!
      super
    end
  end

  # @api private
  module ClassMethods
    def authenticate(email, password)
      if user = find_by_normalized_email(email)
        if password.present? && user.authenticated?(password)
          user
        end
      else
        prevent_timing_attack
      end
    end

    def find_by_normalized_email(email)
      find_by(email: normalize_email(email))
    end

    def normalize_email(email)
      email.to_s.downcase.gsub(/\s+/, "")
    end

    private

    DUMMY_PASSWORD = "*"

    def prevent_timing_attack
      new(password: DUMMY_PASSWORD)
      nil
    end

    def password_strategy
      Clearance.configuration.password_strategy || PasswordStrategies::BCrypt
    end
  end

  # @api private
  module Validations
    extend ActiveSupport::Concern

    included do
      validates :email,
        email: { mode: :strict },
        presence: true,
        uniqueness: { allow_blank: true, case_sensitive: true },
        unless: :email_optional?

      validates :password, presence: true, unless: :skip_password_validation?
    end
  end

  # @api private
  module Callbacks
    extend ActiveSupport::Concern

    included do
      before_validation :normalize_email
      before_create :generate_remember_token
    end
  end

  # Generates a {#confirmation_token} for the user, which allows them to reset
  # their password via an email link.
  #
  # Calling `forgot_password!` will cause the user model to be saved without
  # validations. Any other changes you made to this user instance will also
  # be persisted, without validation. It is inteded to be called on an
  # instance with no changes (`dirty? == false`).
  #
  # @return [Boolean] Was the save successful?
  def forgot_password!
    generate_confirmation_token
    save validate: false
  end

  # Generates a new {#remember_token} for the user, which will have the effect
  # of signing all of the user's current sessions out. This is called
  # internally by {Session#sign_out}.
  #
  # Calling `reset_remember_token!` will cause the user model to be saved
  # without validations. Any other changes you made to this user instance will
  # also be persisted, without validation. It is inteded to be called on an
  # instance with no changes (`dirty? == false`).
  #
  # @return [Boolean] Was the save successful?
  def reset_remember_token!
    generate_remember_token
    save validate: false
  end

  # Sets the user's password to the new value, using the `password=` method on
  # the configured password strategy. By default, this is
  # {PasswordStrategies::BCrypt#password=}.
  #
  # This also has the side-effect of blanking the {#confirmation_token} and
  # rotating the `#remember_token`.
  #
  # Validations will be run as part of this update. If the user instance is
  # not valid, the password change will not be persisted, and this method will
  # return `false`.
  #
  # @return [Boolean] Was the save successful?
  def update_password(new_password)
    self.password = new_password

    if valid?
      self.confirmation_token = nil
      generate_remember_token
    end

    save
  end

  private

  # Sets the email on this instance to the value returned by
  # {.normalize_email}
  #
  # @return [String]
  def normalize_email
    self.email = self.class.normalize_email(email)
  end

  # Always false. Override this method in your user model to allow for other
  # forms of user authentication (username, Facebook, etc).
  #
  # @return [Boolean]
  def email_optional?
    false
  end

  # Always false. Override this method in your user model to allow for other
  # forms of user authentication (username, Facebook, etc).
  #
  # @return [Boolean]
  def password_optional?
    false
  end

  # True if {#password_optional?} is true or if the user already has an
  # {#encrypted_password} that is not changing.
  #
  # @return [Boolean]
  def skip_password_validation?
    password_optional? ||
      (encrypted_password.present? && !encrypted_password_changed?)
  end

  # Sets the {#confirmation_token} on the instance to a new value generated by
  # {Token.new}. The change is not automatically persisted. If you would like
  # to generate and save in a single method call, use {#forgot_password!}.
  #
  # @return [String] The new confirmation token
  def generate_confirmation_token
    self.confirmation_token = Clearance::Token.new
  end

  # Sets the {#remember_token} on the instance to a new value generated by
  # {Token.new}. The change is not automatically persisted. If you would like
  # to generate and save in a single method call, use
  # {#reset_remember_token!}.
  #
  # @return [String] The new remember token
  def generate_remember_token
    self.remember_token = Clearance::Token.new
  end

  private_constant :Callbacks, :ClassMethods, :Validations
end

#passwordString (readonly)

Returns Transient (non-persisted) attribute that is set when updating a user's password. Only the #encrypted_password is persisted.

Returns:

  • (String)

    Transient (non-persisted) attribute that is set when updating a user's password. Only the #encrypted_password is persisted.



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/clearance/user.rb', line 97

module User
  extend ActiveSupport::Concern

  included do
    attr_reader :password

    include Validations
    include Callbacks
    include password_strategy

    def password=(value)
      encrypted_password_will_change!
      super
    end
  end

  # @api private
  module ClassMethods
    def authenticate(email, password)
      if user = find_by_normalized_email(email)
        if password.present? && user.authenticated?(password)
          user
        end
      else
        prevent_timing_attack
      end
    end

    def find_by_normalized_email(email)
      find_by(email: normalize_email(email))
    end

    def normalize_email(email)
      email.to_s.downcase.gsub(/\s+/, "")
    end

    private

    DUMMY_PASSWORD = "*"

    def prevent_timing_attack
      new(password: DUMMY_PASSWORD)
      nil
    end

    def password_strategy
      Clearance.configuration.password_strategy || PasswordStrategies::BCrypt
    end
  end

  # @api private
  module Validations
    extend ActiveSupport::Concern

    included do
      validates :email,
        email: { mode: :strict },
        presence: true,
        uniqueness: { allow_blank: true, case_sensitive: true },
        unless: :email_optional?

      validates :password, presence: true, unless: :skip_password_validation?
    end
  end

  # @api private
  module Callbacks
    extend ActiveSupport::Concern

    included do
      before_validation :normalize_email
      before_create :generate_remember_token
    end
  end

  # Generates a {#confirmation_token} for the user, which allows them to reset
  # their password via an email link.
  #
  # Calling `forgot_password!` will cause the user model to be saved without
  # validations. Any other changes you made to this user instance will also
  # be persisted, without validation. It is inteded to be called on an
  # instance with no changes (`dirty? == false`).
  #
  # @return [Boolean] Was the save successful?
  def forgot_password!
    generate_confirmation_token
    save validate: false
  end

  # Generates a new {#remember_token} for the user, which will have the effect
  # of signing all of the user's current sessions out. This is called
  # internally by {Session#sign_out}.
  #
  # Calling `reset_remember_token!` will cause the user model to be saved
  # without validations. Any other changes you made to this user instance will
  # also be persisted, without validation. It is inteded to be called on an
  # instance with no changes (`dirty? == false`).
  #
  # @return [Boolean] Was the save successful?
  def reset_remember_token!
    generate_remember_token
    save validate: false
  end

  # Sets the user's password to the new value, using the `password=` method on
  # the configured password strategy. By default, this is
  # {PasswordStrategies::BCrypt#password=}.
  #
  # This also has the side-effect of blanking the {#confirmation_token} and
  # rotating the `#remember_token`.
  #
  # Validations will be run as part of this update. If the user instance is
  # not valid, the password change will not be persisted, and this method will
  # return `false`.
  #
  # @return [Boolean] Was the save successful?
  def update_password(new_password)
    self.password = new_password

    if valid?
      self.confirmation_token = nil
      generate_remember_token
    end

    save
  end

  private

  # Sets the email on this instance to the value returned by
  # {.normalize_email}
  #
  # @return [String]
  def normalize_email
    self.email = self.class.normalize_email(email)
  end

  # Always false. Override this method in your user model to allow for other
  # forms of user authentication (username, Facebook, etc).
  #
  # @return [Boolean]
  def email_optional?
    false
  end

  # Always false. Override this method in your user model to allow for other
  # forms of user authentication (username, Facebook, etc).
  #
  # @return [Boolean]
  def password_optional?
    false
  end

  # True if {#password_optional?} is true or if the user already has an
  # {#encrypted_password} that is not changing.
  #
  # @return [Boolean]
  def skip_password_validation?
    password_optional? ||
      (encrypted_password.present? && !encrypted_password_changed?)
  end

  # Sets the {#confirmation_token} on the instance to a new value generated by
  # {Token.new}. The change is not automatically persisted. If you would like
  # to generate and save in a single method call, use {#forgot_password!}.
  #
  # @return [String] The new confirmation token
  def generate_confirmation_token
    self.confirmation_token = Clearance::Token.new
  end

  # Sets the {#remember_token} on the instance to a new value generated by
  # {Token.new}. The change is not automatically persisted. If you would like
  # to generate and save in a single method call, use
  # {#reset_remember_token!}.
  #
  # @return [String] The new remember token
  def generate_remember_token
    self.remember_token = Clearance::Token.new
  end

  private_constant :Callbacks, :ClassMethods, :Validations
end

#remember_tokenString

Returns The value used to identify this user in their Session cookie.

Returns:

  • (String)

    The value used to identify this user in their Session cookie.



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/clearance/user.rb', line 97

module User
  extend ActiveSupport::Concern

  included do
    attr_reader :password

    include Validations
    include Callbacks
    include password_strategy

    def password=(value)
      encrypted_password_will_change!
      super
    end
  end

  # @api private
  module ClassMethods
    def authenticate(email, password)
      if user = find_by_normalized_email(email)
        if password.present? && user.authenticated?(password)
          user
        end
      else
        prevent_timing_attack
      end
    end

    def find_by_normalized_email(email)
      find_by(email: normalize_email(email))
    end

    def normalize_email(email)
      email.to_s.downcase.gsub(/\s+/, "")
    end

    private

    DUMMY_PASSWORD = "*"

    def prevent_timing_attack
      new(password: DUMMY_PASSWORD)
      nil
    end

    def password_strategy
      Clearance.configuration.password_strategy || PasswordStrategies::BCrypt
    end
  end

  # @api private
  module Validations
    extend ActiveSupport::Concern

    included do
      validates :email,
        email: { mode: :strict },
        presence: true,
        uniqueness: { allow_blank: true, case_sensitive: true },
        unless: :email_optional?

      validates :password, presence: true, unless: :skip_password_validation?
    end
  end

  # @api private
  module Callbacks
    extend ActiveSupport::Concern

    included do
      before_validation :normalize_email
      before_create :generate_remember_token
    end
  end

  # Generates a {#confirmation_token} for the user, which allows them to reset
  # their password via an email link.
  #
  # Calling `forgot_password!` will cause the user model to be saved without
  # validations. Any other changes you made to this user instance will also
  # be persisted, without validation. It is inteded to be called on an
  # instance with no changes (`dirty? == false`).
  #
  # @return [Boolean] Was the save successful?
  def forgot_password!
    generate_confirmation_token
    save validate: false
  end

  # Generates a new {#remember_token} for the user, which will have the effect
  # of signing all of the user's current sessions out. This is called
  # internally by {Session#sign_out}.
  #
  # Calling `reset_remember_token!` will cause the user model to be saved
  # without validations. Any other changes you made to this user instance will
  # also be persisted, without validation. It is inteded to be called on an
  # instance with no changes (`dirty? == false`).
  #
  # @return [Boolean] Was the save successful?
  def reset_remember_token!
    generate_remember_token
    save validate: false
  end

  # Sets the user's password to the new value, using the `password=` method on
  # the configured password strategy. By default, this is
  # {PasswordStrategies::BCrypt#password=}.
  #
  # This also has the side-effect of blanking the {#confirmation_token} and
  # rotating the `#remember_token`.
  #
  # Validations will be run as part of this update. If the user instance is
  # not valid, the password change will not be persisted, and this method will
  # return `false`.
  #
  # @return [Boolean] Was the save successful?
  def update_password(new_password)
    self.password = new_password

    if valid?
      self.confirmation_token = nil
      generate_remember_token
    end

    save
  end

  private

  # Sets the email on this instance to the value returned by
  # {.normalize_email}
  #
  # @return [String]
  def normalize_email
    self.email = self.class.normalize_email(email)
  end

  # Always false. Override this method in your user model to allow for other
  # forms of user authentication (username, Facebook, etc).
  #
  # @return [Boolean]
  def email_optional?
    false
  end

  # Always false. Override this method in your user model to allow for other
  # forms of user authentication (username, Facebook, etc).
  #
  # @return [Boolean]
  def password_optional?
    false
  end

  # True if {#password_optional?} is true or if the user already has an
  # {#encrypted_password} that is not changing.
  #
  # @return [Boolean]
  def skip_password_validation?
    password_optional? ||
      (encrypted_password.present? && !encrypted_password_changed?)
  end

  # Sets the {#confirmation_token} on the instance to a new value generated by
  # {Token.new}. The change is not automatically persisted. If you would like
  # to generate and save in a single method call, use {#forgot_password!}.
  #
  # @return [String] The new confirmation token
  def generate_confirmation_token
    self.confirmation_token = Clearance::Token.new
  end

  # Sets the {#remember_token} on the instance to a new value generated by
  # {Token.new}. The change is not automatically persisted. If you would like
  # to generate and save in a single method call, use
  # {#reset_remember_token!}.
  #
  # @return [String] The new remember token
  def generate_remember_token
    self.remember_token = Clearance::Token.new
  end

  private_constant :Callbacks, :ClassMethods, :Validations
end

Class Method Details

.authenticateUser?

Finds the user with the given email and authenticates them with the provided password. If the email corresponds to a user and the provided password is correct for that user, this method will return that user. Otherwise it will return nil.

Returns:



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/clearance/user.rb', line 97

module User
  extend ActiveSupport::Concern

  included do
    attr_reader :password

    include Validations
    include Callbacks
    include password_strategy

    def password=(value)
      encrypted_password_will_change!
      super
    end
  end

  # @api private
  module ClassMethods
    def authenticate(email, password)
      if user = find_by_normalized_email(email)
        if password.present? && user.authenticated?(password)
          user
        end
      else
        prevent_timing_attack
      end
    end

    def find_by_normalized_email(email)
      find_by(email: normalize_email(email))
    end

    def normalize_email(email)
      email.to_s.downcase.gsub(/\s+/, "")
    end

    private

    DUMMY_PASSWORD = "*"

    def prevent_timing_attack
      new(password: DUMMY_PASSWORD)
      nil
    end

    def password_strategy
      Clearance.configuration.password_strategy || PasswordStrategies::BCrypt
    end
  end

  # @api private
  module Validations
    extend ActiveSupport::Concern

    included do
      validates :email,
        email: { mode: :strict },
        presence: true,
        uniqueness: { allow_blank: true, case_sensitive: true },
        unless: :email_optional?

      validates :password, presence: true, unless: :skip_password_validation?
    end
  end

  # @api private
  module Callbacks
    extend ActiveSupport::Concern

    included do
      before_validation :normalize_email
      before_create :generate_remember_token
    end
  end

  # Generates a {#confirmation_token} for the user, which allows them to reset
  # their password via an email link.
  #
  # Calling `forgot_password!` will cause the user model to be saved without
  # validations. Any other changes you made to this user instance will also
  # be persisted, without validation. It is inteded to be called on an
  # instance with no changes (`dirty? == false`).
  #
  # @return [Boolean] Was the save successful?
  def forgot_password!
    generate_confirmation_token
    save validate: false
  end

  # Generates a new {#remember_token} for the user, which will have the effect
  # of signing all of the user's current sessions out. This is called
  # internally by {Session#sign_out}.
  #
  # Calling `reset_remember_token!` will cause the user model to be saved
  # without validations. Any other changes you made to this user instance will
  # also be persisted, without validation. It is inteded to be called on an
  # instance with no changes (`dirty? == false`).
  #
  # @return [Boolean] Was the save successful?
  def reset_remember_token!
    generate_remember_token
    save validate: false
  end

  # Sets the user's password to the new value, using the `password=` method on
  # the configured password strategy. By default, this is
  # {PasswordStrategies::BCrypt#password=}.
  #
  # This also has the side-effect of blanking the {#confirmation_token} and
  # rotating the `#remember_token`.
  #
  # Validations will be run as part of this update. If the user instance is
  # not valid, the password change will not be persisted, and this method will
  # return `false`.
  #
  # @return [Boolean] Was the save successful?
  def update_password(new_password)
    self.password = new_password

    if valid?
      self.confirmation_token = nil
      generate_remember_token
    end

    save
  end

  private

  # Sets the email on this instance to the value returned by
  # {.normalize_email}
  #
  # @return [String]
  def normalize_email
    self.email = self.class.normalize_email(email)
  end

  # Always false. Override this method in your user model to allow for other
  # forms of user authentication (username, Facebook, etc).
  #
  # @return [Boolean]
  def email_optional?
    false
  end

  # Always false. Override this method in your user model to allow for other
  # forms of user authentication (username, Facebook, etc).
  #
  # @return [Boolean]
  def password_optional?
    false
  end

  # True if {#password_optional?} is true or if the user already has an
  # {#encrypted_password} that is not changing.
  #
  # @return [Boolean]
  def skip_password_validation?
    password_optional? ||
      (encrypted_password.present? && !encrypted_password_changed?)
  end

  # Sets the {#confirmation_token} on the instance to a new value generated by
  # {Token.new}. The change is not automatically persisted. If you would like
  # to generate and save in a single method call, use {#forgot_password!}.
  #
  # @return [String] The new confirmation token
  def generate_confirmation_token
    self.confirmation_token = Clearance::Token.new
  end

  # Sets the {#remember_token} on the instance to a new value generated by
  # {Token.new}. The change is not automatically persisted. If you would like
  # to generate and save in a single method call, use
  # {#reset_remember_token!}.
  #
  # @return [String] The new remember token
  def generate_remember_token
    self.remember_token = Clearance::Token.new
  end

  private_constant :Callbacks, :ClassMethods, :Validations
end

.find_by_normalized_emailUser?

Finds the user with the given email. The email with be normalized via #normalize_email.

Returns:



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/clearance/user.rb', line 97

module User
  extend ActiveSupport::Concern

  included do
    attr_reader :password

    include Validations
    include Callbacks
    include password_strategy

    def password=(value)
      encrypted_password_will_change!
      super
    end
  end

  # @api private
  module ClassMethods
    def authenticate(email, password)
      if user = find_by_normalized_email(email)
        if password.present? && user.authenticated?(password)
          user
        end
      else
        prevent_timing_attack
      end
    end

    def find_by_normalized_email(email)
      find_by(email: normalize_email(email))
    end

    def normalize_email(email)
      email.to_s.downcase.gsub(/\s+/, "")
    end

    private

    DUMMY_PASSWORD = "*"

    def prevent_timing_attack
      new(password: DUMMY_PASSWORD)
      nil
    end

    def password_strategy
      Clearance.configuration.password_strategy || PasswordStrategies::BCrypt
    end
  end

  # @api private
  module Validations
    extend ActiveSupport::Concern

    included do
      validates :email,
        email: { mode: :strict },
        presence: true,
        uniqueness: { allow_blank: true, case_sensitive: true },
        unless: :email_optional?

      validates :password, presence: true, unless: :skip_password_validation?
    end
  end

  # @api private
  module Callbacks
    extend ActiveSupport::Concern

    included do
      before_validation :normalize_email
      before_create :generate_remember_token
    end
  end

  # Generates a {#confirmation_token} for the user, which allows them to reset
  # their password via an email link.
  #
  # Calling `forgot_password!` will cause the user model to be saved without
  # validations. Any other changes you made to this user instance will also
  # be persisted, without validation. It is inteded to be called on an
  # instance with no changes (`dirty? == false`).
  #
  # @return [Boolean] Was the save successful?
  def forgot_password!
    generate_confirmation_token
    save validate: false
  end

  # Generates a new {#remember_token} for the user, which will have the effect
  # of signing all of the user's current sessions out. This is called
  # internally by {Session#sign_out}.
  #
  # Calling `reset_remember_token!` will cause the user model to be saved
  # without validations. Any other changes you made to this user instance will
  # also be persisted, without validation. It is inteded to be called on an
  # instance with no changes (`dirty? == false`).
  #
  # @return [Boolean] Was the save successful?
  def reset_remember_token!
    generate_remember_token
    save validate: false
  end

  # Sets the user's password to the new value, using the `password=` method on
  # the configured password strategy. By default, this is
  # {PasswordStrategies::BCrypt#password=}.
  #
  # This also has the side-effect of blanking the {#confirmation_token} and
  # rotating the `#remember_token`.
  #
  # Validations will be run as part of this update. If the user instance is
  # not valid, the password change will not be persisted, and this method will
  # return `false`.
  #
  # @return [Boolean] Was the save successful?
  def update_password(new_password)
    self.password = new_password

    if valid?
      self.confirmation_token = nil
      generate_remember_token
    end

    save
  end

  private

  # Sets the email on this instance to the value returned by
  # {.normalize_email}
  #
  # @return [String]
  def normalize_email
    self.email = self.class.normalize_email(email)
  end

  # Always false. Override this method in your user model to allow for other
  # forms of user authentication (username, Facebook, etc).
  #
  # @return [Boolean]
  def email_optional?
    false
  end

  # Always false. Override this method in your user model to allow for other
  # forms of user authentication (username, Facebook, etc).
  #
  # @return [Boolean]
  def password_optional?
    false
  end

  # True if {#password_optional?} is true or if the user already has an
  # {#encrypted_password} that is not changing.
  #
  # @return [Boolean]
  def skip_password_validation?
    password_optional? ||
      (encrypted_password.present? && !encrypted_password_changed?)
  end

  # Sets the {#confirmation_token} on the instance to a new value generated by
  # {Token.new}. The change is not automatically persisted. If you would like
  # to generate and save in a single method call, use {#forgot_password!}.
  #
  # @return [String] The new confirmation token
  def generate_confirmation_token
    self.confirmation_token = Clearance::Token.new
  end

  # Sets the {#remember_token} on the instance to a new value generated by
  # {Token.new}. The change is not automatically persisted. If you would like
  # to generate and save in a single method call, use
  # {#reset_remember_token!}.
  #
  # @return [String] The new remember token
  def generate_remember_token
    self.remember_token = Clearance::Token.new
  end

  private_constant :Callbacks, :ClassMethods, :Validations
end

.normalize_emailString

Normalizes the provided email by downcasing and removing all spaces. This is used by find_by_normalized_email and is also called when validating a user to ensure only normalized emails are stored in the database.

Returns:

  • (String)


97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/clearance/user.rb', line 97

module User
  extend ActiveSupport::Concern

  included do
    attr_reader :password

    include Validations
    include Callbacks
    include password_strategy

    def password=(value)
      encrypted_password_will_change!
      super
    end
  end

  # @api private
  module ClassMethods
    def authenticate(email, password)
      if user = find_by_normalized_email(email)
        if password.present? && user.authenticated?(password)
          user
        end
      else
        prevent_timing_attack
      end
    end

    def find_by_normalized_email(email)
      find_by(email: normalize_email(email))
    end

    def normalize_email(email)
      email.to_s.downcase.gsub(/\s+/, "")
    end

    private

    DUMMY_PASSWORD = "*"

    def prevent_timing_attack
      new(password: DUMMY_PASSWORD)
      nil
    end

    def password_strategy
      Clearance.configuration.password_strategy || PasswordStrategies::BCrypt
    end
  end

  # @api private
  module Validations
    extend ActiveSupport::Concern

    included do
      validates :email,
        email: { mode: :strict },
        presence: true,
        uniqueness: { allow_blank: true, case_sensitive: true },
        unless: :email_optional?

      validates :password, presence: true, unless: :skip_password_validation?
    end
  end

  # @api private
  module Callbacks
    extend ActiveSupport::Concern

    included do
      before_validation :normalize_email
      before_create :generate_remember_token
    end
  end

  # Generates a {#confirmation_token} for the user, which allows them to reset
  # their password via an email link.
  #
  # Calling `forgot_password!` will cause the user model to be saved without
  # validations. Any other changes you made to this user instance will also
  # be persisted, without validation. It is inteded to be called on an
  # instance with no changes (`dirty? == false`).
  #
  # @return [Boolean] Was the save successful?
  def forgot_password!
    generate_confirmation_token
    save validate: false
  end

  # Generates a new {#remember_token} for the user, which will have the effect
  # of signing all of the user's current sessions out. This is called
  # internally by {Session#sign_out}.
  #
  # Calling `reset_remember_token!` will cause the user model to be saved
  # without validations. Any other changes you made to this user instance will
  # also be persisted, without validation. It is inteded to be called on an
  # instance with no changes (`dirty? == false`).
  #
  # @return [Boolean] Was the save successful?
  def reset_remember_token!
    generate_remember_token
    save validate: false
  end

  # Sets the user's password to the new value, using the `password=` method on
  # the configured password strategy. By default, this is
  # {PasswordStrategies::BCrypt#password=}.
  #
  # This also has the side-effect of blanking the {#confirmation_token} and
  # rotating the `#remember_token`.
  #
  # Validations will be run as part of this update. If the user instance is
  # not valid, the password change will not be persisted, and this method will
  # return `false`.
  #
  # @return [Boolean] Was the save successful?
  def update_password(new_password)
    self.password = new_password

    if valid?
      self.confirmation_token = nil
      generate_remember_token
    end

    save
  end

  private

  # Sets the email on this instance to the value returned by
  # {.normalize_email}
  #
  # @return [String]
  def normalize_email
    self.email = self.class.normalize_email(email)
  end

  # Always false. Override this method in your user model to allow for other
  # forms of user authentication (username, Facebook, etc).
  #
  # @return [Boolean]
  def email_optional?
    false
  end

  # Always false. Override this method in your user model to allow for other
  # forms of user authentication (username, Facebook, etc).
  #
  # @return [Boolean]
  def password_optional?
    false
  end

  # True if {#password_optional?} is true or if the user already has an
  # {#encrypted_password} that is not changing.
  #
  # @return [Boolean]
  def skip_password_validation?
    password_optional? ||
      (encrypted_password.present? && !encrypted_password_changed?)
  end

  # Sets the {#confirmation_token} on the instance to a new value generated by
  # {Token.new}. The change is not automatically persisted. If you would like
  # to generate and save in a single method call, use {#forgot_password!}.
  #
  # @return [String] The new confirmation token
  def generate_confirmation_token
    self.confirmation_token = Clearance::Token.new
  end

  # Sets the {#remember_token} on the instance to a new value generated by
  # {Token.new}. The change is not automatically persisted. If you would like
  # to generate and save in a single method call, use
  # {#reset_remember_token!}.
  #
  # @return [String] The new remember token
  def generate_remember_token
    self.remember_token = Clearance::Token.new
  end

  private_constant :Callbacks, :ClassMethods, :Validations
end

Instance Method Details

#authenticated?(password) ⇒ Boolean

Check's the provided password against the user's encrypted password using the configured password strategy. By default, this will be PasswordStrategies::BCrypt#authenticated?, but can be changed with Configuration#password_strategy.

Parameters:

  • password (String)

    The password to check.

Returns:

  • (Boolean)

    True if the password provided is correct for the user.

See Also:



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/clearance/user.rb', line 97

module User
  extend ActiveSupport::Concern

  included do
    attr_reader :password

    include Validations
    include Callbacks
    include password_strategy

    def password=(value)
      encrypted_password_will_change!
      super
    end
  end

  # @api private
  module ClassMethods
    def authenticate(email, password)
      if user = find_by_normalized_email(email)
        if password.present? && user.authenticated?(password)
          user
        end
      else
        prevent_timing_attack
      end
    end

    def find_by_normalized_email(email)
      find_by(email: normalize_email(email))
    end

    def normalize_email(email)
      email.to_s.downcase.gsub(/\s+/, "")
    end

    private

    DUMMY_PASSWORD = "*"

    def prevent_timing_attack
      new(password: DUMMY_PASSWORD)
      nil
    end

    def password_strategy
      Clearance.configuration.password_strategy || PasswordStrategies::BCrypt
    end
  end

  # @api private
  module Validations
    extend ActiveSupport::Concern

    included do
      validates :email,
        email: { mode: :strict },
        presence: true,
        uniqueness: { allow_blank: true, case_sensitive: true },
        unless: :email_optional?

      validates :password, presence: true, unless: :skip_password_validation?
    end
  end

  # @api private
  module Callbacks
    extend ActiveSupport::Concern

    included do
      before_validation :normalize_email
      before_create :generate_remember_token
    end
  end

  # Generates a {#confirmation_token} for the user, which allows them to reset
  # their password via an email link.
  #
  # Calling `forgot_password!` will cause the user model to be saved without
  # validations. Any other changes you made to this user instance will also
  # be persisted, without validation. It is inteded to be called on an
  # instance with no changes (`dirty? == false`).
  #
  # @return [Boolean] Was the save successful?
  def forgot_password!
    generate_confirmation_token
    save validate: false
  end

  # Generates a new {#remember_token} for the user, which will have the effect
  # of signing all of the user's current sessions out. This is called
  # internally by {Session#sign_out}.
  #
  # Calling `reset_remember_token!` will cause the user model to be saved
  # without validations. Any other changes you made to this user instance will
  # also be persisted, without validation. It is inteded to be called on an
  # instance with no changes (`dirty? == false`).
  #
  # @return [Boolean] Was the save successful?
  def reset_remember_token!
    generate_remember_token
    save validate: false
  end

  # Sets the user's password to the new value, using the `password=` method on
  # the configured password strategy. By default, this is
  # {PasswordStrategies::BCrypt#password=}.
  #
  # This also has the side-effect of blanking the {#confirmation_token} and
  # rotating the `#remember_token`.
  #
  # Validations will be run as part of this update. If the user instance is
  # not valid, the password change will not be persisted, and this method will
  # return `false`.
  #
  # @return [Boolean] Was the save successful?
  def update_password(new_password)
    self.password = new_password

    if valid?
      self.confirmation_token = nil
      generate_remember_token
    end

    save
  end

  private

  # Sets the email on this instance to the value returned by
  # {.normalize_email}
  #
  # @return [String]
  def normalize_email
    self.email = self.class.normalize_email(email)
  end

  # Always false. Override this method in your user model to allow for other
  # forms of user authentication (username, Facebook, etc).
  #
  # @return [Boolean]
  def email_optional?
    false
  end

  # Always false. Override this method in your user model to allow for other
  # forms of user authentication (username, Facebook, etc).
  #
  # @return [Boolean]
  def password_optional?
    false
  end

  # True if {#password_optional?} is true or if the user already has an
  # {#encrypted_password} that is not changing.
  #
  # @return [Boolean]
  def skip_password_validation?
    password_optional? ||
      (encrypted_password.present? && !encrypted_password_changed?)
  end

  # Sets the {#confirmation_token} on the instance to a new value generated by
  # {Token.new}. The change is not automatically persisted. If you would like
  # to generate and save in a single method call, use {#forgot_password!}.
  #
  # @return [String] The new confirmation token
  def generate_confirmation_token
    self.confirmation_token = Clearance::Token.new
  end

  # Sets the {#remember_token} on the instance to a new value generated by
  # {Token.new}. The change is not automatically persisted. If you would like
  # to generate and save in a single method call, use
  # {#reset_remember_token!}.
  #
  # @return [String] The new remember token
  def generate_remember_token
    self.remember_token = Clearance::Token.new
  end

  private_constant :Callbacks, :ClassMethods, :Validations
end

#email_optional?Boolean (private)

Always false. Override this method in your user model to allow for other forms of user authentication (username, Facebook, etc).

Returns:

  • (Boolean)


238
239
240
# File 'lib/clearance/user.rb', line 238

def email_optional?
  false
end

#forgot_password!Boolean

Generates a #confirmation_token for the user, which allows them to reset their password via an email link.

Calling forgot_password! will cause the user model to be saved without validations. Any other changes you made to this user instance will also be persisted, without validation. It is inteded to be called on an instance with no changes (dirty? == false).

Returns:

  • (Boolean)

    Was the save successful?



181
182
183
184
# File 'lib/clearance/user.rb', line 181

def forgot_password!
  generate_confirmation_token
  save validate: false
end

#generate_confirmation_tokenString (private)

Sets the #confirmation_token on the instance to a new value generated by Token.new. The change is not automatically persisted. If you would like to generate and save in a single method call, use #forgot_password!.

Returns:

  • (String)

    The new confirmation token



264
265
266
# File 'lib/clearance/user.rb', line 264

def generate_confirmation_token
  self.confirmation_token = Clearance::Token.new
end

#generate_remember_tokenString (private)

Sets the #remember_token on the instance to a new value generated by Token.new. The change is not automatically persisted. If you would like to generate and save in a single method call, use #reset_remember_token!.

Returns:

  • (String)

    The new remember token



274
275
276
# File 'lib/clearance/user.rb', line 274

def generate_remember_token
  self.remember_token = Clearance::Token.new
end

#normalize_emailString (private)

Sets the email on this instance to the value returned by normalize_email

Returns:

  • (String)


230
231
232
# File 'lib/clearance/user.rb', line 230

def normalize_email
  self.email = self.class.normalize_email(email)
end

#password=void (readonly)

This method returns an undefined value.

Sets the user's encrypted_password by using the configured Password Strategy's password= method. By default, this will be PasswordStrategies::BCrypt#password=, but can be changed with Configuration#password_strategy.

See Also:



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/clearance/user.rb', line 97

module User
  extend ActiveSupport::Concern

  included do
    attr_reader :password

    include Validations
    include Callbacks
    include password_strategy

    def password=(value)
      encrypted_password_will_change!
      super
    end
  end

  # @api private
  module ClassMethods
    def authenticate(email, password)
      if user = find_by_normalized_email(email)
        if password.present? && user.authenticated?(password)
          user
        end
      else
        prevent_timing_attack
      end
    end

    def find_by_normalized_email(email)
      find_by(email: normalize_email(email))
    end

    def normalize_email(email)
      email.to_s.downcase.gsub(/\s+/, "")
    end

    private

    DUMMY_PASSWORD = "*"

    def prevent_timing_attack
      new(password: DUMMY_PASSWORD)
      nil
    end

    def password_strategy
      Clearance.configuration.password_strategy || PasswordStrategies::BCrypt
    end
  end

  # @api private
  module Validations
    extend ActiveSupport::Concern

    included do
      validates :email,
        email: { mode: :strict },
        presence: true,
        uniqueness: { allow_blank: true, case_sensitive: true },
        unless: :email_optional?

      validates :password, presence: true, unless: :skip_password_validation?
    end
  end

  # @api private
  module Callbacks
    extend ActiveSupport::Concern

    included do
      before_validation :normalize_email
      before_create :generate_remember_token
    end
  end

  # Generates a {#confirmation_token} for the user, which allows them to reset
  # their password via an email link.
  #
  # Calling `forgot_password!` will cause the user model to be saved without
  # validations. Any other changes you made to this user instance will also
  # be persisted, without validation. It is inteded to be called on an
  # instance with no changes (`dirty? == false`).
  #
  # @return [Boolean] Was the save successful?
  def forgot_password!
    generate_confirmation_token
    save validate: false
  end

  # Generates a new {#remember_token} for the user, which will have the effect
  # of signing all of the user's current sessions out. This is called
  # internally by {Session#sign_out}.
  #
  # Calling `reset_remember_token!` will cause the user model to be saved
  # without validations. Any other changes you made to this user instance will
  # also be persisted, without validation. It is inteded to be called on an
  # instance with no changes (`dirty? == false`).
  #
  # @return [Boolean] Was the save successful?
  def reset_remember_token!
    generate_remember_token
    save validate: false
  end

  # Sets the user's password to the new value, using the `password=` method on
  # the configured password strategy. By default, this is
  # {PasswordStrategies::BCrypt#password=}.
  #
  # This also has the side-effect of blanking the {#confirmation_token} and
  # rotating the `#remember_token`.
  #
  # Validations will be run as part of this update. If the user instance is
  # not valid, the password change will not be persisted, and this method will
  # return `false`.
  #
  # @return [Boolean] Was the save successful?
  def update_password(new_password)
    self.password = new_password

    if valid?
      self.confirmation_token = nil
      generate_remember_token
    end

    save
  end

  private

  # Sets the email on this instance to the value returned by
  # {.normalize_email}
  #
  # @return [String]
  def normalize_email
    self.email = self.class.normalize_email(email)
  end

  # Always false. Override this method in your user model to allow for other
  # forms of user authentication (username, Facebook, etc).
  #
  # @return [Boolean]
  def email_optional?
    false
  end

  # Always false. Override this method in your user model to allow for other
  # forms of user authentication (username, Facebook, etc).
  #
  # @return [Boolean]
  def password_optional?
    false
  end

  # True if {#password_optional?} is true or if the user already has an
  # {#encrypted_password} that is not changing.
  #
  # @return [Boolean]
  def skip_password_validation?
    password_optional? ||
      (encrypted_password.present? && !encrypted_password_changed?)
  end

  # Sets the {#confirmation_token} on the instance to a new value generated by
  # {Token.new}. The change is not automatically persisted. If you would like
  # to generate and save in a single method call, use {#forgot_password!}.
  #
  # @return [String] The new confirmation token
  def generate_confirmation_token
    self.confirmation_token = Clearance::Token.new
  end

  # Sets the {#remember_token} on the instance to a new value generated by
  # {Token.new}. The change is not automatically persisted. If you would like
  # to generate and save in a single method call, use
  # {#reset_remember_token!}.
  #
  # @return [String] The new remember token
  def generate_remember_token
    self.remember_token = Clearance::Token.new
  end

  private_constant :Callbacks, :ClassMethods, :Validations
end

#password_optional?Boolean (private)

Always false. Override this method in your user model to allow for other forms of user authentication (username, Facebook, etc).

Returns:

  • (Boolean)


246
247
248
# File 'lib/clearance/user.rb', line 246

def password_optional?
  false
end

#reset_remember_token!Boolean

Generates a new #remember_token for the user, which will have the effect of signing all of the user's current sessions out. This is called internally by Session#sign_out.

Calling reset_remember_token! will cause the user model to be saved without validations. Any other changes you made to this user instance will also be persisted, without validation. It is inteded to be called on an instance with no changes (dirty? == false).

Returns:

  • (Boolean)

    Was the save successful?



196
197
198
199
# File 'lib/clearance/user.rb', line 196

def reset_remember_token!
  generate_remember_token
  save validate: false
end

#skip_password_validation?Boolean (private)

True if #password_optional? is true or if the user already has an #encrypted_password that is not changing.

Returns:

  • (Boolean)


254
255
256
257
# File 'lib/clearance/user.rb', line 254

def skip_password_validation?
  password_optional? ||
    (encrypted_password.present? && !encrypted_password_changed?)
end

#update_password(new_password) ⇒ Boolean

Sets the user's password to the new value, using the password= method on the configured password strategy. By default, this is PasswordStrategies::BCrypt#password=.

This also has the side-effect of blanking the #confirmation_token and rotating the #remember_token.

Validations will be run as part of this update. If the user instance is not valid, the password change will not be persisted, and this method will return false.

Returns:

  • (Boolean)

    Was the save successful?



213
214
215
216
217
218
219
220
221
222
# File 'lib/clearance/user.rb', line 213

def update_password(new_password)
  self.password = new_password

  if valid?
    self.confirmation_token = nil
    generate_remember_token
  end

  save
end