import { betterAuth } from 'better-auth'; import { magicLink } from 'better-auth/plugins'; import { pikkuBetterAuth } from '#pikku/pikku-types.gen.js'; import { UserRoles, UserTypes } from '@heygermany/sdk'; const DEV_BETTER_AUTH_SECRET = 'dev-better-auth-secret-change-me'; const isLocalDomain = (domain) => { return !domain || domain.includes('localhost') || domain.includes('127.0.0.1'); }; const getOrigin = (domain) => { if (isLocalDomain(domain)) { return 'http://localhost:3000'; } return `https://api.${domain}`; }; export const auth = pikkuBetterAuth(async ({ kysely, secrets, variables, config, emailService }) => { const betterAuthSecret = (await variables.get('BETTER_AUTH_SECRET')) || await secrets.getSecret('BETTER_AUTH_SECRET').catch(() => DEV_BETTER_AUTH_SECRET); if (process.env.NODE_ENV === 'production' && betterAuthSecret === DEV_BETTER_AUTH_SECRET) { throw new Error('BETTER_AUTH_SECRET must be configured in production'); } return betterAuth({ secret: betterAuthSecret, baseURL: getOrigin(config.domain), trustedOrigins: [ 'http://localhost:3001', 'http://localhost:3000', 'http://127.0.0.1:3001', 'http://127.0.0.1:3000', `https://${config.domain}`, `https://api.${config.domain}`, 'https://*.pikkufabric.dev', ], advanced: { database: { generateId: 'uuid', }, crossSubDomainCookies: { enabled: !isLocalDomain(config.domain), domain: isLocalDomain(config.domain) ? undefined : config.domain, }, }, database: { db: kysely, type: 'sqlite', }, user: { modelName: 'authUser', fields: { emailVerified: 'emailVerified', createdAt: 'createdAt', updatedAt: 'updatedAt', }, additionalFields: { role: { type: 'string', required: true, defaultValue: UserRoles.OWNER, input: true, }, type: { type: 'string', required: true, defaultValue: UserTypes.CANDIDATE, input: true, }, }, }, session: { modelName: 'authSession', fields: { expiresAt: 'expiresAt', createdAt: 'createdAt', updatedAt: 'updatedAt', ipAddress: 'ipAddress', userAgent: 'userAgent', userId: 'userId', }, }, account: { modelName: 'authAccount', fields: { accountId: 'accountId', providerId: 'providerId', userId: 'userId', accessToken: 'accessToken', refreshToken: 'refreshToken', idToken: 'idToken', accessTokenExpiresAt: 'accessTokenExpiresAt', refreshTokenExpiresAt: 'refreshTokenExpiresAt', createdAt: 'createdAt', updatedAt: 'updatedAt', }, }, verification: { modelName: 'authVerification', fields: { expiresAt: 'expiresAt', createdAt: 'createdAt', updatedAt: 'updatedAt', }, }, emailAndPassword: { enabled: true, }, emailVerification: { sendVerificationEmail: async ({ user, url }) => { await emailService?.send({ to: user.email, template: { name: 'candidate-double-opt-in', locale: 'en', data: { firstname: user.name, verificationLink: url, }, }, }); }, }, plugins: [ magicLink({ disableSignUp: true, sendMagicLink: async ({ email: to, token, metadata }) => { const user = await kysely .selectFrom('authUser') .select(['id', 'name']) .where('email', '=', to) .executeTakeFirst(); const name = user?.name || to.split('@')[0]; const templateName = getMagicLinkTemplateName(metadata?.template); const magicLink = buildMagicLink(config.domain, token); await emailService?.send({ to, template: { name: templateName, locale: 'en', data: { firstname: name, magicLink, }, }, }); }, }), ], }); }); const buildMagicLink = (domain, token) => { const host = isLocalDomain(domain) ? `http://${domain}` : `https://${domain}`; return `${host}/auth/magic-link?token=${token}&redirect=/jobs/application`; }; const MAGIC_LINK_TEMPLATE_NAMES = new Set([ 'candidate-magic-link', 'candidate-qualified', 'candidate-invalid-documents', 'candidate-process-incomplete', ]); const getMagicLinkTemplateName = (template) => { if (typeof template === 'string' && MAGIC_LINK_TEMPLATE_NAMES.has(template)) { return template; } return 'candidate-magic-link'; }; //# sourceMappingURL=auth.wiring.js.map