Docs
Authentication

Authentication

Configure Better-Auth with email/password, Google OAuth, magic link, and team management.

What Ships by Default

Authentication is configured in lib/auth.ts using Better-Auth with the Drizzle adapter. The following are pre-configured:

  • Email + password with required email verification
  • Google OAuth (select_account prompt)
  • Magic link — passwordless email login
  • Email OTP — one-time passcode verification
  • Organization plugin — mapped to your teams table
  • Admin plugin — RBAC with three app-level roles

Auto Team Creation

When a user signs up (any method), a team is created automatically:

// lib/auth.ts
databaseHooks: {
  user: {
    create: {
      after: async (user) => {
        const teamId = nanoid();
        await db.insert(schema.teams).values({
          id: teamId,
          name: `${user.name}'s Organization`,
          email: user.email,
          slug: `${user.name.toLowerCase().replace(/\s+/g, "-")}-${Date.now().toString().slice(-4)}`,
        });
        await db.insert(schema.teamMembers).values({
          teamId,
          userId: user.id,
          role: "owner",
        });
      },
    },
  },
},

RBAC Roles

Two levels of roles exist independently:

App role (users.role) — platform-wide access:

RolePermissions
superadmincreate, update, delete, ban, set-role, list users
admincreate, update, ban, set-role, list users
userNo admin permissions

Team role (teamMembers.role) — per-team access:

RoleDescription
ownerCreated automatically on signup
adminCan manage team settings and members
memberStandard team member

Schema Mapping

The organization plugin is remapped to use your custom teams table:

organization({
  schema: {
    organization: schema.teams,
    member: schema.teamMembers,
    invitation: schema.invitations,
  },
  modelName: {
    organization: "team",
  },
})

This means Better-Auth's organization features (invitations, member roles) all operate against your teams and teamMembers tables.

Auth Client

On the client side, import from lib/auth-client.ts:

import { authClient } from "@/lib/auth-client";
 
// Sign in
await authClient.signIn.email({ email, password });
 
// Sign in with Google
await authClient.signIn.social({ provider: "google" });
 
// Magic link
await authClient.signIn.magicLink({ email });

Required Environment Variables

BETTER_AUTH_SECRET=       # openssl rand -base64 32
GOOGLE_CLIENT_ID=         # Google Cloud Console → APIs & Services → Credentials
GOOGLE_CLIENT_SECRET=     # Google Cloud Console → APIs & Services → Credentials

Add http://localhost:3000/api/auth/callback/google to your Google OAuth authorized redirect URIs for local development. See the Google Cloud Console to manage your OAuth client.

For more on the plugins used, see the Better-Auth organization plugin docs and the admin plugin docs.