import * as crypto from 'crypto' ; import * as signInMethodRepository from './sign-in-method.repository' ; import * as userService from '../user.service' ; import * as authTokenService from '../../authentication/auth-token.service' ; import { Transaction } from 'kysely' ; import { Database } from '../../database' ; import { UserNotFoundError } from '../../util/errors' ; import { SignedInUser } from '../signed-in-user' ; import { PasswordSignInMethod } from './sign-in-method' ; export const MIN_PASSWORD_LENGTH = 8 ; export const MAX_PASSWORD_LENGTH = 255 ; export class UserAlreadyHasSignInMethodError extends Error { } export class SignInMethodNotFoundError extends Error { } export class WrongPasswordError extends Error { } export class PasswordTooWeakError extends Error { } export class PasswordTooLongError extends Error { } export async function addPasswordSignInMethod ( trx , userId , method ) { const user = await userService . lockUserById ( trx , userId ) ; if ( ! user ) { throw new UserNotFoundError ( ) ; } if ( user . email ) { throw new UserAlreadyHasSignInMethodError ( ) ; } await signInMethodRepository . insertPasswordSignInMethod ( trx , { user_id : userId , password_hash : await encryptPassword ( method . password ) , } ) ; await userService . setUserEmail ( trx , userId , method . email ) ; } async function encryptPassword ( password ) { if ( password . length < MIN_PASSWORD_LENGTH ) { throw new PasswordTooWeakError ( ) ; } if ( password . length > MAX_PASSWORD_LENGTH ) { throw new PasswordTooLongError ( ) ; } return encryptSecret ( password ) ; } async function encryptSecret ( secret ) { const salt = crypto . randomBytes ( 16 ) . toString ( 'hex' ) ; return `${ salt }:${ await scrypt ( secret , salt ) }` ; } async function verifySecret ( secret , hash ) { const [ salt , secretHash ] = hash . split ( ':' ) ; return ( await scrypt ( secret , salt ) ) === secretHash ; } async function scrypt ( secret , salt ) { return new Promise ( ( resolve , reject ) => { crypto . scrypt ( secret , salt , 64 , { N : 16384 , r : 8 , p : 1 } , ( err , secretHash ) => { if ( err ) { return reject ( err ) ; } resolve ( secretHash . toString ( 'hex' ) ) ; } ) ; } ) ; } export async function singInUsingPassword ( trx , method ) { const user = await userService . lockUserByEmail ( trx , method . email ) ; if ( ! user ) { throw new UserNotFoundError ( ) ; } const signInMethod = await signInMethodRepository . findPasswordSignInMethod ( trx , user . id ) ; if ( ! signInMethod ) { throw new SignInMethodNotFoundError ( ) ; } if ( ! ( await verifyPassword ( method . password , signInMethod . password_hash ) ) ) { throw new WrongPasswordError ( ) ; } const refreshToken = await authTokenService . createRefreshToken ( trx , user . id ) ; const authToken = await authTokenService . createAuthToken ( trx , refreshToken ) ; return { user , authToken , refreshToken , } ; } async function verifyPassword ( password , hash ) { return verifySecret ( password , hash ) ; }