Files
2026-08-01 14:01:17 +02:00

49 lines
1.4 KiB
TypeScript

import NextAuth, { NextAuthOptions } from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import bcrypt from "bcryptjs";
import { getDb } from "@/lib/db";
import { isRateLimited } from "@/lib/rate-limit";
type UserRow = { id: string; email: string; password_hash: string };
export const authOptions: NextAuthOptions = {
providers: [
CredentialsProvider({
name: "Credentials",
credentials: {
email: { label: "Email", type: "email" },
password: { label: "Password", type: "password" },
},
async authorize(credentials, req) {
if (!credentials?.email || !credentials?.password) return null;
// Throttle login attempts per IP — this is the password brute-force surface.
if (isRateLimited(req, "login", 10, 15 * 60 * 1000)) {
throw new Error("Too many attempts. Try again later.");
}
const db = getDb();
const user = db
.prepare("SELECT id, email, password_hash FROM users WHERE email = ?")
.get(credentials.email) as UserRow | undefined;
if (!user) return null;
const valid = await bcrypt.compare(credentials.password, user.password_hash);
if (!valid) return null;
return { id: user.id, email: user.email };
},
}),
],
pages: {
signIn: "/login",
},
session: {
strategy: "jwt",
},
secret: process.env.NEXTAUTH_SECRET,
};
export default NextAuth(authOptions);