Authentication Configuration
This guide covers the complete setup and customization of the Better Auth authentication system used in SaaS Core, including comprehensive OAuth provider configuration.
๐ Authentication Overview
SaaS Core uses Better Auth as its authentication solution, providing:
- Email and password authentication
- Social login (Google, GitHub)
- Magic link authentication
- Session management
- Multi-tenant organization support
- Role-based access control
๐ Quick Setup
1. Environment Variables
Add these authentication-related variables to your .env.local file:
# ===========================================
# Authentication Configuration
# ===========================================
BETTER_AUTH_SECRET="your-super-secret-key-here-make-it-long-and-random"
BETTER_AUTH_URL="http://localhost:3000"
# ===========================================
# OAuth Configuration
# ===========================================
# Google OAuth
GOOGLE_CLIENT_ID="123456789012-abcdefghijklmnopqrstuvwxyz123456.apps.googleusercontent.com"
GOOGLE_CLIENT_SECRET="GOCSPX-abcdefghijklmnopqrstuvwxyz123456"
# GitHub OAuth
GITHUB_CLIENT_ID="abcdef1234567890abcd"
GITHUB_CLIENT_SECRET="abcdef1234567890abcdef1234567890abcdef12"
# Email Service (Magic Links)
RESEND_API_KEY="your-resend-api-key"
2. Generate Authentication Secret
Generate a secure authentication secret:
# Using OpenSSL
openssl rand -base64 32
# Using Node.js
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
3. Generate Authentication Schema
npx @better-auth/cli generate
Copy the generated auth schema content to your Drizzle schema in src/db/schema.ts.
4. Verify Authentication Setup
Start your development server and test the authentication flow:
pnpm run dev
Visit http://localhost:3000/auth/signin to test the sign-in process.
๐ง Detailed Configuration
Better Auth Setup
The main authentication configuration is in src/lib/auth.ts:
import { stripe } from "@better-auth/stripe";
import { type BetterAuthOptions, betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { nextCookies } from "better-auth/next-js";
import { magicLink, openAPI, organization } from "better-auth/plugins";
import { db } from "@/db/drizzle";
import { schema } from "@/db/schema";
export const auth = betterAuth({
trustedOrigins: ["https://*.zifyai.com", "http://localhost:3000"],
database: drizzleAdapter(db, {
provider: "pg",
schema,
}),
user: {
deleteUser: {
enabled: true,
},
additionalFields: {
stripeCustomerId: {
type: "string",
required: false,
},
},
},
session: {
expiresIn: 60 * 60 * 24 * 7, // 7 days
updateAge: 60 * 60 * 24, // 1 day
cookieCache: {
enabled: true,
maxAge: 5 * 60, // Cache duration in seconds
},
},
emailAndPassword: {
enabled: true,
minPasswordLength: 8,
maxPasswordLength: 20,
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!,
},
},
plugins: [
magicLink({
sendMagicLink: async ({ email, token, url }) => {
// Send magic link email
await sendEmail({
to: email,
subject: "Sign in to SaaS Core",
html: `
<h2>Sign in to SaaS Core</h2>
<p>Click the link below to sign in:</p>
<a href="${url}">Sign In</a>
<p>This link will expire in 15 minutes.</p>
`,
});
},
}),
organization({
enabled: true,
defaultRole: "member",
roles: ["owner", "admin", "member"],
}),
openAPI({
enabled: process.env.NODE_ENV === "development",
}),
],
});
Session Configuration
Customize session settings based on your security requirements:
session: {
expiresIn: 60 * 60 * 24 * 7, // 7 days
updateAge: 60 * 60 * 24, // Update session every 24 hours
cookie: {
name: "saas-core-session",
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
httpOnly: true,
},
cookieCache: {
enabled: true,
maxAge: 5 * 60, // 5 minutes cache
},
}
Email Verification
Configure email verification settings:
emailAndPassword: {
enabled: true,
minPasswordLength: 8,
maxPasswordLength: 20,
requireEmailVerification: true,
sendVerificationEmail: async ({ email, token, url }) => {
await sendEmail({
to: email,
subject: "Verify your email address",
html: `
<h2>Verify your email address</h2>
<p>Click the link below to verify your email:</p>
<a href="${url}">Verify Email</a>
<p>This link will expire in 24 hours.</p>
`,
});
},
}
๐ OAuth Provider Setup
Google OAuth Configuration
1. Create Google Cloud Project
- Visit Google Cloud Console
- Click the project selector, then click "New Project"
- Enter project name (e.g.,
saas-core-pro) - Select organization (optional)
- Click "Create"
2. Enable Required APIs
- In Google Cloud Console, navigate to "APIs & Services" > "Library"
- Search for "Google+ API" or "People API"
- Click "Google+ API", then click "Enable"
- Also enable "People API" (recommended)
3. Configure OAuth Consent Screen
-
Navigate to "APIs & Services" > "OAuth consent screen"
-
Select user type:
- Internal: Only organization users (G Suite)
- External: Any Google user
-
Fill in application information:
Application name: SaaS Core User support email: [email protected] Application logo: Upload your app icon (optional) Application homepage: https://yourdomain.com Application privacy policy link: https://yourdomain.com/privacy Application terms of service link: https://yourdomain.com/terms -
Add authorized domains:
yourdomain.com localhost (development only) -
Configure scopes:
../auth/userinfo.email../auth/userinfo.profileopenid
4. Create OAuth 2.0 Credentials
-
Navigate to "APIs & Services" > "Credentials"
-
Click "Create credentials" > "OAuth 2.0 Client IDs"
-
Select application type: Web application
-
Configure client information:
Name: SaaS Core Web Client Authorized JavaScript origins: - http://localhost:3000 (development) - https://yourdomain.com (production) Authorized redirect URIs: - http://localhost:3000/api/auth/callback/google (development) - https://yourdomain.com/api/auth/callback/google (production) -
Click "Create"
-
Copy the generated Client ID and Client Secret
5. Environment Variables
# Google OAuth Configuration
GOOGLE_CLIENT_ID="123456789012-abcdefghijklmnopqrstuvwxyz123456.apps.googleusercontent.com"
GOOGLE_CLIENT_SECRET="GOCSPX-abcdefghijklmnopqrstuvwxyz123456"
GitHub OAuth Configuration
1. Create GitHub OAuth Application
- Log in to GitHub, visit Developer Settings
- Click "OAuth Apps" tab
- Click "New OAuth App" button
- Fill in application information:
Application name: SaaS Core Homepage URL: https://yourdomain.com Application description: Modern SaaS template for global products Authorization callback URL: https://yourdomain.com/api/auth/callback/github
2. Development Environment Configuration
Create a separate OAuth application for development:
Application name: SaaS Core (Development)
Homepage URL: http://localhost:3000
Authorization callback URL: http://localhost:3000/api/auth/callback/github
3. Get Client Credentials
- After creating the application, you'll see "Client ID"
- Click "Generate a new client secret" to generate client secret
- Important: Copy the client secret immediately, it will only be shown once
4. Environment Variables
# GitHub OAuth Configuration
GITHUB_CLIENT_ID="abcdef1234567890abcd"
GITHUB_CLIENT_SECRET="abcdef1234567890abcdef1234567890abcdef12"
๐ข Multi-Tenant Organization Setup
SaaS Core supports multi-tenant organizations with role-based access:
Organization Schema
// In your Drizzle schema
export const organizations = pgTable("organizations", {
id: text("id").primaryKey(),
name: text("name").notNull(),
slug: text("slug").notNull().unique(),
createdAt: timestamp("created_at").defaultNow().notNull(),
updatedAt: timestamp("updated_at").defaultNow().notNull(),
});
export const organizationMembers = pgTable("organization_members", {
id: text("id").primaryKey(),
userId: text("user_id").notNull(),
organizationId: text("organization_id").notNull(),
role: text("role").notNull().default("member"), // owner, admin, member
joinedAt: timestamp("joined_at").defaultNow().notNull(),
});
Organization Management
// Create organization
const org = await auth.api.createOrganization({
name: "My Organization",
slug: "my-org",
});
// Invite members
await auth.api.inviteToOrganization({
organizationId: org.id,
email: "[email protected]",
role: "member",
});
๐ Security Configuration
Rate Limiting
Implement rate limiting for authentication endpoints:
// Example with Redis rate limiting
import { Redis } from "ioredis";
const redis = new Redis(process.env.REDIS_URL);
export async function rateLimit(key: string, limit: number, window: number) {
const current = await redis.incr(key);
if (current === 1) {
await redis.expire(key, window);
}
return current <= limit;
}
Password Policies
Customize password requirements:
emailAndPassword: {
enabled: true,
minPasswordLength: 8,
maxPasswordLength: 128,
requireEmailVerification: true,
passwordPolicy: {
requireUppercase: true,
requireLowercase: true,
requireNumbers: true,
requireSpecialCharacters: true,
},
}
Security Headers
Add security headers to your Next.js configuration:
// next.config.js
const securityHeaders = [
{
key: 'X-Frame-Options',
value: 'DENY'
},
{
key: 'X-Content-Type-Options',
value: 'nosniff'
},
{
key: 'Referrer-Policy',
value: 'origin-when-cross-origin'
}
];
๐งช Testing Authentication
Test Authentication Flow
Create a test script to verify authentication setup:
# Create test script
cat > scripts/test-auth.js << 'EOF'
const { auth } = require('../src/lib/auth');
async function testAuthentication() {
console.log('๐ Testing authentication setup...');
try {
// Test configuration
console.log('โ
Better Auth configuration loaded');
// Test environment variables
const requiredVars = [
'BETTER_AUTH_SECRET',
'BETTER_AUTH_URL',
'GOOGLE_CLIENT_ID',
'GITHUB_CLIENT_ID'
];
requiredVars.forEach(varName => {
if (process.env[varName]) {
console.log(`โ
${varName}: Configured`);
} else {
console.log(`โ ${varName}: Missing`);
}
});
console.log('โ
Authentication test completed');
} catch (error) {
console.error('โ Authentication test failed:', error.message);
}
}
testAuthentication();
EOF
# Run test
node scripts/test-auth.js
Manual Testing Checklist
- [ ] Sign up with email and password
- [ ] Verify email address
- [ ] Sign in with email and password
- [ ] Sign in with Google OAuth
- [ ] Sign in with GitHub OAuth
- [ ] Use magic link authentication
- [ ] Test password reset
- [ ] Verify session persistence
- [ ] Test sign out
Testing OAuth Login
- Start development server:
pnpm run dev - Visit login page:
http://localhost:3000/auth/signin - Click "Continue with Google" or "Continue with GitHub"
- Complete OAuth authorization flow
- Verify user information is correctly saved
๐ Security Best Practices
1. Client Secret Security
- Never expose client secrets in client-side code
- Use environment variables to store sensitive information
- Regularly rotate client secrets
- Use different OAuth applications for different environments
2. Redirect URI Validation
- Only add necessary redirect URIs
- Use HTTPS (production environment)
- Avoid using wildcard redirect URIs
3. Scope Minimization
Only request application-required permissions:
socialProviders: {
google: {
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
scope: 'openid email profile' // Minimum permissions
}
}
4. Rate Limiting
Implement rate limiting for authentication endpoints:
// Example with Redis rate limiting
import { Redis } from "ioredis";
const redis = new Redis(process.env.REDIS_URL);
export async function rateLimit(key: string, limit: number, window: number) {
const current = await redis.incr(key);
if (current === 1) {
await redis.expire(key, window);
}
return current <= limit;
}
5. Password Policies
Customize password requirements:
emailAndPassword: {
enabled: true,
minPasswordLength: 8,
maxPasswordLength: 128,
requireEmailVerification: true,
passwordPolicy: {
requireUppercase: true,
requireLowercase: true,
requireNumbers: true,
requireSpecialCharacters: true,
},
}
6. Security Headers
Add security headers to your Next.js configuration:
// next.config.js
const securityHeaders = [
{
key: 'X-Frame-Options',
value: 'DENY'
},
{
key: 'X-Content-Type-Options',
value: 'nosniff'
},
{
key: 'Referrer-Policy',
value: 'origin-when-cross-origin'
}
];
๐ Multi-Environment Configuration
Development Environment
BETTER_AUTH_URL="http://localhost:3000"
GOOGLE_CLIENT_ID="dev-google-client-id"
GITHUB_CLIENT_ID="dev-github-client-id"
Staging Environment
BETTER_AUTH_URL="https://staging.yourdomain.com"
GOOGLE_CLIENT_ID="staging-google-client-id"
GITHUB_CLIENT_ID="staging-github-client-id"
Production Environment
BETTER_AUTH_URL="https://yourdomain.com"
GOOGLE_CLIENT_ID="prod-google-client-id"
GITHUB_CLIENT_ID="prod-github-client-id"
๐จ Troubleshooting
Debug Mode
Enable debug logging for authentication issues:
export const auth = betterAuth({
// ... other config
debug: process.env.NODE_ENV === "development",
});
Local Development HTTPS Issues
Problem: Some OAuth providers require HTTPS Solutions:
- Use ngrok or similar tools to create HTTPS tunnels
- Configure local HTTPS development environment
- Use OAuth provider's developer mode when available
Getting Help
If you encounter authentication issues:
- Check the browser console for client-side errors
- Check server logs for backend errors
- Verify all environment variables are set correctly
- Test with different authentication methods
- Consult the Better Auth documentation
๐ Monitoring and Analytics
OAuth Usage Statistics
Track OAuth provider usage in your database:
-- View OAuth provider usage statistics
SELECT
provider,
COUNT(*) as user_count,
COUNT(*) * 100.0 / (SELECT COUNT(*) FROM accounts) as percentage
FROM accounts
GROUP BY provider;
Login Success Rate Monitoring
// Record authentication attempts
await db.insert(loginAttempts).values({
provider: 'google',
success: true,
timestamp: new Date(),
userAgent: req.headers['user-agent']
})
๐ฏ Next Steps
After setting up authentication, consider:
- User Management - Advanced user administration
- Security Dashboard - Monitor authentication activity
- API Integration - Secure your API endpoints
- Multi-factor Authentication - Add additional security layers
Your authentication system is now ready to secure your SaaS Core application!