import { env } from "@/env.server.mjs"; import { getPool } from "@/modules/database"; import { getServerUrl, getTrustedOrigins } from "@/modules/server-config"; import { betterAuth } from "better-auth"; import { nextCookies } from "better-auth/next-js"; import { emailOTP, magicLink, organization } from "better-auth/plugins"; /** * @todo make this typesafe */ export const organisationTables = { organization: { modelName: "organization", fields: { id: "id", name: "name", slug: "slug", type: "type", createdAt: "created_at", updatedAt: "updated_at", }, additionalFields: { type: { type: "string" as const, fieldName: "type", returned: true, input: true, required: true, } }, }, member: { modelName: "member", fields: { id: "id", organizationId: "organization_id", userId: "user_id", role: "role", metadata: "metadata", createdAt: "created_at", updatedAt: "updated_at", } }, session: { modelName: "session", fields: { userId: "user_id", token: "token", activeOrganizationId: "active_organization_id", ipAddress: "ip_address", createdAt: "created_at", updatedAt: "updated_at", expiresAt: "expires_at", userAgent: "user_agent", } }, invitation: { modelName: "invitation", fields: { id: "id", organizationId: "organization_id", inviterId: "inviter_id", email: "email", role: "role", status: "status", createdAt: "created_at", expiresAt: "expires_at", } } } export const orgPlugin = organization({ schema: { member: { ...organisationTables.member, }, organization: { ...organisationTables.organization, }, session: { ...organisationTables.session, }, invitation: { ...organisationTables.invitation, }, // No team support as of yet } }); export const authServer = betterAuth({ user: { modelName: "users", fields: { id: "id", email: "email", name: "name", emailVerified: "email_verified", createdAt: "created_at", updatedAt: "updated_at", image: "image", }, additionalFields: { firstName: { type: "string", fieldName: "firstName", returned: true, input: true, required: false, }, lastName: { type: "string", fieldName: "lastName", returned: true, input: true, required: false, }, }, }, session: { ...organisationTables.session, }, account: { modelName: "account", fields: { userId: "user_id", createdAt: "created_at", updatedAt: "updated_at", expiresAt: "expires_at", accessTokenExpiresAt: "access_token_expires_at", refreshTokenExpiresAt: "refresh_token_expires_at", scope: "scope", idToken: "id_token", password: "password", accessToken: "access_token", refreshToken: "refresh_token", accountId: "account_id", providerId: "provider_id", } }, verification: { modelName: "verification", fields: { value: "value", identifier: "identifier", createdAt: "created_at", updatedAt: "updated_at", expiresAt: "expires_at", } }, // Org specific schemas member: { ...organisationTables.member, }, organization: { ...organisationTables.organization, }, invitation: { ...organisationTables.invitation, }, appName: "testapp", secret: env.BETTER_AUTH_SECRET, logger: console, onAPIError: { throw: true, onError(e, context) { console.error("[BETTER_AUTH - ERROR]", e); console.error("[BETTER_AUTH - CONTEXT]", context); } }, trustedOrigins: getTrustedOrigins(), baseURL: getServerUrl(), // Simply returns Pg Pool instance database: getPool(), plugins: [ nextCookies(), magicLink({ expiresIn: 600, sendMagicLink: async ({ email, url }) => { }, }), emailOTP({ otpLength: 6, expiresIn: 600, // 10 minutes allowedAttempts: 3, sendVerificationOTP: async ({ email, otp, type }) => {} }), orgPlugin, ], }); export const auth = authServer; export default authServer;