22
33import { ratelimit } from "@/lib/upstash" ;
44import { prisma } from "@dub/prisma" ;
5+ import { waitUntil } from "@vercel/functions" ;
56import { flattenValidationErrors } from "next-safe-action" ;
67import { createId } from "../api/create-id" ;
7- import { getIP } from "../api/utils" ;
88import { hashPassword } from "../auth/password" ;
99import z from "../zod" ;
1010import { signUpSchema } from "../zod/schemas/auth" ;
@@ -15,6 +15,9 @@ const schema = signUpSchema.extend({
1515 code : z . string ( ) . min ( 6 , "OTP must be 6 characters long." ) ,
1616} ) ;
1717
18+ const MAX_OTP_ATTEMPTS = 5 ; // Block after 5 failed attempts
19+ const OTP_LOCKOUT_DURATION = "24 h" ; // Block for 24 hours
20+
1821// Sign up a new user using email and password
1922export const createUserAccountAction = actionClient
2023 . schema ( schema , {
@@ -25,26 +28,45 @@ export const createUserAccountAction = actionClient
2528 . action ( async ( { parsedInput } ) => {
2629 const { email, password, code } = parsedInput ;
2730
28- const { success } = await ratelimit ( 2 , "1 m" ) . limit ( `signup:${ getIP ( ) } ` ) ;
31+ const signupAttemptKey = `signup:attempts:${ email } ` ;
32+
33+ const { remaining : attemptsRemaining } = await ratelimit (
34+ MAX_OTP_ATTEMPTS ,
35+ OTP_LOCKOUT_DURATION ,
36+ ) . getRemaining ( signupAttemptKey ) ;
2937
30- if ( ! success ) {
31- throw new Error ( "Too many requests. Please try again later." ) ;
38+ if ( attemptsRemaining <= 0 ) {
39+ throw new Error ( "Too many failed attempts. You have to try again later." ) ;
3240 }
3341
3442 const verificationToken = await prisma . emailVerificationToken . findUnique ( {
3543 where : {
3644 identifier : email ,
3745 token : code ,
38- expires : {
39- gte : new Date ( ) ,
40- } ,
4146 } ,
4247 } ) ;
4348
4449 if ( ! verificationToken ) {
50+ await ratelimit ( MAX_OTP_ATTEMPTS , OTP_LOCKOUT_DURATION ) . limit (
51+ signupAttemptKey ,
52+ ) ;
53+
4554 throw new Error ( "Invalid verification code entered." ) ;
4655 }
4756
57+ if ( verificationToken . expires && verificationToken . expires < new Date ( ) ) {
58+ waitUntil (
59+ prisma . emailVerificationToken . delete ( {
60+ where : {
61+ identifier : email ,
62+ token : code ,
63+ } ,
64+ } ) ,
65+ ) ;
66+
67+ throw new Error ( "The OTP has expired. Please request a new one." ) ;
68+ }
69+
4870 await prisma . emailVerificationToken . delete ( {
4971 where : {
5072 identifier : email ,
0 commit comments