{"version":3,"sources":["../../../src/auth/operations/forgotPassword.ts"],"sourcesContent":["import crypto from 'crypto'\nimport { status as httpStatus } from 'http-status'\n\nimport type {\n  AuthOperationsFromCollectionSlug,\n  Collection,\n} from '../../collections/config/types.js'\nimport type { AuthCollectionSlug } from '../../index.js'\nimport type { PayloadRequest, Where } from '../../types/index.js'\n\nimport { buildAfterOperation } from '../../collections/operations/utilities/buildAfterOperation.js'\nimport { buildBeforeOperation } from '../../collections/operations/utilities/buildBeforeOperation.js'\nimport { APIError } from '../../errors/index.js'\nimport { Forbidden } from '../../index.js'\nimport { appendNonTrashedFilter } from '../../utilities/appendNonTrashedFilter.js'\nimport { commitTransaction } from '../../utilities/commitTransaction.js'\nimport { formatAdminURL } from '../../utilities/formatAdminURL.js'\nimport { getRequestOrigin } from '../../utilities/getRequestOrigin.js'\nimport { initTransaction } from '../../utilities/initTransaction.js'\nimport { killTransaction } from '../../utilities/killTransaction.js'\nimport { getLoginOptions } from '../getLoginOptions.js'\n\nexport type Arguments<TSlug extends AuthCollectionSlug> = {\n  collection: Collection\n  data: {\n    [key: string]: unknown\n  } & AuthOperationsFromCollectionSlug<TSlug>['forgotPassword']\n  disableEmail?: boolean\n  expiration?: number\n  overrideAccess?: boolean\n  req: PayloadRequest\n}\n\nexport type Result = string\n\nexport const forgotPasswordOperation = async <TSlug extends AuthCollectionSlug>(\n  incomingArgs: Arguments<TSlug>,\n): Promise<null | string> => {\n  const loginWithUsername = incomingArgs.collection.config.auth.loginWithUsername\n  const { data, overrideAccess } = incomingArgs\n\n  const { canLoginWithEmail, canLoginWithUsername } = getLoginOptions(loginWithUsername)\n\n  const sanitizedEmail =\n    (canLoginWithEmail && (incomingArgs.data.email || '').toLowerCase().trim()) || null\n  const sanitizedUsername =\n    'username' in data && typeof data?.username === 'string'\n      ? data.username.toLowerCase().trim()\n      : null\n\n  let args = incomingArgs\n\n  if (incomingArgs.collection.config.auth.disableLocalStrategy) {\n    throw new Forbidden(incomingArgs.req.t)\n  }\n  if (!sanitizedEmail && !sanitizedUsername) {\n    throw new APIError(\n      `Missing ${loginWithUsername ? 'username' : 'email'}.`,\n      httpStatus.BAD_REQUEST,\n    )\n  }\n\n  try {\n    const shouldCommit = await initTransaction(args.req)\n\n    // /////////////////////////////////////\n    // beforeOperation - Collection\n    // /////////////////////////////////////\n    args = await buildBeforeOperation({\n      args,\n      collection: args.collection.config,\n      operation: 'forgotPassword',\n      overrideAccess,\n    })\n\n    const {\n      collection: { config: collectionConfig },\n      disableEmail,\n      expiration,\n      req: {\n        payload: { config, email },\n        payload,\n      },\n      req,\n    } = args\n\n    // /////////////////////////////////////\n    // Forget password\n    // /////////////////////////////////////\n\n    let token: string = crypto.randomBytes(20).toString('hex')\n    type UserDoc = {\n      email?: string\n      id: number | string\n      resetPasswordExpiration?: string\n      resetPasswordToken?: string\n    }\n\n    if (!sanitizedEmail && !sanitizedUsername) {\n      throw new APIError(\n        `Missing ${loginWithUsername ? 'username' : 'email'}.`,\n        httpStatus.BAD_REQUEST,\n      )\n    }\n\n    let whereConstraint: Where = {}\n\n    if (canLoginWithEmail && sanitizedEmail) {\n      whereConstraint = {\n        email: {\n          equals: sanitizedEmail,\n        },\n      }\n    } else if (canLoginWithUsername && sanitizedUsername) {\n      whereConstraint = {\n        username: {\n          equals: sanitizedUsername,\n        },\n      }\n    }\n\n    // Exclude trashed users unless `trash: true`\n    whereConstraint = appendNonTrashedFilter({\n      enableTrash: collectionConfig.trash,\n      trash: false,\n      where: whereConstraint,\n    })\n\n    let user = await payload.db.findOne<UserDoc>({\n      collection: collectionConfig.slug,\n      req,\n      where: whereConstraint,\n    })\n\n    // We don't want to indicate specifically that an email was not found,\n    // as doing so could lead to the exposure of registered emails.\n    // Therefore, we prefer to fail silently.\n    if (!user) {\n      await commitTransaction(args.req)\n      return null\n    }\n\n    const resetPasswordExpiration = new Date(\n      Date.now() + (collectionConfig.auth?.forgotPassword?.expiration ?? expiration ?? 3600000),\n    ).toISOString()\n\n    user = await payload.update({\n      id: user.id,\n      collection: collectionConfig.slug,\n      data: {\n        resetPasswordExpiration,\n        resetPasswordToken: token,\n      },\n      req,\n    })\n\n    if (!disableEmail && user.email) {\n      const serverURL = getRequestOrigin({ config, req })\n      const forgotURL = formatAdminURL({\n        adminRoute: config.routes.admin,\n        path: `${config.admin.routes.reset}/${token}`,\n        serverURL,\n      })\n      let html = `${req.t('authentication:youAreReceivingResetPassword')}\n    <a href=\"${forgotURL}\">${forgotURL}</a>\n    ${req.t('authentication:youDidNotRequestPassword')}`\n\n      if (typeof collectionConfig.auth.forgotPassword?.generateEmailHTML === 'function') {\n        html = await collectionConfig.auth.forgotPassword.generateEmailHTML({\n          req,\n          token,\n          user,\n        })\n      }\n\n      let subject = req.t('authentication:resetYourPassword')\n\n      if (typeof collectionConfig.auth.forgotPassword?.generateEmailSubject === 'function') {\n        subject = await collectionConfig.auth.forgotPassword.generateEmailSubject({\n          req,\n          token,\n          user,\n        })\n      }\n\n      await email.sendEmail({\n        from: `\"${email.defaultFromName}\" <${email.defaultFromAddress}>`,\n        html,\n        subject,\n        to: user.email,\n      })\n    }\n\n    // /////////////////////////////////////\n    // afterForgotPassword - Collection\n    // /////////////////////////////////////\n\n    if (collectionConfig.hooks?.afterForgotPassword?.length) {\n      for (const hook of collectionConfig.hooks.afterForgotPassword) {\n        await hook({ args, collection: args.collection?.config, context: req.context })\n      }\n    }\n\n    // /////////////////////////////////////\n    // afterOperation - Collection\n    // /////////////////////////////////////\n\n    token = await buildAfterOperation({\n      args,\n      collection: args.collection?.config,\n      operation: 'forgotPassword',\n      overrideAccess,\n      result: token,\n    })\n\n    if (shouldCommit) {\n      await commitTransaction(req)\n    }\n\n    return token\n  } catch (error: unknown) {\n    await killTransaction(args.req)\n    throw error\n  }\n}\n"],"names":["crypto","status","httpStatus","buildAfterOperation","buildBeforeOperation","APIError","Forbidden","appendNonTrashedFilter","commitTransaction","formatAdminURL","getRequestOrigin","initTransaction","killTransaction","getLoginOptions","forgotPasswordOperation","incomingArgs","loginWithUsername","collection","config","auth","data","overrideAccess","canLoginWithEmail","canLoginWithUsername","sanitizedEmail","email","toLowerCase","trim","sanitizedUsername","username","args","disableLocalStrategy","req","t","BAD_REQUEST","shouldCommit","operation","collectionConfig","disableEmail","expiration","payload","token","randomBytes","toString","whereConstraint","equals","enableTrash","trash","where","user","db","findOne","slug","resetPasswordExpiration","Date","now","forgotPassword","toISOString","update","id","resetPasswordToken","serverURL","forgotURL","adminRoute","routes","admin","path","reset","html","generateEmailHTML","subject","generateEmailSubject","sendEmail","from","defaultFromName","defaultFromAddress","to","hooks","afterForgotPassword","length","hook","context","result","error"],"mappings":"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"}