From 58949cc6664e7262c006a58fc856e963f3bfe774 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 9 Oct 2025 13:42:13 -0400 Subject: [PATCH] Fix OTP sign-in failure caused by email case-sensitivity on portal (#1623) * fix(app): store emails to be lowercase * fix(portal): prevent automatic user creation on OTP sign-in * fix(portal): don't show OTP input when sending OTP code is failed --------- Co-authored-by: chasprowebdev --- .../people/all/actions/addEmployeeWithoutInvite.ts | 7 +++++-- .../[orgId]/people/all/components/InviteMembersModal.tsx | 8 ++++---- apps/app/src/lib/db/employee.ts | 9 +++++++-- apps/portal/src/app/components/otp.tsx | 4 +++- apps/portal/src/app/lib/auth.ts | 2 ++ 5 files changed, 21 insertions(+), 9 deletions(-) diff --git a/apps/app/src/app/(app)/[orgId]/people/all/actions/addEmployeeWithoutInvite.ts b/apps/app/src/app/(app)/[orgId]/people/all/actions/addEmployeeWithoutInvite.ts index db45b5648..5ee3eae37 100644 --- a/apps/app/src/app/(app)/[orgId]/people/all/actions/addEmployeeWithoutInvite.ts +++ b/apps/app/src/app/(app)/[orgId]/people/all/actions/addEmployeeWithoutInvite.ts @@ -36,9 +36,12 @@ export const addEmployeeWithoutInvite = async ({ } let userId = ''; - const existingUser = await db.user.findUnique({ + const existingUser = await db.user.findFirst({ where: { - email, + email: { + equals: email, + mode: 'insensitive', + }, }, }); diff --git a/apps/app/src/app/(app)/[orgId]/people/all/components/InviteMembersModal.tsx b/apps/app/src/app/(app)/[orgId]/people/all/components/InviteMembersModal.tsx index cca623854..b9a0b0108 100644 --- a/apps/app/src/app/(app)/[orgId]/people/all/components/InviteMembersModal.tsx +++ b/apps/app/src/app/(app)/[orgId]/people/all/components/InviteMembersModal.tsx @@ -164,13 +164,13 @@ export function InviteMembersModal({ if (hasEmployeeRoleAndNoAdmin) { await addEmployeeWithoutInvite({ organizationId, - email: invite.email, + email: invite.email.toLowerCase(), roles: invite.roles, }); } else { // Use authClient to send the invitation await authClient.organization.inviteMember({ - email: invite.email, + email: invite.email.toLowerCase(), role: invite.roles.length === 1 ? invite.roles[0] : invite.roles, }); } @@ -325,12 +325,12 @@ export function InviteMembersModal({ if (hasEmployeeRoleAndNoAdmin) { await addEmployeeWithoutInvite({ organizationId, - email, + email: email.toLowerCase(), roles: validRoles, }); } else { await authClient.organization.inviteMember({ - email, + email: email.toLowerCase(), role: validRoles, }); } diff --git a/apps/app/src/lib/db/employee.ts b/apps/app/src/lib/db/employee.ts index 1fd2971b6..04fa786bb 100644 --- a/apps/app/src/lib/db/employee.ts +++ b/apps/app/src/lib/db/employee.ts @@ -25,8 +25,13 @@ export async function completeEmployeeCreation(params: { console.log(`Starting employee creation for ${email}`); // Check if the user already exists - const existingUser = await db.user.findUnique({ - where: { email }, + const existingUser = await db.user.findFirst({ + where: { + email: { + equals: email, + mode: 'insensitive', + }, + }, }); let employee: Member; diff --git a/apps/portal/src/app/components/otp.tsx b/apps/portal/src/app/components/otp.tsx index b1f08506c..e1989d676 100644 --- a/apps/portal/src/app/components/otp.tsx +++ b/apps/portal/src/app/components/otp.tsx @@ -45,9 +45,11 @@ export function OtpSignIn({ className }: Props) { if (error) { setLoading(false); toast.error(error.message); + setSent(false); + } else { + setSent(true); } - setSent(true); setLoading(false); } diff --git a/apps/portal/src/app/lib/auth.ts b/apps/portal/src/app/lib/auth.ts index 45b8fc073..1fec11bc8 100644 --- a/apps/portal/src/app/lib/auth.ts +++ b/apps/portal/src/app/lib/auth.ts @@ -56,6 +56,8 @@ export const auth = betterAuth({ emailOTP({ otpLength: 6, expiresIn: 10 * 60, + // Prevent automatic user creation on OTP sign-in + disableSignUp: true, async sendVerificationOTP({ email, otp }) { await sendEmail({ to: email,