Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:23:56 +08:00
commit dba45740a3
12 changed files with 3456 additions and 0 deletions

View File

@@ -0,0 +1,147 @@
/**
* Next.js API Route with better-auth
*
* This example demonstrates:
* - PostgreSQL with Drizzle ORM
* - Email/password + social auth
* - Email verification
* - Organizations plugin
* - 2FA plugin
* - Custom error handling
*/
import { betterAuth } from 'better-auth'
import { drizzle } from 'drizzle-orm/postgres-js'
import postgres from 'postgres'
import { twoFactor, organization } from 'better-auth/plugins'
import { sendEmail } from '@/lib/email' // Your email service
// Database connection
const client = postgres(process.env.DATABASE_URL!)
const db = drizzle(client)
// Initialize better-auth
export const auth = betterAuth({
database: db,
secret: process.env.BETTER_AUTH_SECRET!,
baseURL: process.env.NEXT_PUBLIC_APP_URL!,
// Email/password authentication
emailAndPassword: {
enabled: true,
requireEmailVerification: true,
// Custom email sending
sendVerificationEmail: async ({ user, url, token }) => {
await sendEmail({
to: user.email,
subject: 'Verify your email',
html: `
<h1>Verify your email</h1>
<p>Click the link below to verify your email address:</p>
<a href="${url}">Verify Email</a>
<p>Or enter this code: <strong>${token}</strong></p>
<p>This link expires in 24 hours.</p>
`
})
},
// Password reset email
sendResetPasswordEmail: async ({ user, url, token }) => {
await sendEmail({
to: user.email,
subject: 'Reset your password',
html: `
<h1>Reset your password</h1>
<p>Click the link below to reset your password:</p>
<a href="${url}">Reset Password</a>
<p>Or enter this code: <strong>${token}</strong></p>
<p>This link expires in 1 hour.</p>
`
})
}
},
// Social providers
socialProviders: {
google: {
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
scope: ['openid', 'email', 'profile']
},
github: {
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
scope: ['user:email', 'read:user']
},
microsoft: {
clientId: process.env.MICROSOFT_CLIENT_ID!,
clientSecret: process.env.MICROSOFT_CLIENT_SECRET!,
tenantId: process.env.MICROSOFT_TENANT_ID || 'common'
}
},
// Session configuration
session: {
expiresIn: 60 * 60 * 24 * 7, // 7 days
updateAge: 60 * 60 * 24, // Update every 24 hours
cookieCache: {
enabled: true,
maxAge: 60 * 5 // 5 minutes
}
},
// Advanced features via plugins
plugins: [
// Two-factor authentication
twoFactor({
methods: ['totp', 'sms'],
issuer: 'MyApp',
sendOTP: async ({ user, otp, method }) => {
if (method === 'sms') {
// Send SMS with OTP (use Twilio, etc.)
console.log(`Send SMS to ${user.phone}: ${otp}`)
}
}
}),
// Organizations and teams
organization({
roles: ['owner', 'admin', 'member'],
permissions: {
owner: ['*'], // All permissions
admin: ['read', 'write', 'delete', 'invite'],
member: ['read']
},
sendInvitationEmail: async ({ email, organizationName, inviteUrl }) => {
await sendEmail({
to: email,
subject: `You've been invited to ${organizationName}`,
html: `
<h1>You've been invited!</h1>
<p>Click the link below to join ${organizationName}:</p>
<a href="${inviteUrl}">Accept Invitation</a>
`
})
}
})
],
// Custom error handling
onError: (error, req) => {
console.error('Auth error:', error)
// Log to your error tracking service (Sentry, etc.)
},
// Success callbacks
onSuccess: async (user, action) => {
console.log(`User ${user.id} performed action: ${action}`)
// Log auth events for security monitoring
}
})
// Type definitions for TypeScript
export type Session = typeof auth.$Infer.Session
export type User = typeof auth.$Infer.User