chore: heygermany customer project
This commit is contained in:
164
packages/functions/src/wirings/auth.wiring.js
Normal file
164
packages/functions/src/wirings/auth.wiring.js
Normal file
@@ -0,0 +1,164 @@
|
||||
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
|
||||
Reference in New Issue
Block a user