All files / src/methods reset-password.ts

95.65% Statements 44/46
73.33% Branches 11/15
100% Functions 6/6
95.56% Lines 43/45

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 1301x 1x 1x 1x 1x 1x 1x 1x 1x                         1x   1x           48x   48x     1x             11x 11x   9x                   57x 57x 57x     57x 48x 48x 48x 9x 9x             57x 57x   42x 42x   4x           38x       13x               42x 42x   13x 4x       4x   9x             9x           29x               29x 29x    
import { BadRequest } from '@feathersjs/errors';
import makeDebug from 'debug';
import comparePasswords from '../helpers/compare-passwords';
import deconstructId from '../helpers/deconstruct-id';
import ensureObjPropsValid from '../helpers/ensure-obj-props-valid';
import ensureValuesAreStrings from '../helpers/ensure-values-are-strings';
import getUserData from '../helpers/get-user-data';
import hashPassword from '../helpers/hash-password';
import notifier from '../helpers/notifier';
 
import type { Query } from '@feathersjs/feathers';
 
import type {
  UsersArrayOrPaginated,
  IdentifyUser,
  ResetPasswordOptions,
  ResetPwdWithShortTokenOptions,
  SanitizedUser,
  Tokens
} from '../types';
 
const debug = makeDebug('authLocalMgnt:resetPassword');
 
export async function resetPwdWithLongToken (
  options: ResetPasswordOptions,
  resetToken: string,
  password: string,
  notifierOptions = {}
): Promise<SanitizedUser> {
  ensureValuesAreStrings(resetToken, password);
 
  return await resetPassword(options, { resetToken }, { resetToken }, password, notifierOptions);
}
 
export async function resetPwdWithShortToken (
  options: ResetPwdWithShortTokenOptions,
  resetShortToken: string,
  identifyUser: IdentifyUser,
  password: string,
  notifierOptions = {}
): Promise<SanitizedUser> {
  ensureValuesAreStrings(resetShortToken, password);
  ensureObjPropsValid(identifyUser, options.identifyUserProps);
 
  return await resetPassword(options, identifyUser, { resetShortToken }, password, notifierOptions);
}
 
async function resetPassword (
  options: ResetPasswordOptions,
  query: Query,
  tokens: Tokens,
  password: string,
  notifierOptions = {}
): Promise<SanitizedUser> {
  debug('resetPassword', query, tokens, password);
  const usersService = options.app.service(options.service);
  const usersServiceIdName = usersService.id;
  let users: UsersArrayOrPaginated;
 
  if (tokens.resetToken) {
    const id = deconstructId(tokens.resetToken);
    const user = await usersService.get(id);
    users = [user];
  } else Eif (tokens.resetShortToken) {
    users = await usersService.find({ query });
  } else {
    throw new BadRequest('resetToken and resetShortToken are missing. (authLocalMgnt)', {
      errors: { $className: 'missingToken' }
    });
  }
 
  const checkProps = options.skipIsVerifiedCheck ? ['resetNotExpired'] : ['resetNotExpired', 'isVerified'];
  const user1 = getUserData(users, checkProps);
 
  const tokenChecks = Object.keys(tokens).map(async key => {
    if (options.reuseResetToken) {
      // Comparing token directly as reused resetToken is not hashed
      Iif (tokens[key] !== user1[key]) {
        throw new BadRequest('Reset Token is incorrect. (authLocalMgnt)', {
          errors: { $className: 'incorrectToken' }
        });
      }
    } else {
      return await comparePasswords(
        tokens[key],
        user1[key] as string,
        () =>
          new BadRequest(
            'Reset Token is incorrect. (authLocalMgnt)',
            { errors: { $className: 'incorrectToken' } }
          )
      );
    }
  });
 
  try {
    await Promise.all(tokenChecks);
  } catch (err) {
    if (user1.resetAttempts > 0) {
      await usersService.patch(user1[usersServiceIdName], {
        resetAttempts: user1.resetAttempts - 1
      });
 
      throw err;
    } else {
      await usersService.patch(user1[usersServiceIdName], {
        resetToken: null,
        resetAttempts: null,
        resetShortToken: null,
        resetExpires: null
      });
 
      throw new BadRequest('Invalid token. Get for a new one. (authLocalMgnt)', {
        errors: { $className: 'invalidToken' }
      });
    }
  }
 
  const user2 = await usersService.patch(user1[usersServiceIdName], {
    password: await hashPassword(options.app, password, options.passwordField),
    resetExpires: null,
    resetAttempts: null,
    resetToken: null,
    resetShortToken: null
  });
 
  const user3 = await notifier(options.notifier, 'resetPwd', user2, notifierOptions);
  return options.sanitizeUserForClient(user3);
}