loopbackio / loopback-next

LoopBack makes it easy to build modern API applications that require complex integrations.
https://loopback.io
Other
4.95k stars 1.07k forks source link

How to encrypt password in loopback 4? #2267

Closed bijarniya closed 5 years ago

bijarniya commented 5 years ago
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,`
bkstorm commented 5 years ago

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

bijarniya commented 5 years ago

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

bijarniya commented 5 years ago

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');
  }
}
bajtos commented 5 years ago

@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/

sbacem commented 5 years ago

@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

psaunders commented 5 years ago

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');
    });
  }
}
dhmlau commented 5 years ago

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

jannyHou commented 5 years ago

@bijarniya I believe you were trying the authentication system in shopping example, now it's refactored to use @loopback/authentication@2.x, you can find:

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 :)

BIMMITHA-GB commented 3 years ago

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

Lipinalipi commented 3 years ago

md5 encryption is possible or not in loopback4?

achrinza commented 3 years ago

@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).

dougal83 commented 3 years ago

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. 👍