module Negroni::Models::Authenticable

The `Authenticable` module should be included in any application classes that should be authenticable via a JSON web token.

This module makes a few assumptions about your class:

* It has an `email` attribute

Public Class Methods

required_fields(klass) click to toggle source

Required fields for this module

# File lib/negroni/models/authenticable.rb, line 26
def self.required_fields(klass)
  [:password_digest] + klass.authentication_keys
end

Public Instance Methods

authenticable_salt() click to toggle source

Reliably returns the salt, regardless of implementation

@return [String]

# File lib/negroni/models/authenticable.rb, line 136
def authenticable_salt
  password_digest[0, 29] if password_digest
end
authenticate(unencrypted_password) click to toggle source

Authenticates the including class with `unencrypted_password`.

@param unencrypted_password [String] the password to auth against

@return [Boolean] if the user is successfully authenticated

# File lib/negroni/models/authenticable.rb, line 115
def authenticate(unencrypted_password)
  valid_password?(unencrypted_password) && self
end
authenticate!(unencrypted_password) click to toggle source

Authenticates the including class with `unencrypted_password`.

@param unencrypted_password [String] the password to auth against

@raise [ActiveRecord::RecordNotFound] if the user is not successfully

authenticated

@return [Boolean] if the user is successfully authenticated

# File lib/negroni/models/authenticable.rb, line 127
def authenticate!(unencrypted_password)
  authenticate(unencrypted_password) || raise('Bad password!')
end
destroy_with_password(current_password) click to toggle source

Destroy record when :current_password matches, otherwise returns error on :current_password. It also automatically rejects :current_password if it is blank.

@param current_password [String] the current password for the record

# File lib/negroni/models/authenticable.rb, line 95
def destroy_with_password(current_password)
  result = if valid_password?(current_password)
             destroy # rubocop:disable Rails/SaveBang
           else
             valid?
             message = current_password.blank? ? :blank : :invalid
             errors.add(:current_password, message)
             false
           end

  result
end
password=(new_password) click to toggle source

Generates a hashed password based on the given value.

# File lib/negroni/models/authenticable.rb, line 31
def password=(new_password)
  @password = new_password
  self.password_digest = digest_password(@password) if @password.present?
end
update_with_password(params, *options) click to toggle source

Update record attributes when :current_password matches, otherwise returns error on :current_password.

This method also rejects the password field if it is blank (allowing users to change relevant information like the e-mail without changing their password). In case the password field is rejected, the confirmation is also rejected as long as it is also blank.

@param params [Hash] params from the controller @param options [Hash] a hash of options

# File lib/negroni/models/authenticable.rb, line 53
def update_with_password(params, *options)
  current_password = params.delete :current_password

  params = _sanitize_password_params(params)

  result = if valid_password?(current_password)
             update_attributes(params, *options)
           else
             _invalid_update(current_password, params, *options)
           end

  clean_up_passwords
  result
end
update_without_password(params, *options) click to toggle source

Updates record attributes without asking for the current password. Never allows a change to the current password. If you are using this method, you should probably override this method to protect other attributes you would not like to be updated without a password.

@example

def update_without_password(params, *options)
  params.delete(:email)
  super(params)
end
# File lib/negroni/models/authenticable.rb, line 80
def update_without_password(params, *options)
  params.delete(:password)
  params.delete(:password_confirmation)

  result = update_attributes(params, *options)
  clean_up_passwords
  result
end
valid_password?(password) click to toggle source

Checks if a password is valid for the given instance

# File lib/negroni/models/authenticable.rb, line 37
def valid_password?(password)
  Negroni::Encryptor.compare(self.class, password_digest, password)
end

Protected Instance Methods

clean_up_passwords() click to toggle source

Cleans up the @password and @password_confirmation ivars

# File lib/negroni/models/authenticable.rb, line 157
def clean_up_passwords
  self.password = self.password_confirmation = nil
end
digest_password(password) click to toggle source
# File lib/negroni/models/authenticable.rb, line 142
def digest_password(password)
  Negroni::Encryptor.digest(self.class, password)
end
send_password_change_notification() click to toggle source

@!group Callbacks

# File lib/negroni/models/authenticable.rb, line 152
def send_password_change_notification
  send_auth_notification(:password_change)
end
send_password_change_notification?() click to toggle source
# File lib/negroni/models/authenticable.rb, line 146
def send_password_change_notification?
  self.class.send_password_change_notification && password_digest_changed?
end

Private Instance Methods

_invalid_update(current_password, params, *options) click to toggle source
# File lib/negroni/models/authenticable.rb, line 179
def _invalid_update(current_password, params, *options)
  _sanitize_password_params(params, remove_all: true)
  assign_attributes(params, *options)
  valid?

  message = current_password.blank? ? :blank : :invalid
  errors.add(:current_password, message)
  false
end
_sanitize_password_params(params, remove_all: false) click to toggle source

remove :password and :password_confirmation if they are present

@param params [Hash] the params hash to sanitize @return [Hash] the sanitized params

# File lib/negroni/models/authenticable.rb, line 169
def _sanitize_password_params(params, remove_all: false)
  params.delete(:password) if params[:password].blank? || remove_all

  if params[:password_confirmation].blank? || remove_all
    params.delete(:password_confirmation)
  end

  params
end