2 min readLast updated: 2025-12-31 12:12
Authentication
This template uses Better Auth for authentication, providing a secure and flexible authentication system.
Features
- Email/Password - Traditional email and password authentication
- Social Login - OAuth providers (Google, GitHub, etc.)
- Magic Links - Passwordless email authentication
- Two-Factor Authentication - Enhanced security with 2FA
- Session Management - Secure session handling
Configuration
Environment Variables
Add the following to your .env.local:
# Better Auth Configuration
BETTER_AUTH_SECRET="your-secret-key-min-32-chars"
BETTER_AUTH_URL="http://localhost:3000"
# OAuth Providers (optional)
GOOGLE_CLIENT_ID="your-google-client-id"
GOOGLE_CLIENT_SECRET="your-google-client-secret"
GITHUB_CLIENT_ID="your-github-client-id"
GITHUB_CLIENT_SECRET="your-github-client-secret"
Auth Configuration
The authentication configuration is located in src/lib/auth.ts:
import { betterAuth } from 'better-auth'
import { drizzleAdapter } from 'better-auth/adapters/drizzle'
import { db } from './database'
export const auth = betterAuth({
database: drizzleAdapter(db, {
provider: 'pg', // or 'sqlite'
}),
emailAndPassword: {
enabled: true,
requireEmailVerification: true,
},
socialProviders: {
google: {
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
},
github: {
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
},
},
})
Usage
Client-Side Authentication
Use the auth client in your React components:
'use client'
import { useSession, signIn, signOut } from '@/lib/auth-client'
export function AuthButton() {
const { data: session, isPending } = useSession()
if (isPending) {
return <div>Loading...</div>
}
if (session) {
return (
<div>
<p>Welcome, {session.user.name}!</p>
<button onClick={() => signOut()}>Sign Out</button>
</div>
)
}
return (
<button onClick={() => signIn.email({ email, password })}>
Sign In
</button>
)
}
Server-Side Authentication
Check authentication in Server Components:
import { auth } from '@/lib/auth'
import { headers } from 'next/headers'
export default async function ProtectedPage() {
const session = await auth.api.getSession({
headers: await headers(),
})
if (!session) {
redirect('/login')
}
return <div>Protected content for {session.user.name}</div>
}
API Route Protection
Protect your API routes:
import { auth } from '@/lib/auth'
import { headers } from 'next/headers'
import { NextResponse } from 'next/server'
export async function GET() {
const session = await auth.api.getSession({
headers: await headers(),
})
if (!session) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
return NextResponse.json({ data: 'Protected data' })
}
Customization
Custom Login Page
Create a custom login page at src/app/[lang]/(auth)/login/page.tsx:
import { LoginForm } from '@/components/auth/LoginForm'
export default function LoginPage() {
return (
<div className="min-h-screen flex items-center justify-center">
<LoginForm />
</div>
)
}
Email Templates
Customize email templates in src/lib/email/templates/:
verification.tsx- Email verificationpassword-reset.tsx- Password resetmagic-link.tsx- Magic link login
Security Best Practices
- Use HTTPS in production
- Set secure cookies with
httpOnlyandsecureflags - Implement rate limiting on auth endpoints
- Enable 2FA for sensitive accounts
- Regular session rotation for long-lived sessions
Troubleshooting
Common Issues
Session not persisting:
- Check that
BETTER_AUTH_URLmatches your domain - Verify cookie settings in production
OAuth redirect errors:
- Ensure callback URLs are configured in provider settings
- Check that environment variables are set correctly
Email not sending:
- Verify Resend API key is configured
- Check email templates for errors