Fix verification attempts not transactional (#2549)

* Run verification token check in one transaction

* Only fetch unexpired verification requests
This commit is contained in:
ekzyis 2025-09-19 17:55:34 +02:00 committed by GitHub
parent 919e71de79
commit 06872a1314
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -327,38 +327,43 @@ export const getAuthOptions = (req, res) => ({
return user return user
}, },
useVerificationToken: async ({ identifier, token }) => { useVerificationToken: async ({ identifier, token }) => {
// we need to find the most recent verification request for this email/identifier return await prisma.$transaction(async (tx) => {
const verificationRequest = await prisma.verificationToken.findFirst({ const [verificationRequest] = await tx.$queryRaw`
where: { UPDATE verification_requests
identifier, SET attempts = attempts + 1
attempts: { FROM (
lt: 2 // count starts at 0 SELECT id FROM verification_requests
} WHERE identifier = ${identifier}
}, AND created_at > NOW() - INTERVAL '5 minutes'
orderBy: { -- we need to find the most recent verification request for this email/identifier
createdAt: 'desc' ORDER BY created_at DESC
LIMIT 1
FOR UPDATE
) for_update
WHERE verification_requests.id = for_update.id
RETURNING *
`
if (!verificationRequest) throw new Error('No verification request found')
if (verificationRequest.token === token) {
// correct token was entered, delete the verification request because we no longer need it
await tx.verificationToken.delete({
where: { id: verificationRequest.id }
})
return verificationRequest
} }
if (verificationRequest.attempts >= 3) {
// too many attempts, delete the verification request and redirect to error page by throwing an error
await tx.verificationToken.delete({
where: { id: verificationRequest.id }
})
throw new Error('too many attempts')
}
// wrong code but can try again
return null
}) })
if (!verificationRequest) throw new Error('No verification request found')
if (verificationRequest.token === token) { // if correct delete the token and continue
await prisma.verificationToken.delete({
where: { id: verificationRequest.id }
})
return verificationRequest
}
await prisma.verificationToken.update({
where: { id: verificationRequest.id },
data: { attempts: { increment: 1 } }
})
await prisma.verificationToken.deleteMany({
where: { id: verificationRequest.id, attempts: { gte: 2 } }
})
return null
} }
}, },
session: { session: {