A free, open, self-hosted app platform (GNU AGPLv3): one-click app deploys, Traefik reverse proxy with automatic SSL, rootless Docker support, gluetun VPN routing, and a web dashboard to manage it all. Free & open forever to self-host; optional paid hosted services fund it. See PROMISE.md. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Signed-off-by: librelad <librelad@digitalangels.vip>
75 lines
2.3 KiB
TypeScript
75 lines
2.3 KiB
TypeScript
import NextAuth from "next-auth";
|
|
import Credentials from "next-auth/providers/credentials";
|
|
import { DrizzleAdapter } from "@auth/drizzle-adapter";
|
|
import bcrypt from "bcryptjs";
|
|
import { eq } from "drizzle-orm";
|
|
import { db } from "@/db";
|
|
import {
|
|
users,
|
|
authAccounts,
|
|
sessions,
|
|
verificationTokens,
|
|
} from "@/db/schema";
|
|
import { authConfig } from "@/auth.config";
|
|
|
|
export const { handlers, signIn, signOut, auth } = NextAuth({
|
|
...authConfig,
|
|
adapter: DrizzleAdapter(db, {
|
|
usersTable: users,
|
|
accountsTable: authAccounts,
|
|
sessionsTable: sessions,
|
|
verificationTokensTable: verificationTokens,
|
|
}),
|
|
session: { strategy: "jwt" },
|
|
providers: [
|
|
Credentials({
|
|
name: "Credentials",
|
|
credentials: {
|
|
email: { label: "Email", type: "email" },
|
|
password: { label: "Password", type: "password" },
|
|
},
|
|
authorize: async (credentials) => {
|
|
const email = String(credentials?.email ?? "").toLowerCase().trim();
|
|
const password = String(credentials?.password ?? "");
|
|
if (!email || !password) return null;
|
|
const [u] = await db.select().from(users).where(eq(users.email, email));
|
|
if (!u || !u.passwordHash) return null;
|
|
const ok = await bcrypt.compare(password, u.passwordHash);
|
|
if (!ok) return null;
|
|
return {
|
|
id: u.id,
|
|
email: u.email,
|
|
name: u.name ?? u.email,
|
|
image: u.image ?? undefined,
|
|
};
|
|
},
|
|
}),
|
|
],
|
|
callbacks: {
|
|
...authConfig.callbacks,
|
|
async jwt({ token, user }) {
|
|
if (user?.id) token.userId = user.id;
|
|
if (token.userId) {
|
|
const [u] = await db
|
|
.select({ householdId: users.householdId, role: users.role })
|
|
.from(users)
|
|
.where(eq(users.id, token.userId as string));
|
|
if (u) {
|
|
token.householdId = u.householdId ?? null;
|
|
token.role = u.role;
|
|
}
|
|
}
|
|
return token;
|
|
},
|
|
async session({ session, token }) {
|
|
if (session.user) {
|
|
session.user.id = token.userId as string;
|
|
(session.user as { householdId?: string | null }).householdId =
|
|
(token.householdId as string | null) ?? null;
|
|
(session.user as { role?: string }).role = (token.role as string) ?? "member";
|
|
}
|
|
return session;
|
|
},
|
|
},
|
|
});
|