Loopback-next: How to encrypt password in loopback 4?

Created on 18 Jan 2019  路  12Comments  路  Source: strongloop/loopback-next

import { Member } from '../models';
import { MemberRepository} from '../repositories';
import {inject, Context} from '@loopback/context';

export class MemberController {
constructor(
 @repository(MemberRepository) private memberRepository: MemberRepository,
@inject('utilities.PasswordHasher') private passwordHasher: PasswordHasher,
 ) { }


 }

Getting this error.
Which module can i use for this?.
src/controllers/member.controller.ts:27:65 - error TS2304: Cannot find name 'PasswordHasher'.

@inject('utilities.PasswordHasher') private passwordHasher: PasswordHasher
~~~~~~

src/controllers/member.controller.ts:27:65 - error TS4063: Parameter 'passwordHasher' of constructor from exported class has or is using private name 'PasswordHasher'.

@inject('utilities.PasswordHasher') private passwordHasher: PasswordHasher,
~~~~~~

Authentication Docs question

Most helpful comment

I think it's just an example, you must implement PasswordHasher by yourself.
To encrypt password, you can check the loopback 4 shopping cart example, they use bcryptjs module: https://github.com/strongloop/loopback4-example-shopping/blob/master/src/controllers/user.controller.ts

All 12 comments

I think it's just an example, you must implement PasswordHasher by yourself.
To encrypt password, you can check the loopback 4 shopping cart example, they use bcryptjs module: https://github.com/strongloop/loopback4-example-shopping/blob/master/src/controllers/user.controller.ts

Thanks, @bkstorm

But I am confused because I checked loopback 4 document, I got a document for the password.
Loopback 4 providing an example but that is not working for me.

I got an error and I am not able to find "PasswordHasher Module".

Refrence URL
https://loopback.io/doc/en/lb4/Extending-LoopBack-4.html

Solution here
/MemberController/

import {
  Count,
  CountSchema,
  Filter,
  repository,
  Where,
} from '@loopback/repository';
import {
  post,
  param,
  get,
  getFilterSchemaFor,
  getWhereSchemaFor,
  patch,
  put,
  del,
  requestBody,
  HttpErrors,
} from '@loopback/rest';
import {Member} from '../models';
import {MemberRepository} from '../repositories';
import {hash} from 'bcryptjs';
import {promisify} from 'util';
import * as common from '../components/common.component';

/**
 * This Controller handle all member operation
 */
export class MemberController extends common.commonComponent {
  //Hash for password
  private hashAsync = promisify(hash);
  constructor(
    @repository(MemberRepository) private memberRepository: MemberRepository,
  ) {
    super();
  }

  @post('/members', {
    responses: {
      '200': {
        description: 'Member model instance',
        content: {'application/json': {schema: {'x-ts-type': Member}}},
      },
    },
  })
  async create(@requestBody() member: Member): Promise<Member> {
    try {
      this.mDValidation({email: member.email, password: member.password}); // check empty fields
      this.emailValidation(member.email); // Check validate email
      this.checkPasswordLength(member.password); //Check password length

      //Check member exist
      let dataObj: any = await this.memberRepository.count({
        email: member.email,
        status: {in: [0, 1]},
      });
      if (dataObj && dataObj.count > 0)
        throw new HttpErrors.UnprocessableEntity('This email id already exist');

      //Generate password
      member.password = await this.hashAsync(member.password, 10);

      //Save member data in database and return object
      return await new Promise<any>((resolve, reject) => {
        this.memberRepository.create(member).then(
          function(result) {
            delete result.password; //Remove password from member object
            return resolve(result);
          },
          function(error) {
            return reject(new HttpErrors.UnprocessableEntity(error));
          },
        );
      });
    } catch (e) {
      throw new HttpErrors.UnprocessableEntity(e);
    }
  }
}

/*common.component.ts*/
import { HttpErrors, } from '@loopback/rest';
import { Component } from '@loopback/core';
import * as joi from 'joi';
import * as EmailValidator from 'email-validator';

/**
 * This component handle the validation or calculation
 */
export class commonComponent implements Component {
  //Member schema fields object
  private schemaObj: object = {
    email: joi.string().required(),
    password: joi.string().required()
  };

  constructor() {
  }

  /**
   * Check empty fileds
   */
  protected mDValidation(data: object): void {
    let result = joi.validate(data, this.schemaObj);
    if (!(result && result.error === null))
      throw new HttpErrors.UnprocessableEntity(result.error.details[0].message.replace(/"/g, ''));
  }

  /**
   * Check email validatons
   */
  protected emailValidation(email: string): void {
    if (!(EmailValidator.validate(email)))
      throw new HttpErrors.UnprocessableEntity('Invalid email');
  }

  /**
   * Check password length
   */
  protected checkPasswordLength(password: string): void {
    if (password.length < 6)
      throw new HttpErrors.UnprocessableEntity('Password must be minimum 6 characters');
  }
}

@bijarniya I have reformatted your comment. When posting code snippets, use triple back tick followed by a language name for syntax highlighting. See https://help.github.com/articles/creating-and-highlighting-code-blocks/

@bijarniya I think is better to set encrypt password in model when update the value of password,
@bajtos I think loopback 4 needs listeners like in TypeOrm

Here is a small password service we use

import SecurePass, { VerificationResult } from 'argon2-pass';
import { Account } from '../models';

export class PasswordService {
  constructor(private securePass: SecurePass = new SecurePass()) {}

  async passwordIsForAccount(plainPassword: string, account: Account): Promise<boolean> {
    if (!account.password) {
      return false;
    }

    const result: VerificationResult = await this.securePass.verifyHash(
      Buffer.from(plainPassword),
      Buffer.from(account.password, 'base64'),
    );

    if (SecurePass.isValid(result)) {
      return true;
    }

    return false;
  }

  async getStringHashForPassword(plainPassword: string): Promise<string> {
    return this.securePass.hashPassword(await Buffer.from(plainPassword)).then(value => {
      return value.toString('base64');
    });
  }
}

Discussion with @raymondfeng @jannyHou @emonddr,
there might be something already existed in example-shopping. @emonddr to look up the sample code.

@bijarniya I believe you were trying the authentication system in shopping example, now it's refactored to use @loopback/[email protected], you can find:

  • the password hasher class in this service
  • how user service uses it in file
  • how the password encryption works in the user controller in file

You can follow the tutorial Authentication-Tutorial to learn how the JWT authentication works in that example.

I am closing the issue as the answer is provided, feel free to reopen it if you still have questions :)

Is there any possible way to encrypt password using MD5 encryption in loopback? And how it is implement in our loopback4 program.

md5 encryption is possible or not in loopback4?

@BIMMITHA-GB @Lipinalipi LoopBack 4 does not have any special requirements for password hashing. Hence, you can use whichever hashing library to hash the password before storing it in the database. This can be done either in the Service (recommended), Controller or Repository level. @loopback/authentication-jwt is a lightweight JWT authentication implementation that uses bcrypt, a strong, purpose-built password hashing algorithm, and can be used as a reference point for the codebase design.

As for how to do the actual hashing, please consult the Node.js crypto library or the respective hashing library on their usage. Since an LB4 app is still a Node.js app, the hashing libraries can be used as-is in accordance with their documentation. LB4 only requires the developer to decide where to put the hashing logic (see previous paragraph).

Is there any possible way to encrypt password using MD5 encryption in loopback? And how it is implement in our loopback4 program.

Can I just say... don't encrypt a password with MD5. MD5 should be for creating file signatures, etc that require a fast hash. Password encryption should be computationally expensive to make them resistant to brute force attacks.

+1 to the fellow above using 'argon2' implementation. 馃憤

Was this page helpful?
0 / 5 - 0 ratings

Related issues

half-blood-programmer picture half-blood-programmer  路  3Comments

marioestradarosa picture marioestradarosa  路  3Comments

mightytyphoon picture mightytyphoon  路  3Comments

cloudwheels picture cloudwheels  路  3Comments

zero-bugs picture zero-bugs  路  3Comments