UNPKG

@youngshand/payload-auth-plugin

Version:

A temporary fork for testing of Authentication plugin for Payload CMS, use @payload-auth-plugin

238 lines (237 loc) 6.73 kB
// src/core/protocols/password.ts import { getCookieExpiration, parseCookies } from "payload"; import { AuthenticationFailed, EmailAlreadyExistError, InvalidCredentials, InvalidRequestBodyError, UnauthorizedAPIRequest, UserNotFoundAPIError } from "../errors/apiErrors.js"; import { hashPassword, verifyPassword } from "../utils/password.js"; import { SuccessKind } from "../../types.js"; import { ephemeralCode, verifyEphemeralCode } from "../utils/hash.js"; import { EPHEMERAL_CODE_COOKIE_NAME } from "../../constants.js"; import { invalidateSessionCookies, verifySessionCookie } from "../utils/cookies.js"; import { revokeSession } from "../utils/session.js"; var PasswordSignin = async (request, internal, sessionCallBack) => { const body = request.json && await request.json(); if (!body?.email || !body.password) { return new InvalidRequestBodyError; } const { payload } = request; const { docs } = await payload.find({ collection: internal.usersCollectionSlug, where: { email: { equals: body.email } }, limit: 1 }); if (docs.length !== 1) { return new UserNotFoundAPIError; } const user = docs[0]; const isVerifed = await verifyPassword(body.password, user["hashedPassword"], user["salt"], user["hashIterations"]); if (!isVerifed) { return new InvalidCredentials; } return sessionCallBack({ id: user.id, email: body.email }); }; var PasswordSignup = async (request, internal, sessionCallBack) => { const body = request.json && await request.json(); if (!body?.email || !body.password) { return new InvalidRequestBodyError; } const { payload } = request; const { docs } = await payload.find({ collection: internal.usersCollectionSlug, where: { email: { equals: body.email } }, limit: 1 }); if (docs.length > 0) { return new EmailAlreadyExistError; } const { hash: hashedPassword, salt, iterations } = await hashPassword(body.password); const user = await payload.create({ collection: internal.usersCollectionSlug, data: { email: body.email, hashedPassword, hashIterations: iterations, salt, ...body.profile } }); if (body.allowAutoSignin) { return sessionCallBack({ id: user.id, email: body.email }); } return Response.json({ message: "Signed up successfully", kind: SuccessKind.Created, isSuccess: true, isError: false }, { status: 201 }); }; var ForgotPasswordInit = async (request, internal) => { const { payload } = request; const body = request.json && await request.json(); if (!body?.email) { return new InvalidRequestBodyError; } const { docs } = await payload.find({ collection: internal.usersCollectionSlug, where: { email: { equals: body.email } }, limit: 1 }); if (docs.length !== 1) { return new UserNotFoundAPIError; } const { code, hash } = await ephemeralCode(6, payload.secret); await payload.sendEmail({ to: body.email, subject: "Password recovery", text: "Password recovery code: " + code }); const res = new Response(JSON.stringify({ message: "Password recovery initiated successfully", kind: SuccessKind.Created, isSuccess: true, isError: false }), { status: 201 }); const tokenExpiration = getCookieExpiration({ seconds: 300 }); res.headers.append("Set-Cookie", `${EPHEMERAL_CODE_COOKIE_NAME}=${hash};Path=/;HttpOnly;Secure=true;SameSite=lax;Expires=${tokenExpiration.toUTCString()}`); return res; }; var ForgotPasswordVerify = async (request, internal) => { const { payload } = request; const body = request.json && await request.json(); if (!body?.email || !body?.password || !body.code) { return new InvalidRequestBodyError; } const cookies = parseCookies(request.headers); const hash = cookies.get(EPHEMERAL_CODE_COOKIE_NAME); if (!hash) { return new UnauthorizedAPIRequest; } const isVerified = await verifyEphemeralCode(body.code, hash, payload.secret); if (!isVerified) { return new AuthenticationFailed; } const { docs } = await payload.find({ collection: internal.usersCollectionSlug, where: { email: { equals: body.email } }, limit: 1 }); if (docs.length !== 1) { return new UserNotFoundAPIError; } const { hash: hashedPassword, salt, iterations } = await hashPassword(body.password); await payload.update({ collection: internal.usersCollectionSlug, id: docs[0].id, data: { hashedPassword, salt, hashIterations: iterations } }); const res = new Response(JSON.stringify({ message: "Password recovered successfully", kind: SuccessKind.Updated, isSuccess: true, isError: false }), { status: 201 }); res.headers.append("Set-Cookie", `${EPHEMERAL_CODE_COOKIE_NAME}=;Path=/;HttpOnly;Secure=true;SameSite=lax;Expires=Thu, 01 Jan 1970 00:00:00 GMT`); return res; }; var ResetPassword = async (cookieName, secret, internal, request) => { const { payload } = request; const cookies = parseCookies(request.headers); const token = cookies.get(cookieName); if (!token) { return new UnauthorizedAPIRequest; } const jwtResponse = await verifySessionCookie(token, secret); if (!jwtResponse.payload) { return new UnauthorizedAPIRequest; } const body = request.json && await request.json(); if (!body?.email || !body?.currentPassword || !body?.newPassword) { return new InvalidRequestBodyError; } const { docs } = await payload.find({ collection: internal.usersCollectionSlug, where: { email: { equals: body.email } }, limit: 1 }); if (docs.length !== 1) { return new UserNotFoundAPIError; } const user = docs[0]; const isVerifed = await verifyPassword(body.currentPassword, user["hashedPassword"], user["salt"], user["hashIterations"]); if (!isVerifed) { return new InvalidCredentials; } const { hash: hashedPassword, salt, iterations } = await hashPassword(body.newPassword); await payload.update({ collection: internal.usersCollectionSlug, id: user.id, data: { hashedPassword, salt, hashIterations: iterations } }); if (!!body.signoutOnUpdate) { let cookies2 = []; cookies2 = [...invalidateSessionCookies(cookieName, cookies2)]; return revokeSession(cookies2); } const res = new Response(JSON.stringify({ message: "Password reset complete", kind: SuccessKind.Updated, isSuccess: true, isError: false }), { status: 201 }); return res; }; export { ResetPassword, PasswordSignup, PasswordSignin, ForgotPasswordVerify, ForgotPasswordInit };