All files / src verifier.js

95.35% Statements 41/43
80% Branches 16/20
83.33% Functions 5/6
95.24% Lines 40/42
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 861x 1x 1x 1x   1x     38x 38x 38x 38x   38x 1x     37x 37x 37x         5x   5x 1x     4x   4x 4x   4x       4x 1x 1x     3x 3x             5x 5x     5x 2x     3x 3x     8x 8x 8x           8x   1x   1x 1x 1x              
import Debug from 'debug';
import errors from 'feathers-errors';
import bcrypt from 'bcryptjs';
import get from 'lodash.get';
 
const debug = Debug('feathers-authentication-local:verify');
 
class LocalVerifier {
  constructor(app, options = {}) {
    this.app = app;
    this.options = options;
    this.service = typeof options.service === 'string' ? app.service(options.service) : options.service;
 
    if (!this.service) {
      throw new Error(`options.service does not exist.\n\tMake sure you are passing a valid service path or service instance and it is initialized before feathers-authentication-local.`);
    }
 
    this._comparePassword = this._comparePassword.bind(this);
    this._normalizeResult = this._normalizeResult.bind(this);
    this.verify = this.verify.bind(this);
  }
 
  _comparePassword(entity, password) {
    // find password in entity, this allows for dot notation
    const hash = get(entity, this.options.passwordField);
 
    if (!hash) {
      return Promise.reject(new Error(`'${this.options.entity}' record in the database is missing a '${this.options.passwordField}'`));
    }
 
    debug('Verifying password');
 
    return new Promise((resolve, reject) => {
      bcrypt.compare(password, hash, function(error, result) {
        // Handle 500 server error.
        Iif (error) {
          return reject(error);
        }
 
        if (!result) {
          debug('Password incorrect');
          return reject(false);
        }
 
        debug('Password correct');
        return resolve(entity);
      });
    });
  }
 
  _normalizeResult(results) {
    // Paginated services return the array of results in the data attribute.
    let entities = results.data ? results.data : results;
    let entity = entities[0];
 
    // Handle bad username.
    if (!entity) {
      return Promise.reject(false);
    }
 
    debug(`${this.options.entity} found`);
    return Promise.resolve(entity);
  }
 
  verify(req, username, password, done) {
    debug('Checking credentials', username, password);
    const query = {
      [this.options.usernameField]: username,
      $limit: 1
    };
 
    // Look up the entity
    this.service.find({ query })
      .then(this._normalizeResult)
      .then(entity => this._comparePassword(entity, password))
      .then(entity => {
        const id = entity[this.service.id];
        const payload = { [`${this.options.entity}Id`]: id };
        done(null, entity, payload);
      })
      .catch(error => error ? done(error) : done(null, error, { message: 'Invalid login' }));
  }
}
 
export default LocalVerifier;