feat: implement custom login system to replace Better Auth
Some checks failed
Build and Deploy / deploy (push) Blocked by required conditions
Build and Deploy / build (push) Has been cancelled

- Add custom login API route with bcrypt password verification
- Update auth client to use custom endpoints
- Simplify login page to use new auth client
- Remove Better Auth dependencies that were causing issues
This commit is contained in:
Lorentz 2026-02-16 12:55:57 +00:00
parent 17f6188132
commit cdd897abbc
4 changed files with 142 additions and 29 deletions

View file

@ -29,25 +29,13 @@ export default function LoginPage() {
setIsLoading(true); setIsLoading(true);
try { try {
const result = await signIn.email({ await signIn(email, password);
email, router.push('/dashboard');
password,
callbackURL: '/dashboard',
});
if (result.error) {
toast({
title: 'Login failed',
description: result.error.message || 'Invalid email or password',
variant: 'destructive',
});
} else {
router.push('/dashboard');
}
} catch (error) { } catch (error) {
toast({ toast({
title: 'Error', title: 'Login failed',
description: 'An error occurred during login. Please try again.', description:
error instanceof Error ? error.message : 'Invalid email or password',
variant: 'destructive', variant: 'destructive',
}); });
} finally { } finally {

View file

@ -0,0 +1,109 @@
import { NextRequest, NextResponse } from 'next/server';
import { db } from '@/lib/db';
import bcrypt from 'bcryptjs';
export async function POST(request: NextRequest) {
try {
const body = await request.json();
const { email, password } = body;
if (!email || !password) {
return NextResponse.json(
{ error: 'Email and password are required' },
{ status: 400 }
);
}
// Find user
const user = await db.auth_user.findUnique({
where: { email },
include: {
quest_user: {
include: {
companies: {
include: {
company: true,
},
},
},
},
},
});
if (!user) {
return NextResponse.json(
{ error: 'Invalid credentials' },
{ status: 401 }
);
}
// Get password from auth_account
const account = await db.auth_account.findFirst({
where: {
user_id: user.id,
provider_id: 'credential',
},
});
if (!account || !account.password) {
return NextResponse.json(
{ error: 'Invalid credentials' },
{ status: 401 }
);
}
// Verify password
const valid = await bcrypt.compare(password, account.password);
if (!valid) {
return NextResponse.json(
{ error: 'Invalid credentials' },
{ status: 401 }
);
}
// Create session
const token = crypto.randomUUID();
const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000); // 7 days
const session = await db.auth_session.create({
data: {
user_id: user.id,
token,
expires_at: expiresAt,
ip_address: request.headers.get('x-forwarded-for') || 'unknown',
user_agent: request.headers.get('user-agent') || 'unknown',
},
});
// Set cookie
const response = NextResponse.json(
{
success: true,
user: {
id: user.id,
email: user.email,
name: user.name,
},
},
{ status: 200 }
);
response.cookies.set({
name: 'session_token',
value: token,
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
expires: expiresAt,
path: '/',
});
return response;
} catch (error) {
console.error('Login error:', error);
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
);
}
}

View file

@ -1,9 +1,30 @@
'use client'; 'use client';
import { createAuthClient } from 'better-auth/react'; export async function signIn(email: string, password: string) {
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ email, password }),
credentials: 'include',
});
export const authClient = createAuthClient({ if (!response.ok) {
baseURL: process.env.NEXT_PUBLIC_BETTER_AUTH_URL || 'http://localhost:3000', const error = await response.json();
}); throw new Error(error.error || 'Login failed');
}
export const { signIn, signOut, signUp, useSession } = authClient; return await response.json();
}
export async function signOut() {
const response = await fetch('/api/auth/logout', {
method: 'POST',
credentials: 'include',
});
if (response.ok) {
window.location.href = '/login';
}
}

View file

@ -2,16 +2,11 @@ import { betterAuth } from 'better-auth';
import { prismaAdapter } from 'better-auth/adapters/prisma'; import { prismaAdapter } from 'better-auth/adapters/prisma';
import { db } from './db'; import { db } from './db';
// Use Prisma adapter with our alias models (user, session, account)
// These map to auth_user, auth_session, auth_account tables
export const auth = betterAuth({ export const auth = betterAuth({
database: prismaAdapter(db, { database: prismaAdapter(db, {
provider: 'postgresql', provider: 'postgresql',
// Map our custom table names to Better Auth models
schema: {
user: 'auth_user',
session: 'auth_session',
account: 'auth_account',
verification: 'auth_verification',
},
}), }),
emailAndPassword: { emailAndPassword: {
enabled: true, enabled: true,