diff --git a/apps/web/app/api/auth/signup/handlers/calcomSignupHandler.ts b/apps/web/app/api/auth/signup/handlers/calcomSignupHandler.ts index a31bbfaee0a6c1..c5e8a0efa7da75 100644 --- a/apps/web/app/api/auth/signup/handlers/calcomSignupHandler.ts +++ b/apps/web/app/api/auth/signup/handlers/calcomSignupHandler.ts @@ -102,15 +102,22 @@ const handler: CustomNextApiHandler = async (body, usernameStatus, query) => { isSignup: true, }); - if (foundToken?.teamId) { - const existingUser = await prisma.user.findUnique({ - where: { email }, - select: { invitedTo: true }, - }); - if (existingUser && existingUser.invitedTo !== foundToken.teamId) { - return NextResponse.json({ message: SIGNUP_ERROR_CODES.USER_ALREADY_EXISTS }, { status: 409 }); + if (foundToken?.teamId) { + const existingUser = await prisma.user.findUnique({ + where: { email }, + select: { invitedTo: true, emailVerified: true, password: { select: { hash: true } } }, + }); + if (existingUser) { + const isInvitedPlaceholderAccount = + existingUser.invitedTo === foundToken.teamId && + !existingUser.password?.hash && + !existingUser.emailVerified; + + if (!isInvitedPlaceholderAccount) { + return NextResponse.json({ message: SIGNUP_ERROR_CODES.USER_ALREADY_EXISTS }, { status: 409 }); + } + } } - } } else { const usernameAndEmailValidation = await validateAndGetCorrectedUsernameAndEmail({ username, diff --git a/apps/web/app/api/auth/signup/handlers/selfHostedHandler.ts b/apps/web/app/api/auth/signup/handlers/selfHostedHandler.ts index 9bea17ef643d93..ad9d9ad4bb7266 100644 --- a/apps/web/app/api/auth/signup/handlers/selfHostedHandler.ts +++ b/apps/web/app/api/auth/signup/handlers/selfHostedHandler.ts @@ -46,10 +46,17 @@ export default async function handler(body: Record) { if (foundToken?.teamId) { const existingUser = await prisma.user.findUnique({ where: { email: userEmail }, - select: { invitedTo: true }, + select: { invitedTo: true, emailVerified: true, password: { select: { hash: true } } }, }); - if (existingUser && existingUser.invitedTo !== foundToken.teamId) { - return NextResponse.json({ message: SIGNUP_ERROR_CODES.USER_ALREADY_EXISTS }, { status: 409 }); + if (existingUser) { + const isInvitedPlaceholderAccount = + existingUser.invitedTo === foundToken.teamId && + !existingUser.password?.hash && + !existingUser.emailVerified; + + if (!isInvitedPlaceholderAccount) { + return NextResponse.json({ message: SIGNUP_ERROR_CODES.USER_ALREADY_EXISTS }, { status: 409 }); + } } } } else { diff --git a/packages/features/auth/lib/next-auth-options.ts b/packages/features/auth/lib/next-auth-options.ts index 296e64559fdd36..ee102db8656ba7 100644 --- a/packages/features/auth/lib/next-auth-options.ts +++ b/packages/features/auth/lib/next-auth-options.ts @@ -998,10 +998,10 @@ export const getOptions = ({ } // check if user was invited + // Invited placeholder users may already have a generated username; password/email verification + // are the reliable signals that account setup is incomplete and should be completed here. if ( - !existingUserWithEmail.password?.hash && - !existingUserWithEmail.emailVerified && - !existingUserWithEmail.username + !existingUserWithEmail.password?.hash && !existingUserWithEmail.emailVerified ) { await prisma.user.update({ where: {