All files / src verifier.js

97.67% Statements 42/43
90% Branches 18/20
100% Functions 6/6
97.62% Lines 41/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     37x 37x 37x 37x   37x 1x     36x 36x 36x         9x   9x 1x     8x   8x 8x   8x       8x 1x 1x     7x 7x             9x 9x     9x 2x     7x 7x     7x 7x 7x           7x   5x   5x 5x 5x   2x          
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));
  }
}
 
export default LocalVerifier;