43 lines
1.4 KiB
TypeScript
43 lines
1.4 KiB
TypeScript
|
import { resolver, generateToken, hash256 } from "blitz"
|
||
|
|
||
|
import db from "../../../db"
|
||
|
import { forgotPasswordMailer } from "../../../mailers/forgot-password-mailer"
|
||
|
import { ForgotPassword } from "../validations"
|
||
|
|
||
|
const RESET_PASSWORD_TOKEN_EXPIRATION_IN_HOURS = 4
|
||
|
|
||
|
export default resolver.pipe(resolver.zod(ForgotPassword), async ({ email }) => {
|
||
|
// 1. Get the user
|
||
|
const user = await db.user.findFirst({ where: { email: email.toLowerCase() } })
|
||
|
|
||
|
// 2. Generate the token and expiration date.
|
||
|
const token = generateToken()
|
||
|
const hashedToken = hash256(token)
|
||
|
const expiresAt = new Date()
|
||
|
expiresAt.setHours(expiresAt.getHours() + RESET_PASSWORD_TOKEN_EXPIRATION_IN_HOURS)
|
||
|
|
||
|
// 3. If user with this email was found
|
||
|
if (user) {
|
||
|
// 4. Delete any existing password reset tokens
|
||
|
await db.token.deleteMany({ where: { type: "RESET_PASSWORD", userId: user.id } })
|
||
|
// 5. Save this new token in the database.
|
||
|
await db.token.create({
|
||
|
data: {
|
||
|
user: { connect: { id: user.id } },
|
||
|
type: "RESET_PASSWORD",
|
||
|
expiresAt,
|
||
|
hashedToken,
|
||
|
sentTo: user.email,
|
||
|
},
|
||
|
})
|
||
|
// 6. Send the email
|
||
|
await forgotPasswordMailer({ to: user.email, token }).send()
|
||
|
} else {
|
||
|
// 7. If no user found wait the same time so attackers can't tell the difference
|
||
|
await new Promise((resolve) => setTimeout(resolve, 750))
|
||
|
}
|
||
|
|
||
|
// 8. Return the same result whether a password reset email was sent or not
|
||
|
return
|
||
|
})
|