feathersjs-ecosystem/feathers-authentication-management

View on GitHub
src/methods/verify-signup-set-password.ts

Summary

Maintainability
A
1 hr
Test Coverage
A
94%
import { BadRequest } from '@feathersjs/errors';
import makeDebug from 'debug';
import {
  ensureObjPropsValid,
  ensureValuesAreStrings,
  getUserData,
  hashPassword,
  isDateAfterNow,
  notify
} from '../helpers';
import type { Id, Params } from '@feathersjs/feathers';
import type { VerifyChanges } from '..';

import type {
  IdentifyUser,
  SanitizedUser,
  Tokens,
  User,
  VerifySignupSetPasswordOptions,
  VerifySignupSetPasswordWithShortTokenOptions,
  NotifierOptions
} from '../types';

const debug = makeDebug('authLocalMgnt:verifySignupSetPassword');

export async function verifySignupSetPasswordWithLongToken (
  options: VerifySignupSetPasswordOptions,
  verifyToken: string,
  password: string,
  notifierOptions: NotifierOptions = {},
  params?: Params
): Promise<SanitizedUser> {
  ensureValuesAreStrings(verifyToken, password);

  const result = await verifySignupSetPassword(
    options,
    { verifyToken },
    { verifyToken },
    password,
    notifierOptions,
    params
  );
  return result;
}

export async function verifySignupSetPasswordWithShortToken (
  options: VerifySignupSetPasswordWithShortTokenOptions,
  verifyShortToken: string,
  identifyUser: IdentifyUser,
  password: string,
  notifierOptions: NotifierOptions = {},
  params?: Params
): Promise<SanitizedUser> {
  ensureValuesAreStrings(verifyShortToken, password);
  ensureObjPropsValid(identifyUser, options.identifyUserProps);

  const result = await verifySignupSetPassword(
    options,
    identifyUser,
    {
      verifyShortToken
    },
    password,
    notifierOptions,
    params
  );
  return result;
}

async function verifySignupSetPassword (
  options: VerifySignupSetPasswordOptions,
  identifyUser: IdentifyUser,
  tokens: Tokens,
  password: string,
  notifierOptions: NotifierOptions = {},
  params?: Params
): Promise<SanitizedUser> {
  debug('verifySignupSetPassword', identifyUser, tokens, password);

  if (params && "query" in params) {
    params = Object.assign({}, params);
    delete params.query;
  }

  const {
    app,
    passwordField,
    skipPasswordHash,
    sanitizeUserForClient,
    service,
    notifier
  } = options;

  const usersService = app.service(service);
  const usersServiceId = usersService.id;

  const users = await usersService.find({
    ...params,
    query: { ...identifyUser, $limit: 2 },
    paginate: false,
  }) as User[];
  const user = getUserData(users, [
    'isNotVerifiedOrHasVerifyChanges',
    'verifyNotExpired'
  ]);

  if (!Object.keys(tokens).every((key) => tokens[key] === user[key])) {
    await eraseVerifyProps(user, user.isVerified, params);

    throw new BadRequest(
      'Invalid token. Get for a new one. (authLocalMgnt)',
      { errors: { $className: 'badParam' } }
    );
  }

  const userErasedVerify = await eraseVerifyPropsSetPassword(
    user,
    isDateAfterNow(user.verifyExpires),
    user.verifyChanges || {},
    password,
    skipPasswordHash,
    params
  );

  const userResult = await notify(notifier, 'verifySignupSetPassword', userErasedVerify, notifierOptions);
  return sanitizeUserForClient(userResult);

  async function eraseVerifyProps (
    user: User,
    isVerified: boolean,
    params?: Params
  ): Promise<User> {
    const patchData = Object.assign({}, {
      isVerified,
      verifyToken: null,
      verifyShortToken: null,
      verifyExpires: null,
      verifyChanges: {},
    });

    const result = await usersService.patch(
      user[usersServiceId] as Id,
      patchData,
      Object.assign({}, params)
    ) as User;
    return result;
  }

  async function eraseVerifyPropsSetPassword (
    user: User,
    isVerified: boolean,
    verifyChanges: VerifyChanges,
    password: string,
    skipPasswordHash: boolean,
    params?: Params
  ): Promise<User> {
    
    const patchData = Object.assign({}, verifyChanges || {}, {
      isVerified,
      verifyToken: null,
      verifyShortToken: null,
      verifyExpires: null,
      verifyChanges: {},
      [passwordField]: skipPasswordHash ? password : await hashPassword(app, password, passwordField)
    });

    const result = await usersService.patch(
      user[usersServiceId] as Id,
      patchData,
      Object.assign({}, params)
    ) as User;
    return result;
  }
}