diff --git a/src/app/api/auth/check-email/route.ts b/src/app/api/auth/check-email/route.ts index 9c62a02..7b029e1 100644 --- a/src/app/api/auth/check-email/route.ts +++ b/src/app/api/auth/check-email/route.ts @@ -24,8 +24,8 @@ export async function POST(req: NextRequest) { return NextResponse.json({ exists: false }, { status: 400 }) } - const user = await prisma.user.findUnique({ - where: { email: email.toLowerCase().trim() }, + const user = await prisma.user.findFirst({ + where: { email: { equals: email.toLowerCase().trim(), mode: 'insensitive' } }, select: { status: true }, }) diff --git a/src/lib/auth.ts b/src/lib/auth.ts index bd88afc..c4ad594 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -62,8 +62,8 @@ export const { handlers, auth, signIn, signOut } = NextAuth({ // Send magic links to any existing, non-suspended user. // This is the primary first-login path for applicants seeded from CSV // (status NONE) who have no password yet. - const existingUser = await prisma.user.findUnique({ - where: { email: email.toLowerCase().trim() }, + const existingUser = await prisma.user.findFirst({ + where: { email: { equals: email.toLowerCase().trim(), mode: 'insensitive' } }, select: { id: true, status: true }, }) if (!existingUser || existingUser.status === 'SUSPENDED') { @@ -164,9 +164,9 @@ export const { handlers, auth, signIn, signOut } = NextAuth({ throw new Error('Account temporarily locked due to too many failed attempts. Try again later.') } - // Find user by email - const user = await prisma.user.findUnique({ - where: { email }, + // Find user by email (case-insensitive — DB may store mixed-case emails) + const user = await prisma.user.findFirst({ + where: { email: { equals: email, mode: 'insensitive' } }, select: { id: true, email: true, @@ -371,8 +371,8 @@ export const { handlers, auth, signIn, signOut } = NextAuth({ async signIn({ user, account }) { // For email provider (magic link), check user status and get password info if (account?.provider === 'email') { - const dbUser = await prisma.user.findUnique({ - where: { email: user.email! }, + const dbUser = await prisma.user.findFirst({ + where: { email: { equals: user.email!, mode: 'insensitive' } }, select: { id: true, status: true, @@ -404,22 +404,24 @@ export const { handlers, auth, signIn, signOut } = NextAuth({ // Update last login time on actual sign-in, and activate INVITED users on login if (user.email) { - const loginUser = await prisma.user.findUnique({ - where: { email: user.email }, - select: { status: true }, - }) - await prisma.user.update({ - where: { email: user.email }, - data: { - lastLoginAt: new Date(), - // If user is still INVITED but successfully logged in, activate them - ...(loginUser && loginUser.status === 'INVITED' - ? { status: 'ACTIVE' } - : {}), - }, - }).catch(() => { - // Ignore errors from updating last login + const loginUser = await prisma.user.findFirst({ + where: { email: { equals: user.email, mode: 'insensitive' } }, + select: { id: true, status: true }, }) + if (loginUser) { + await prisma.user.update({ + where: { id: loginUser.id }, + data: { + lastLoginAt: new Date(), + // If user is still INVITED but successfully logged in, activate them + ...(loginUser.status === 'INVITED' + ? { status: 'ACTIVE' } + : {}), + }, + }).catch(() => { + // Ignore errors from updating last login + }) + } // Log successful login await prisma.auditLog.create({ diff --git a/src/server/routers/user.ts b/src/server/routers/user.ts index c76f100..0aedf67 100644 --- a/src/server/routers/user.ts +++ b/src/server/routers/user.ts @@ -1595,9 +1595,9 @@ export const userRouter = router({ .mutation(async ({ ctx, input }) => { const email = input.email.toLowerCase().trim() - // Find user by email - const user = await ctx.prisma.user.findUnique({ - where: { email }, + // Find user by email (case-insensitive — DB may store mixed-case emails) + const user = await ctx.prisma.user.findFirst({ + where: { email: { equals: email, mode: 'insensitive' } }, select: { id: true, email: true, name: true, status: true }, })