feat: add email/password auth backend + forgot/reset password routes

- Configure Better Auth emailAndPassword plugin with Resend
- Add email verification and password reset email sending
- Create forgot-password and reset-password frontend routes
- Add auth schemas to @lila/shared
This commit is contained in:
lila 2026-04-30 18:30:20 +02:00
parent 35e54014b3
commit 6297dff399
10 changed files with 317 additions and 0 deletions

View file

@ -17,6 +17,7 @@
"express": "^5.2.1",
"express-rate-limit": "^8.4.0",
"helmet": "^8.1.0",
"resend": "^6.12.2",
"ws": "^8.20.0"
},
"devDependencies": {

View file

@ -1,8 +1,12 @@
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { Resend } from "resend";
import { db } from "@lila/db";
import * as schema from "@lila/db/schema";
const resend = new Resend(process.env["RESEND_API_KEY"]);
const emailFrom = process.env["EMAIL_FROM"] ?? "noreply@lilastudy.com";
export const auth = betterAuth({
baseURL: process.env["BETTER_AUTH_URL"] || "http://localhost:3000",
advanced: {
@ -16,6 +20,30 @@ export const auth = betterAuth({
},
},
database: drizzleAdapter(db, { provider: "pg", schema }),
emailAndPassword: {
enabled: true,
requireEmailVerification: true,
sendResetPassword: async ({ user, url }) => {
await resend.emails.send({
from: emailFrom,
to: user.email,
subject: "Reset your lila password",
html: `<p>Click <a href="${url}">here</a> to reset your password. This link expires in 1 hour.</p>`,
});
},
},
emailVerification: {
sendVerificationEmail: async ({ user, url }) => {
await resend.emails.send({
from: emailFrom,
to: user.email,
subject: "Verify your lila account",
html: `<p>Click <a href="${url}">here</a> to verify your email address.</p>`,
});
},
sendOnSignUp: true,
autoSignInAfterVerification: true,
},
trustedOrigins: [process.env["CORS_ORIGIN"] || "http://localhost:5173"],
socialProviders: {
google: {