Claude
Skills
Sign in
Back

auth

Included with Lifetime
$97 forever

Setup better-auth authentication with email/password, social providers, role-based access control, account management, and session handling. Use this skill when the user says "setup auth", "add authentication", "setup better-auth", "add login", or "setup user auth".

Productivity

What this skill does


# Authentication Setup with better-auth

Sets up a complete authentication system using better-auth with email/password, social OAuth providers, role-based access control, account management, session management, and Drizzle ORM integration.

## What Gets Created

### Core Files

1. **`src/lib/auth.tsx`** - Server-side auth configuration (JSX for email templates)
2. **`src/lib/auth-client.ts`** - Client-side auth hooks and methods
3. **`src/lib/auth-guard.ts`** - Protected route helpers for API routes
4. **`src/app/api/auth/[...all]/route.ts`** - Auth API route handler
5. **`drizzle.config.ts`** - Drizzle ORM configuration
6. **`src/db/index.ts`** - Database client
7. **`src/db/schema/auth.ts`** - Drizzle schema for auth tables
8. **`src/proxy.ts`** - Route protection (Next.js 16+)

### UI Components

1. **`src/components/auth/auth-card.tsx`** - Pre-built sign-in/sign-up card
2. **`src/components/nav-bar.tsx`** - Navigation with user menu dropdown
3. **`src/app/(auth)/sign-in/page.tsx`** - Sign-in page
4. **`src/app/(auth)/sign-up/page.tsx`** - Sign-up page
5. **`src/app/(auth)/layout.tsx`** - Auth pages layout

### Account Management

1. **`src/app/account/layout.tsx`** - Account pages layout with sidebar
2. **`src/app/account/page.tsx`** - Profile management (name, email, avatar)
3. **`src/app/account/security/page.tsx`** - Security settings (password, sessions)
4. **`src/app/api/account/delete/route.tsx`** - Account deletion endpoint

## Project Structure Note

This skill uses paths with `src/` prefix (standard Next.js structure with src directory).

**If your project uses `--no-src-dir` (app and lib in root):**

- Replace `src/lib/` with `lib/`
- Replace `src/app/` with `app/`
- Replace `src/components/` with `components/`
- Replace `src/db/` with `db/`
- Replace `src/emails/` with `emails/`
- Move `src/proxy.ts` to root as `proxy.ts`

## Prerequisites

Before using this skill:

1. **Docker must be running** with PostgreSQL container (dependency: `docker` skill)
2. **env-config skill completed** - Requires `env.ts` and `.env.local` with validation
3. **Recommended: email skill** - Provides email templates and sendEmail function

## Installation

```bash
bun add better-auth @daveyplate/better-auth-ui drizzle-orm postgres drizzle-kit -d
bun add -d @tailwindcss/postcss
```

**Note**: `@tailwindcss/postcss` is required for Tailwind CSS v4.

## Environment Variables

### Update `env.ts`

Add these variables to your environment schema:

```typescript
// In env.ts, add to server object:
server: {
  // ... existing variables
  BETTER_AUTH_SECRET: z.string().min(32),
  BETTER_AUTH_URL: z.string().url(),
  EMAIL_FROM: z.string().email().optional(),
  // Optional OAuth
  GOOGLE_CLIENT_ID: z.string().optional(),
  GOOGLE_CLIENT_SECRET: z.string().optional(),
  GITHUB_CLIENT_ID: z.string().optional(),
  GITHUB_CLIENT_SECRET: z.string().optional(),
},

// Add to runtimeEnv:
runtimeEnv: {
  // ... existing variables
  BETTER_AUTH_SECRET: process.env.BETTER_AUTH_SECRET,
  BETTER_AUTH_URL: process.env.BETTER_AUTH_URL,
  EMAIL_FROM: process.env.EMAIL_FROM,
  GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID,
  GOOGLE_CLIENT_SECRET: process.env.GOOGLE_CLIENT_SECRET,
  GITHUB_CLIENT_ID: process.env.GITHUB_CLIENT_ID,
  GITHUB_CLIENT_SECRET: process.env.GITHUB_CLIENT_SECRET,
},
```

### Add to `.env.local`

> **Note:** `EMAIL_FROM` is owned by the `email` skill (applied first in Layer 1). If the `email` skill is already applied, `EMAIL_FROM` is already set — no need to configure it again.

```env
# Auth Core (generate: openssl rand -base64 32)
BETTER_AUTH_SECRET=your-secret-key-here
BETTER_AUTH_URL=http://localhost:3000

# Database
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/store

# Email (for verification/password reset — provided by `email` skill if already applied)
[email protected]
SMTP_HOST=localhost
SMTP_PORT=1025

# Google OAuth (optional)
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=

# GitHub OAuth (optional)
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
```

## Quick Setup

### 1. Create Auth Configuration (`src/lib/auth.tsx`)

**IMPORTANT**: Use `.tsx` extension for JSX support in email templates.

**Email Template Dependencies**: This file requires three email templates:

- `PasswordResetEmail` - For password reset emails
- `VerificationEmail` - For email verification
- `WelcomeEmail` - For new user welcome emails

These should be created using the `email` skill first, or you can create them manually in `src/emails/`.

```tsx
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { db } from "@/db";
import * as schema from "@/db/schema/auth";
import { env } from "@/env";
import { sendEmail } from "@/lib/email";
import { PasswordResetEmail } from "@/emails/password-reset";
import { VerificationEmail } from "@/emails/verification";
import { WelcomeEmail } from "@/emails/welcome";

export const auth = betterAuth({
  database: drizzleAdapter(db, {
    provider: "pg",
    schema,
    debugLogs: true, // Enable for troubleshooting
  }),
  emailAndPassword: {
    enabled: true,
    requireEmailVerification: false, // Set to true in production
    sendResetPassword: async ({ user, url }) => {
      await sendEmail({
        to: user.email,
        subject: "Reset your password",
        template: <PasswordResetEmail url={url} />,
      });
    },
  },
  emailVerification: {
    sendOnSignUp: true,
    autoSignInAfterVerification: true,
    sendVerificationEmail: async ({ user, url }) => {
      await sendEmail({
        to: user.email,
        subject: "Verify your email address",
        template: <VerificationEmail verificationUrl={url} />,
      });
    },
  },
  socialProviders: {
    google: {
      clientId: env.GOOGLE_CLIENT_ID ?? "",
      clientSecret: env.GOOGLE_CLIENT_SECRET ?? "",
      enabled: !!env.GOOGLE_CLIENT_ID,
    },
    github: {
      clientId: env.GITHUB_CLIENT_ID ?? "",
      clientSecret: env.GITHUB_CLIENT_SECRET ?? "",
      enabled: !!env.GITHUB_CLIENT_ID,
    },
  },
  plugins: [
    // Plugins disabled by default - require additional database tables
    // See "Advanced: Plugins" section below
  ],
  user: {
    additionalFields: {
      role: {
        type: "string",
        defaultValue: "member",
        input: false,
      },
    },
  },
  session: {
    expiresIn: 60 * 60 * 24 * 7, // 7 days
    updateAge: 60 * 60 * 24, // 1 day
  },
  account: {
    accountLinking: {
      enabled: true,
      trustedProviders: ["google", "github"],
    },
  },
  databaseHooks: {
    user: {
      create: {
        after: async (user) => {
          await sendEmail({
            to: user.email,
            subject: "Welcome to our platform!",
            template: <WelcomeEmail userName={user.name} loginUrl={env.BETTER_AUTH_URL} />,
          });
        },
      },
    },
  },
  secret: env.BETTER_AUTH_SECRET,
  baseURL: env.BETTER_AUTH_URL,
});

export type Session = typeof auth.$Infer.Session;
export type User = typeof auth.$Infer.Session.user;
```

### 2. Create Auth Client (`src/lib/auth-client.ts`)

```typescript
"use client";

import { createAuthClient } from "better-auth/react";

export const authClient = createAuthClient({
  baseURL: process.env.NEXT_PUBLIC_APP_URL,
  plugins: [],
});

export const {
  signIn,
  signUp,
  signOut,
  useSession,
  getSession,
} = authClient;
```

### 3. Create Auth Guard Helpers (`src/lib/auth-guard.ts`)

```typescript
import { auth } from "@/lib/auth";
import { headers } from "next/headers";
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";

type Session = typeof auth.$Infer.Session;
type User = Session["user"];

type AuthenticatedHandler<T = unknown> = (
  request: NextRequest,
  context: { session: Session; user: User }
) => Promise<NextResponse<T> | Response>;

type RoleGuardOptions = {
  allowedRoles?: string[];
  requireVerifiedEmail?: boolean;
};

/**
 * Wraps an API route handler to
Files: 1
Size: 40.7 KB
Complexity: 40/100
Category: Productivity

Related in Productivity