import { drizzleAdapter } from "@better-auth/drizzle-adapter/relations-v2"; import { db } from "../server/lib/db"; import { APIError, createAuthMiddleware, getSessionFromCtx } from 'better-auth/api'; import { betterAuth } from 'better-auth/minimal'; import { createAccessControl } from 'better-auth/plugins/access'; import { admin } from 'better-auth/plugins/admin'; import { defaultStatements } from 'better-auth/plugins/admin/access'; import { twoFactor } from 'better-auth/plugins/two-factor'; import * as schema from '../drizzle/schema'; const accessControl = createAccessControl(defaultStatements); // Admins manage accounts, not content. Signing in as someone else and setting // another user's password or email are left out on purpose: the first would // show their chats, the others would lock them out of their encrypted API keys. const roles = { admin: accessControl.newRole({ user: ['create', 'list', 'set-role', 'ban', 'delete', 'get', 'update'], session: ['list', 'revoke', 'delete'], }), user: accessControl.newRole({ user: [], session: [], }), }; export const auth = betterAuth({ database: drizzleAdapter(db, { provider: "pg", schema, usePlural: true, }), session: { expiresIn: 60 * 60 * 24 * 30, // 1 month updateAge: 60 * 60 * 24 * 1, // 1 day cookieCache: { enabled: true, maxAge: 5 * 60, }, }, baseURL: process.env.NUXT_PUBLIC_URL ?? 'http://localhost:3000', trustedOrigins: [process.env.NUXT_PUBLIC_URL ?? 'http://localhost:3000'], hooks: { // The admin endpoints already stop admins from removing or disabling // themselves. Giving up their own admin role is the one way left to // end up with an instance nobody can manage, so that is refused too. before: createAuthMiddleware(async (ctx) => { if (ctx.path !== '/admin/set-role' && ctx.path !== '/admin/update-user') return; const role = ctx.path === '/admin/set-role' ? ctx.body?.role : ctx.body?.data?.role; if (role === undefined) return; const roleList = (Array.isArray(role) ? role : String(role).split(',')).map((r) => String(r).trim()); if (roleList.includes('admin')) return; const session = await getSessionFromCtx(ctx); if (session && String(ctx.body?.userId) === session.user.id) { throw new APIError('BAD_REQUEST', { message: 'You cannot remove your own admin role', code: 'YOU_CANNOT_DEMOTE_YOURSELF', }); } }), }, plugins: [ twoFactor({ issuer: 'Veridian', }), admin({ ac: accessControl, roles, }), ], emailAndPassword: { // if DISABLE_LOCAL_AUTH is set to "1" or "true" then local auth is disabled enabled: process.env.DISABLE_LOCAL_AUTH?.toLowerCase() !== 'true' && process.env.DISABLE_LOCAL_AUTH !== '1', disableSignUp: process.env.DISABLE_SIGNUP?.toLowerCase() === 'true' || process.env.DISABLE_SIGNUP === '1', }, });