Files
neta/server/auth/auth.ts
T

96 lines
2.2 KiB
TypeScript

import "server-only";
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { nextCookies } from "better-auth/next-js";
import { getServerConfig } from "@/server/config";
import { getSqliteConnection } from "@/server/db/client";
import * as schema from "@/server/db/schema";
import {
completeFirstFreelancerSetup,
recordAuthAuditEvent,
reserveFirstFreelancerSetup,
} from "@/server/auth/setup";
const config = getServerConfig();
export const auth = betterAuth({
appName: "Neta",
baseURL: config.appUrl,
trustedOrigins: config.trustedOrigins,
secret: config.betterAuthSecret,
database: drizzleAdapter(getSqliteConnection().db, {
provider: "sqlite",
schema,
transaction: true,
}),
emailAndPassword: {
enabled: true,
minPasswordLength: 8,
maxPasswordLength: 128,
requireEmailVerification: false,
},
rateLimit: {
enabled: true,
window: 60,
max: 60,
customRules: {
"/sign-in/email": {
window: 60,
max: 10,
},
"/sign-up/email": {
window: 300,
max: 3,
},
},
},
advanced: {
useSecureCookies: config.nodeEnv === "production",
cookiePrefix: "neta",
defaultCookieAttributes: {
httpOnly: true,
sameSite: "lax",
secure: config.nodeEnv === "production",
path: "/",
},
},
databaseHooks: {
user: {
create: {
before: async (user) => {
const isReserved = await reserveFirstFreelancerSetup(user.email);
return isReserved;
},
after: async (user) => {
await completeFirstFreelancerSetup(user);
},
},
},
session: {
create: {
after: async (session) => {
await recordAuthAuditEvent({
type: "login_succeeded",
authUserId: session.userId,
metadata: { source: "session_create" },
});
},
},
delete: {
after: async (session) => {
await recordAuthAuditEvent({
type: "logout_succeeded",
authUserId: session.userId,
metadata: { source: "session_delete" },
});
},
},
},
},
plugins: [nextCookies()],
});
export type Auth = typeof auth;