Skip to main content
CharleOS uses Better Auth for modern, type-safe authentication. This page explains the configuration and features.

Why Better Auth?

Type-Safe

Full TypeScript support with automatic type inference

Framework Agnostic

Works with any framework, but optimized for Next.js

Database-First

Uses your existing database (PostgreSQL via Drizzle)

Flexible

Easy to customize and extend

Configuration

Team Auth (lib/auth.ts)

Client Portal Auth (lib/client-auth.ts)

API Handler

Better Auth provides a catch-all API handler:
This creates all auth endpoints automatically:
  • /api/auth/sign-in/google
  • /api/auth/sign-out
  • /api/auth/session
  • /api/auth/callback/google

Client-Side Hooks

Team Auth Client (lib/auth-client.ts)

Usage:

Features

Google OAuth Integration

Configuration:
  1. Create OAuth credentials in Google Cloud Console
  2. Set authorized redirect URIs:
    • http://localhost:3000/api/auth/callback/google (local)
    • https://charle.agency/api/auth/callback/google (production)
  3. Add credentials to .env.local
Sign-In Flow:
  1. User clicks “Sign in with Google”
  2. Redirected to Google consent screen
  3. Google redirects back to /api/auth/callback/google
  4. Better Auth creates session
  5. User redirected to dashboard
Avatar Handling:
  • Google profile picture is downloaded
  • Uploaded to Cloudflare R2
  • Stored in user.image field
  • Updated on every sign-in

Invite-Only System

How it works:
  1. Admin creates user account via admin panel (status: “pending”)
  2. Admin sends invitation email (status: “sent”)
  3. User clicks “Sign in with Google”
  4. Better Auth verifies email exists in database
  5. If not found → “No account found” error
  6. If found → Session created, status auto-updated to “active”

Session Management

Server-Side:
Client-Side:

Database Hooks

Better Auth allows you to hook into lifecycle events:
User Status Flow:
  • pending → Account created but no invite sent
  • sent → Invite email sent, awaiting first login
  • active → User has logged in (auto-activated on first login after invite)

Security

CSRF Protection

Better Auth includes built-in CSRF protection:
  • All POST requests require valid CSRF token
  • Tokens are stored in cookies
  • Automatically validated on each request

Session Security

  • Sessions stored server-side in PostgreSQL
  • Random, unpredictable session tokens
  • Automatic expiry after 7 days
  • IP address and user agent tracked

Password Security (Client Portal)

  • Passwords hashed with bcrypt (cost factor 10)
  • Never stored in plain text
  • Password reset requires email verification

Customization

Adding Custom Fields

Custom Session Data

Environment Variables

Never commit secrets to git. Use .env.local for local dev and Vercel environment variables for production.

Troubleshooting

”No account found” Error

Cause: User trying to sign in with Google but email doesn’t exist in database. Fix: Admin must create user account first via admin panel.

CORS Errors

Cause: BETTER_AUTH_URL doesn’t match actual URL. Fix: Verify .env.local has correct URL for environment:
  • Local: http://localhost:3000
  • Production: https://charle.agency

Session Not Persisting

Cause: Cookies not being set (usually HTTPS/domain issues). Fix:
  1. Check BETTER_AUTH_URL matches your actual domain
  2. Ensure HTTPS in production
  3. Clear cookies and try again

Authentication Overview

Auth architecture and systems

Permissions

Role-based access control

Sessions

Session lifecycle management

Better Auth Docs

Official Better Auth documentation