Files
kan/packages/auth/src/auth.ts
2025-05-22 12:18:56 +01:00

119 lines
3.3 KiB
TypeScript

import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { createAuthMiddleware } from "better-auth/api";
import { apiKey } from "better-auth/plugins";
import { magicLink } from "better-auth/plugins/magic-link";
import type { dbClient } from "@kan/db/client";
import * as memberRepo from "@kan/db/repository/member.repo";
import * as userRepo from "@kan/db/repository/user.repo";
import * as schema from "@kan/db/schema";
import { sendEmail } from "@kan/email";
import { createStripeClient } from "@kan/stripe";
export const initAuth = (db: dbClient) => {
return betterAuth({
secret: process.env.BETTER_AUTH_SECRET!,
baseURL: process.env.BETTER_AUTH_BASE_URL!,
trustedOrigins: process.env.BETTER_AUTH_TRUSTED_ORIGINS
? process.env.BETTER_AUTH_TRUSTED_ORIGINS.split(",")
: [],
database: drizzleAdapter(db, {
provider: "pg",
schema: {
...schema,
user: schema.users,
},
}),
socialProviders: {
google: {
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
},
},
user: {
additionalFields: {
stripeCustomerId: {
type: "string",
required: false,
defaultValue: null,
input: false,
},
},
},
plugins: [
apiKey(),
magicLink({
expiresIn: 60 * 60 * 24 * 7, // 7 days
sendMagicLink: async ({ email, url }) => {
if (url.includes("type=invite")) {
await sendEmail(
email,
"Invitation to join workspace",
"JOIN_WORKSPACE",
{
magicLoginUrl: url,
},
);
} else {
await sendEmail(email, "Sign in to kan.bn", "MAGIC_LINK", {
magicLoginUrl: url,
});
}
},
}),
],
hooks: {
after: createAuthMiddleware(async (ctx) => {
if (ctx.path.startsWith("/get-session")) {
const user = ctx.context.session?.user;
if (
process.env.NEXT_PUBLIC_KAN_ENV === "cloud" &&
user &&
!user.stripeCustomerId
) {
const stripe = createStripeClient();
const stripeCustomer = await stripe.customers.create({
email: user.email,
metadata: {
userId: user.id,
},
});
await userRepo.update(db, user.id, {
stripeCustomerId: stripeCustomer.id,
});
}
} else if (
ctx.path === "/magic-link/verify" &&
(ctx.query?.callbackURL as string | undefined)?.includes(
"type=invite",
) &&
ctx.query?.memberPublicId
) {
const userId = ctx.context.newSession?.session.userId;
const memberPublicId = ctx.query.memberPublicId as string;
if (userId) {
const member = await memberRepo.getByPublicId(db, memberPublicId);
if (member?.id) {
await memberRepo.acceptInvite(db, {
memberId: member.id,
userId,
});
}
}
}
}),
},
advanced: {
cookiePrefix: "kan",
database: {
generateId: false,
},
},
});
};