Organize/auth.ts

66 lines
2.4 KiB
TypeScript

import NextAuth from "next-auth";
import Credentials from "next-auth/providers/credentials";
import bcrypt from "bcryptjs";
import { prisma } from "@/lib/db";
import { LoginSchema } from "@/lib/validation/auth";
import { Role } from "@/lib/generated/prisma/enums";
import { PendingAccountSignin } from "@/lib/auth-errors";
// Credentials-only, JWT sessions, no database adapter: with a single
// Credentials provider and no OAuth, there's nothing for a DB-backed
// Session/Account table to do, so we skip the Prisma adapter entirely.
export const { handlers, auth, signIn, signOut } = NextAuth({
session: { strategy: "jwt" },
// Self-hosted behind whatever host/port the operator points at it (not a
// platform Auth.js auto-detects, like Vercel) -- without this, Auth.js
// rejects every request with an UntrustedHost error.
trustHost: true,
pages: {
signIn: "/login",
},
providers: [
Credentials({
credentials: {
email: {},
password: {},
},
async authorize(rawCredentials) {
const parsed = LoginSchema.safeParse(rawCredentials);
if (!parsed.success) return null;
const { email, password } = parsed.data;
const user = await prisma.user.findUnique({ where: { email } });
if (!user) return null;
const passwordsMatch = await bcrypt.compare(password, user.passwordHash);
if (!passwordsMatch) return null;
// PENDING accounts are waiting on admin approval (or, eventually,
// email confirmation) and can't log in yet -- see lib/settings.ts.
if (user.role === Role.PENDING) throw new PendingAccountSignin();
return { id: user.id, email: user.email, name: user.name, role: user.role };
},
}),
],
callbacks: {
async jwt({ token, user }) {
if (user?.id) {
token.sub = user.id;
token.role = user.role;
}
return token;
},
async session({ session, token }) {
if (session.user && token.sub) session.user.id = token.sub;
// Cached in the JWT at login time -- a role change made through the
// Admin page won't take effect for that user until their next login.
// No session-invalidation mechanism yet; fine for now, revisit if
// role changes need to apply immediately.
if (session.user && token.role) session.user.role = token.role;
return session;
},
},
});