Back to skills
SkillHub ClubRun DevOpsFull StackBackendSecurity

better-auth

Implement authentication and authorization with Better Auth - a framework-agnostic TypeScript authentication framework. Features include email/password authentication with verification, OAuth providers (Google, GitHub, Discord, etc.), two-factor authentication (TOTP, SMS), passkeys/WebAuthn support, session management, role-based access control (RBAC), rate limiting, and database adapters. Use when adding authentication to applications, implementing OAuth flows, setting up 2FA/MFA, managing user sessions, configuring authorization rules, or building secure authentication systems for web applications.

Packaged view

This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.

Stars
0
Hot score
74
Updated
March 20, 2026
Overall rating
C2.6
Composite score
2.6
Best-practice grade
B75.6

Install command

npx @skill-hub/cli install agentgptsmith-monadframework-better-auth

Repository

agentgptsmith/MonadFramework

Skill path: .claude/skills/better-auth

Implement authentication and authorization with Better Auth - a framework-agnostic TypeScript authentication framework. Features include email/password authentication with verification, OAuth providers (Google, GitHub, Discord, etc.), two-factor authentication (TOTP, SMS), passkeys/WebAuthn support, session management, role-based access control (RBAC), rate limiting, and database adapters. Use when adding authentication to applications, implementing OAuth flows, setting up 2FA/MFA, managing user sessions, configuring authorization rules, or building secure authentication systems for web applications.

Open repository

Best for

Primary workflow: Run DevOps.

Technical facets: Full Stack, Backend, Security, Testing.

Target audience: everyone.

License: MIT.

Original source

Catalog source: SkillHub Club.

Repository owner: agentgptsmith.

This is still a mirrored public skill entry. Review the repository before installing into production workflows.

What it helps with

  • Install better-auth into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
  • Review https://github.com/agentgptsmith/MonadFramework before adding better-auth to shared team environments
  • Use better-auth for development workflows

Works across

Claude CodeCodex CLIGemini CLIOpenCode

Favorites: 0.

Sub-skills: 0.

Aggregator: No.

Original source / Raw SKILL.md

---
name: better-auth
description: Implement authentication and authorization with Better Auth - a framework-agnostic TypeScript authentication framework. Features include email/password authentication with verification, OAuth providers (Google, GitHub, Discord, etc.), two-factor authentication (TOTP, SMS), passkeys/WebAuthn support, session management, role-based access control (RBAC), rate limiting, and database adapters. Use when adding authentication to applications, implementing OAuth flows, setting up 2FA/MFA, managing user sessions, configuring authorization rules, or building secure authentication systems for web applications.
license: MIT
version: 2.0.0
---

# Better Auth Skill

Better Auth is comprehensive, framework-agnostic authentication/authorization framework for TypeScript with built-in email/password, social OAuth, and powerful plugin ecosystem for advanced features.

## When to Use

- Implementing auth in TypeScript/JavaScript applications
- Adding email/password or social OAuth authentication
- Setting up 2FA, passkeys, magic links, advanced auth features
- Building multi-tenant apps with organization support
- Managing sessions and user lifecycle
- Working with any framework (Next.js, Nuxt, SvelteKit, Remix, Astro, Hono, Express, etc.)

## Quick Start

### Installation

```bash
npm install better-auth
# or pnpm/yarn/bun add better-auth
```

### Environment Setup

Create `.env`:
```env
BETTER_AUTH_SECRET=<generated-secret-32-chars-min>
BETTER_AUTH_URL=http://localhost:3000
```

### Basic Server Setup

Create `auth.ts` (root, lib/, utils/, or under src/app/server/):

```ts
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  database: {
    // See references/database-integration.md
  },
  emailAndPassword: {
    enabled: true,
    autoSignIn: true
  },
  socialProviders: {
    github: {
      clientId: process.env.GITHUB_CLIENT_ID!,
      clientSecret: process.env.GITHUB_CLIENT_SECRET!,
    }
  }
});
```

### Database Schema

```bash
npx @better-auth/cli generate  # Generate schema/migrations
npx @better-auth/cli migrate   # Apply migrations (Kysely only)
```

### Mount API Handler

**Next.js App Router:**
```ts
// app/api/auth/[...all]/route.ts
import { auth } from "@/lib/auth";
import { toNextJsHandler } from "better-auth/next-js";

export const { POST, GET } = toNextJsHandler(auth);
```

**Other frameworks:** See references/email-password-auth.md#framework-setup

### Client Setup

Create `auth-client.ts`:

```ts
import { createAuthClient } from "better-auth/client";

export const authClient = createAuthClient({
  baseURL: process.env.NEXT_PUBLIC_BETTER_AUTH_URL || "http://localhost:3000"
});
```

### Basic Usage

```ts
// Sign up
await authClient.signUp.email({
  email: "[email protected]",
  password: "secure123",
  name: "John Doe"
});

// Sign in
await authClient.signIn.email({
  email: "[email protected]",
  password: "secure123"
});

// OAuth
await authClient.signIn.social({ provider: "github" });

// Session
const { data: session } = authClient.useSession(); // React/Vue/Svelte
const { data: session } = await authClient.getSession(); // Vanilla JS
```

## Feature Selection Matrix

| Feature | Plugin Required | Use Case | Reference |
|---------|----------------|----------|-----------|
| Email/Password | No (built-in) | Basic auth | [email-password-auth.md](./references/email-password-auth.md) |
| OAuth (GitHub, Google, etc.) | No (built-in) | Social login | [oauth-providers.md](./references/oauth-providers.md) |
| Email Verification | No (built-in) | Verify email addresses | [email-password-auth.md](./references/email-password-auth.md#email-verification) |
| Password Reset | No (built-in) | Forgot password flow | [email-password-auth.md](./references/email-password-auth.md#password-reset) |
| Two-Factor Auth (2FA/TOTP) | Yes (`twoFactor`) | Enhanced security | [advanced-features.md](./references/advanced-features.md#two-factor-authentication) |
| Passkeys/WebAuthn | Yes (`passkey`) | Passwordless auth | [advanced-features.md](./references/advanced-features.md#passkeys-webauthn) |
| Magic Link | Yes (`magicLink`) | Email-based login | [advanced-features.md](./references/advanced-features.md#magic-link) |
| Username Auth | Yes (`username`) | Username login | [email-password-auth.md](./references/email-password-auth.md#username-authentication) |
| Organizations/Multi-tenant | Yes (`organization`) | Team/org features | [advanced-features.md](./references/advanced-features.md#organizations) |
| Rate Limiting | No (built-in) | Prevent abuse | [advanced-features.md](./references/advanced-features.md#rate-limiting) |
| Session Management | No (built-in) | User sessions | [advanced-features.md](./references/advanced-features.md#session-management) |

## Auth Method Selection Guide

**Choose Email/Password when:**
- Building standard web app with traditional auth
- Need full control over user credentials
- Targeting users who prefer email-based accounts

**Choose OAuth when:**
- Want quick signup with minimal friction
- Users already have social accounts
- Need access to social profile data

**Choose Passkeys when:**
- Want passwordless experience
- Targeting modern browsers/devices
- Security is top priority

**Choose Magic Link when:**
- Want passwordless without WebAuthn complexity
- Targeting email-first users
- Need temporary access links

**Combine Multiple Methods when:**
- Want flexibility for different user preferences
- Building enterprise apps with various auth requirements
- Need progressive enhancement (start simple, add more options)

## Core Architecture

Better Auth uses client-server architecture:
1. **Server** (`better-auth`): Handles auth logic, database ops, API routes
2. **Client** (`better-auth/client`): Provides hooks/methods for frontend
3. **Plugins**: Extend both server/client functionality

## Implementation Checklist

- [ ] Install `better-auth` package
- [ ] Set environment variables (SECRET, URL)
- [ ] Create auth server instance with database config
- [ ] Run schema migration (`npx @better-auth/cli generate`)
- [ ] Mount API handler in framework
- [ ] Create client instance
- [ ] Implement sign-up/sign-in UI
- [ ] Add session management to components
- [ ] Set up protected routes/middleware
- [ ] Add plugins as needed (regenerate schema after)
- [ ] Test complete auth flow
- [ ] Configure email sending (verification/reset)
- [ ] Enable rate limiting for production
- [ ] Set up error handling

## Reference Documentation

### Core Authentication
- [Email/Password Authentication](./references/email-password-auth.md) - Email/password setup, verification, password reset, username auth
- [OAuth Providers](./references/oauth-providers.md) - Social login setup, provider configuration, token management
- [Database Integration](./references/database-integration.md) - Database adapters, schema setup, migrations

### Advanced Features
- [Advanced Features](./references/advanced-features.md) - 2FA/MFA, passkeys, magic links, organizations, rate limiting, session management

## Scripts

- `scripts/better_auth_init.py` - Initialize Better Auth configuration with interactive setup

## Resources

- Docs: https://www.better-auth.com/docs
- GitHub: https://github.com/better-auth/better-auth
- Plugins: https://www.better-auth.com/docs/plugins
- Examples: https://www.better-auth.com/docs/examples


---

## Referenced Files

> The following files are referenced in this skill and included for context.

### references/email-password-auth.md

```markdown
# Email/Password Authentication

Email/password is built-in auth method in Better Auth. No plugins required for basic functionality.

## Server Configuration

### Basic Setup

```ts
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  emailAndPassword: {
    enabled: true,
    autoSignIn: true, // Auto sign-in after signup (default: true)
    requireEmailVerification: false, // Require email verification before login
    sendResetPasswordToken: async ({ user, url }) => {
      // Send password reset email
      await sendEmail(user.email, url);
    }
  }
});
```

### Custom Password Requirements

```ts
export const auth = betterAuth({
  emailAndPassword: {
    enabled: true,
    password: {
      minLength: 8,
      requireUppercase: true,
      requireLowercase: true,
      requireNumbers: true,
      requireSpecialChars: true
    }
  }
});
```

## Client Usage

### Sign Up

```ts
import { authClient } from "@/lib/auth-client";

const { data, error } = await authClient.signUp.email({
  email: "[email protected]",
  password: "securePassword123",
  name: "John Doe",
  image: "https://example.com/avatar.jpg", // optional
  callbackURL: "/dashboard" // optional
}, {
  onSuccess: (ctx) => {
    // ctx.data contains user and session
    console.log("User created:", ctx.data.user);
  },
  onError: (ctx) => {
    alert(ctx.error.message);
  }
});
```

### Sign In

```ts
const { data, error } = await authClient.signIn.email({
  email: "[email protected]",
  password: "securePassword123",
  callbackURL: "/dashboard",
  rememberMe: true // default: true
}, {
  onSuccess: () => {
    // redirect or update UI
  },
  onError: (ctx) => {
    console.error(ctx.error.message);
  }
});
```

### Sign Out

```ts
await authClient.signOut({
  fetchOptions: {
    onSuccess: () => {
      router.push("/login");
    }
  }
});
```

## Email Verification

### Server Setup

```ts
export const auth = betterAuth({
  emailVerification: {
    sendVerificationEmail: async ({ user, url, token }) => {
      // Send verification email
      await sendEmail({
        to: user.email,
        subject: "Verify your email",
        html: `Click <a href="${url}">here</a> to verify your email.`
      });
    },
    sendOnSignUp: true, // Send verification email on signup
    autoSignInAfterVerification: true // Auto sign-in after verification
  },
  emailAndPassword: {
    enabled: true,
    requireEmailVerification: true // Require verification before login
  }
});
```

### Client Usage

```ts
// Send verification email
await authClient.sendVerificationEmail({
  email: "[email protected]",
  callbackURL: "/verify-success"
});

// Verify email with token
await authClient.verifyEmail({
  token: "verification-token-from-email"
});
```

## Password Reset Flow

### Server Setup

```ts
export const auth = betterAuth({
  emailAndPassword: {
    enabled: true,
    sendResetPasswordToken: async ({ user, url, token }) => {
      await sendEmail({
        to: user.email,
        subject: "Reset your password",
        html: `Click <a href="${url}">here</a> to reset your password.`
      });
    }
  }
});
```

### Client Flow

```ts
// Step 1: Request password reset
await authClient.forgetPassword({
  email: "[email protected]",
  redirectTo: "/reset-password"
});

// Step 2: Reset password with token
await authClient.resetPassword({
  token: "reset-token-from-email",
  password: "newSecurePassword123"
});
```

### Change Password (Authenticated)

```ts
await authClient.changePassword({
  currentPassword: "oldPassword123",
  newPassword: "newPassword456",
  revokeOtherSessions: true // Optional: logout other sessions
});
```

## Username Authentication

Requires `username` plugin for username-based auth.

### Server Setup

```ts
import { betterAuth } from "better-auth";
import { username } from "better-auth/plugins";

export const auth = betterAuth({
  plugins: [
    username({
      // Allow sign in with username or email
      allowUsernameOrEmail: true
    })
  ]
});
```

### Client Setup

```ts
import { createAuthClient } from "better-auth/client";
import { usernameClient } from "better-auth/client/plugins";

export const authClient = createAuthClient({
  plugins: [usernameClient()]
});
```

### Client Usage

```ts
// Sign up with username
await authClient.signUp.username({
  username: "johndoe",
  password: "securePassword123",
  email: "[email protected]", // optional
  name: "John Doe"
});

// Sign in with username
await authClient.signIn.username({
  username: "johndoe",
  password: "securePassword123"
});

// Sign in with username or email (if allowUsernameOrEmail: true)
await authClient.signIn.username({
  username: "johndoe", // or "[email protected]"
  password: "securePassword123"
});
```

## Framework Setup

### Next.js (App Router)

```ts
// app/api/auth/[...all]/route.ts
import { auth } from "@/lib/auth";
import { toNextJsHandler } from "better-auth/next-js";

export const { POST, GET } = toNextJsHandler(auth);
```

### Next.js (Pages Router)

```ts
// pages/api/auth/[...all].ts
import { auth } from "@/lib/auth";
import { toNextJsHandler } from "better-auth/next-js";

export default toNextJsHandler(auth);
```

### Nuxt

```ts
// server/api/auth/[...all].ts
import { auth } from "~/utils/auth";
import { toWebRequest } from "better-auth/utils/web";

export default defineEventHandler((event) => {
  return auth.handler(toWebRequest(event));
});
```

### SvelteKit

```ts
// hooks.server.ts
import { auth } from "$lib/auth";
import { svelteKitHandler } from "better-auth/svelte-kit";

export async function handle({ event, resolve }) {
  return svelteKitHandler({ event, resolve, auth });
}
```

### Astro

```ts
// pages/api/auth/[...all].ts
import { auth } from "@/lib/auth";

export async function ALL({ request }: { request: Request }) {
  return auth.handler(request);
}
```

### Hono

```ts
import { Hono } from "hono";
import { auth } from "./auth";

const app = new Hono();

app.on(["POST", "GET"], "/api/auth/*", (c) => {
  return auth.handler(c.req.raw);
});
```

### Express

```ts
import express from "express";
import { toNodeHandler } from "better-auth/node";
import { auth } from "./auth";

const app = express();

app.all("/api/auth/*", toNodeHandler(auth));
```

## Protected Routes

### Next.js Middleware

```ts
// middleware.ts
import { auth } from "@/lib/auth";
import { NextRequest, NextResponse } from "next/server";

export async function middleware(request: NextRequest) {
  const session = await auth.api.getSession({
    headers: request.headers
  });

  if (!session) {
    return NextResponse.redirect(new URL("/login", request.url));
  }

  return NextResponse.next();
}

export const config = {
  matcher: ["/dashboard/:path*", "/profile/:path*"]
};
```

### SvelteKit Hooks

```ts
// hooks.server.ts
import { auth } from "$lib/auth";
import { redirect } from "@sveltejs/kit";

export async function handle({ event, resolve }) {
  const session = await auth.api.getSession({
    headers: event.request.headers
  });

  if (event.url.pathname.startsWith("/dashboard") && !session) {
    throw redirect(303, "/login");
  }

  return resolve(event);
}
```

### Nuxt Middleware

```ts
// middleware/auth.ts
export default defineNuxtRouteMiddleware(async (to) => {
  const { data: session } = await useAuthSession();

  if (!session.value && to.path.startsWith("/dashboard")) {
    return navigateTo("/login");
  }
});
```

## User Profile Management

### Get Current User

```ts
const { data: session } = await authClient.getSession();
console.log(session.user);
```

### Update User Profile

```ts
await authClient.updateUser({
  name: "New Name",
  image: "https://example.com/new-avatar.jpg",
  // Custom fields if defined in schema
});
```

### Delete User Account

```ts
await authClient.deleteUser({
  password: "currentPassword", // Required for security
  callbackURL: "/" // Redirect after deletion
});
```

## Best Practices

1. **Password Security**: Enforce strong password requirements
2. **Email Verification**: Enable for production to prevent spam
3. **Rate Limiting**: Prevent brute force attacks (see advanced-features.md)
4. **HTTPS**: Always use HTTPS in production
5. **Error Messages**: Don't reveal if email exists during login
6. **Session Security**: Use secure, httpOnly cookies
7. **CSRF Protection**: Better Auth handles this automatically
8. **Password Reset**: Set short expiration for reset tokens
9. **Account Lockout**: Consider implementing after N failed attempts
10. **Audit Logs**: Track auth events for security monitoring

```

### references/oauth-providers.md

```markdown
# OAuth Providers

Better Auth provides built-in OAuth 2.0 support for social authentication. No plugins required.

## Supported Providers

GitHub, Google, Apple, Discord, Facebook, Microsoft, Twitter/X, Spotify, Twitch, LinkedIn, Dropbox, GitLab, and more.

## Basic OAuth Setup

### Server Configuration

```ts
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  socialProviders: {
    github: {
      clientId: process.env.GITHUB_CLIENT_ID!,
      clientSecret: process.env.GITHUB_CLIENT_SECRET!,
      // Optional: custom scopes
      scope: ["user:email", "read:user"]
    },
    google: {
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
      scope: ["openid", "email", "profile"]
    },
    discord: {
      clientId: process.env.DISCORD_CLIENT_ID!,
      clientSecret: process.env.DISCORD_CLIENT_SECRET!,
    }
  }
});
```

### Client Usage

```ts
import { authClient } from "@/lib/auth-client";

// Basic sign in
await authClient.signIn.social({
  provider: "github",
  callbackURL: "/dashboard"
});

// With callbacks
await authClient.signIn.social({
  provider: "google",
  callbackURL: "/dashboard",
  errorCallbackURL: "/error",
  newUserCallbackURL: "/welcome", // For first-time users
});
```

## Provider Configuration

### GitHub OAuth

1. Create OAuth App at https://github.com/settings/developers
2. Set Authorization callback URL: `http://localhost:3000/api/auth/callback/github`
3. Add credentials to `.env`:

```env
GITHUB_CLIENT_ID=your_client_id
GITHUB_CLIENT_SECRET=your_client_secret
```

### Google OAuth

1. Create project at https://console.cloud.google.com
2. Enable Google+ API
3. Create OAuth 2.0 credentials
4. Add authorized redirect URI: `http://localhost:3000/api/auth/callback/google`
5. Add credentials to `.env`:

```env
GOOGLE_CLIENT_ID=your_client_id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=your_client_secret
```

### Discord OAuth

1. Create application at https://discord.com/developers/applications
2. Add OAuth2 redirect: `http://localhost:3000/api/auth/callback/discord`
3. Add credentials:

```env
DISCORD_CLIENT_ID=your_client_id
DISCORD_CLIENT_SECRET=your_client_secret
```

### Apple Sign In

```ts
export const auth = betterAuth({
  socialProviders: {
    apple: {
      clientId: process.env.APPLE_CLIENT_ID!,
      clientSecret: process.env.APPLE_CLIENT_SECRET!,
      teamId: process.env.APPLE_TEAM_ID!,
      keyId: process.env.APPLE_KEY_ID!,
      privateKey: process.env.APPLE_PRIVATE_KEY!
    }
  }
});
```

### Microsoft/Azure AD

```ts
export const auth = betterAuth({
  socialProviders: {
    microsoft: {
      clientId: process.env.MICROSOFT_CLIENT_ID!,
      clientSecret: process.env.MICROSOFT_CLIENT_SECRET!,
      tenantId: process.env.MICROSOFT_TENANT_ID, // Optional: for specific tenant
    }
  }
});
```

### Twitter/X OAuth

```ts
export const auth = betterAuth({
  socialProviders: {
    twitter: {
      clientId: process.env.TWITTER_CLIENT_ID!,
      clientSecret: process.env.TWITTER_CLIENT_SECRET!,
    }
  }
});
```

## Custom OAuth Provider

Add custom OAuth 2.0 provider:

```ts
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  socialProviders: {
    customProvider: {
      clientId: process.env.CUSTOM_CLIENT_ID!,
      clientSecret: process.env.CUSTOM_CLIENT_SECRET!,
      authorizationUrl: "https://provider.com/oauth/authorize",
      tokenUrl: "https://provider.com/oauth/token",
      userInfoUrl: "https://provider.com/oauth/userinfo",
      scope: ["email", "profile"],
      // Map provider user data to Better Auth user
      mapProfile: (profile) => ({
        id: profile.id,
        email: profile.email,
        name: profile.name,
        image: profile.avatar_url
      })
    }
  }
});
```

## Account Linking

Link multiple OAuth providers to same user account.

### Server Setup

```ts
export const auth = betterAuth({
  account: {
    accountLinking: {
      enabled: true,
      trustedProviders: ["google", "github"] // Auto-link these providers
    }
  }
});
```

### Client Usage

```ts
// Link new provider to existing account
await authClient.linkSocial({
  provider: "google",
  callbackURL: "/profile"
});

// List linked accounts
const { data: session } = await authClient.getSession();
const accounts = session.user.accounts;

// Unlink account
await authClient.unlinkAccount({
  accountId: "account-id"
});
```

## Token Management

### Access OAuth Tokens

```ts
// Server-side
const session = await auth.api.getSession({
  headers: request.headers
});

const accounts = await auth.api.listAccounts({
  userId: session.user.id
});

// Get specific provider token
const githubAccount = accounts.find(a => a.providerId === "github");
const accessToken = githubAccount.accessToken;
const refreshToken = githubAccount.refreshToken;
```

### Refresh Tokens

```ts
// Manually refresh OAuth token
const newToken = await auth.api.refreshToken({
  accountId: "account-id"
});
```

### Use Provider API

```ts
// Example: Use GitHub token to fetch repos
const githubAccount = accounts.find(a => a.providerId === "github");

const response = await fetch("https://api.github.com/user/repos", {
  headers: {
    Authorization: `Bearer ${githubAccount.accessToken}`
  }
});

const repos = await response.json();
```

## Advanced OAuth Configuration

### Custom Scopes

```ts
export const auth = betterAuth({
  socialProviders: {
    github: {
      clientId: process.env.GITHUB_CLIENT_ID!,
      clientSecret: process.env.GITHUB_CLIENT_SECRET!,
      scope: [
        "user:email",
        "read:user",
        "repo", // Access repositories
        "gist" // Access gists
      ]
    }
  }
});
```

### State Parameter

Better Auth automatically handles OAuth state parameter for CSRF protection.

```ts
// Custom state validation
export const auth = betterAuth({
  advanced: {
    generateState: async () => {
      // Custom state generation
      return crypto.randomUUID();
    },
    validateState: async (state: string) => {
      // Custom state validation
      return true;
    }
  }
});
```

### PKCE Support

Better Auth automatically uses PKCE (Proof Key for Code Exchange) for supported providers.

```ts
export const auth = betterAuth({
  socialProviders: {
    customProvider: {
      pkce: true, // Enable PKCE
      // ... other config
    }
  }
});
```

## Error Handling

### Client-Side

```ts
await authClient.signIn.social({
  provider: "github",
  errorCallbackURL: "/auth/error"
}, {
  onError: (ctx) => {
    console.error("OAuth error:", ctx.error);
    // Handle specific errors
    if (ctx.error.code === "OAUTH_ACCOUNT_ALREADY_LINKED") {
      alert("This account is already linked to another user");
    }
  }
});
```

### Server-Side

```ts
export const auth = betterAuth({
  callbacks: {
    async onOAuthError({ error, provider }) {
      console.error(`OAuth error with ${provider}:`, error);
      // Log to monitoring service
      await logError(error);
    }
  }
});
```

## Callback URLs

### Development

```
http://localhost:3000/api/auth/callback/{provider}
```

### Production

```
https://yourdomain.com/api/auth/callback/{provider}
```

**Important:** Add all callback URLs to OAuth provider settings.

## UI Components

### Sign In Button (React)

```tsx
import { authClient } from "@/lib/auth-client";

export function SocialSignIn() {
  const handleOAuth = async (provider: string) => {
    await authClient.signIn.social({
      provider,
      callbackURL: "/dashboard"
    });
  };

  return (
    <div className="space-y-2">
      <button onClick={() => handleOAuth("github")}>
        Sign in with GitHub
      </button>
      <button onClick={() => handleOAuth("google")}>
        Sign in with Google
      </button>
      <button onClick={() => handleOAuth("discord")}>
        Sign in with Discord
      </button>
    </div>
  );
}
```

## Best Practices

1. **Callback URLs**: Add all environments (dev, staging, prod) to OAuth app
2. **Scopes**: Request minimum scopes needed
3. **Token Storage**: Better Auth stores tokens securely in database
4. **Token Refresh**: Implement automatic token refresh for long-lived sessions
5. **Account Linking**: Enable for better UX when user signs in with different providers
6. **Error Handling**: Provide clear error messages for OAuth failures
7. **Provider Icons**: Use official brand assets for OAuth buttons
8. **Mobile Deep Links**: Configure deep links for mobile OAuth flows
9. **Email Matching**: Consider auto-linking accounts with same email
10. **Privacy**: Inform users what data you access from OAuth providers

## Common Issues

### Redirect URI Mismatch

Ensure callback URL in OAuth app matches exactly:
```
http://localhost:3000/api/auth/callback/github
```

### Missing Scopes

Add required scopes for email access:
```ts
scope: ["user:email"] // GitHub
scope: ["email"] // Google
```

### HTTPS Required

Some providers (Apple, Microsoft) require HTTPS callbacks. Use ngrok for local development:
```bash
ngrok http 3000
```

### CORS Errors

Configure CORS if frontend/backend on different domains:
```ts
export const auth = betterAuth({
  advanced: {
    corsOptions: {
      origin: ["https://yourdomain.com"],
      credentials: true
    }
  }
});
```

```

### references/database-integration.md

```markdown
# Database Integration

Better Auth supports multiple databases and ORMs for flexible data persistence.

## Supported Databases

- SQLite
- PostgreSQL
- MySQL/MariaDB
- MongoDB
- Any database with adapter support

## Direct Database Connection

### SQLite

```ts
import { betterAuth } from "better-auth";
import Database from "better-sqlite3";

export const auth = betterAuth({
  database: new Database("./sqlite.db"),
  // or
  database: new Database(":memory:") // In-memory for testing
});
```

### PostgreSQL

```ts
import { betterAuth } from "better-auth";
import { Pool } from "pg";

const pool = new Pool({
  connectionString: process.env.DATABASE_URL,
  // or explicit config
  host: "localhost",
  port: 5432,
  user: "postgres",
  password: "password",
  database: "myapp"
});

export const auth = betterAuth({
  database: pool
});
```

### MySQL

```ts
import { betterAuth } from "better-auth";
import { createPool } from "mysql2/promise";

const pool = createPool({
  host: "localhost",
  user: "root",
  password: "password",
  database: "myapp",
  waitForConnections: true,
  connectionLimit: 10
});

export const auth = betterAuth({
  database: pool
});
```

## ORM Adapters

### Drizzle ORM

**Install:**
```bash
npm install drizzle-orm better-auth
```

**Setup:**
```ts
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { drizzle } from "drizzle-orm/node-postgres";
import { Pool } from "pg";

const pool = new Pool({
  connectionString: process.env.DATABASE_URL
});

const db = drizzle(pool);

export const auth = betterAuth({
  database: drizzleAdapter(db, {
    provider: "pg", // "pg" | "mysql" | "sqlite"
    schema: {
      // Optional: custom table names
      user: "users",
      session: "sessions",
      account: "accounts",
      verification: "verifications"
    }
  })
});
```

**Generate Schema:**
```bash
npx @better-auth/cli generate --adapter drizzle
```

### Prisma

**Install:**
```bash
npm install @prisma/client better-auth
```

**Setup:**
```ts
import { betterAuth } from "better-auth";
import { prismaAdapter } from "better-auth/adapters/prisma";
import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

export const auth = betterAuth({
  database: prismaAdapter(prisma, {
    provider: "postgresql", // "postgresql" | "mysql" | "sqlite"
  })
});
```

**Generate Schema:**
```bash
npx @better-auth/cli generate --adapter prisma
```

**Apply to Prisma:**
```bash
# Add generated schema to schema.prisma
npx prisma migrate dev --name init
npx prisma generate
```

### Kysely

**Install:**
```bash
npm install kysely better-auth
```

**Setup:**
```ts
import { betterAuth } from "better-auth";
import { kyselyAdapter } from "better-auth/adapters/kysely";
import { Kysely, PostgresDialect } from "kysely";
import { Pool } from "pg";

const db = new Kysely({
  dialect: new PostgresDialect({
    pool: new Pool({
      connectionString: process.env.DATABASE_URL
    })
  })
});

export const auth = betterAuth({
  database: kyselyAdapter(db, {
    provider: "pg"
  })
});
```

**Auto-migrate with Kysely:**
```bash
npx @better-auth/cli migrate --adapter kysely
```

### MongoDB

**Install:**
```bash
npm install mongodb better-auth
```

**Setup:**
```ts
import { betterAuth } from "better-auth";
import { mongodbAdapter } from "better-auth/adapters/mongodb";
import { MongoClient } from "mongodb";

const client = new MongoClient(process.env.MONGODB_URI!);
await client.connect();

export const auth = betterAuth({
  database: mongodbAdapter(client, {
    databaseName: "myapp"
  })
});
```

**Generate Collections:**
```bash
npx @better-auth/cli generate --adapter mongodb
```

## Core Database Schema

Better Auth requires these core tables/collections:

### User Table

```sql
CREATE TABLE user (
  id TEXT PRIMARY KEY,
  email TEXT UNIQUE NOT NULL,
  emailVerified BOOLEAN DEFAULT FALSE,
  name TEXT,
  image TEXT,
  createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```

### Session Table

```sql
CREATE TABLE session (
  id TEXT PRIMARY KEY,
  userId TEXT NOT NULL,
  expiresAt TIMESTAMP NOT NULL,
  ipAddress TEXT,
  userAgent TEXT,
  createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  FOREIGN KEY (userId) REFERENCES user(id) ON DELETE CASCADE
);
```

### Account Table

```sql
CREATE TABLE account (
  id TEXT PRIMARY KEY,
  userId TEXT NOT NULL,
  accountId TEXT NOT NULL,
  providerId TEXT NOT NULL,
  accessToken TEXT,
  refreshToken TEXT,
  expiresAt TIMESTAMP,
  scope TEXT,
  createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  FOREIGN KEY (userId) REFERENCES user(id) ON DELETE CASCADE,
  UNIQUE(providerId, accountId)
);
```

### Verification Table

```sql
CREATE TABLE verification (
  id TEXT PRIMARY KEY,
  identifier TEXT NOT NULL,
  value TEXT NOT NULL,
  expiresAt TIMESTAMP NOT NULL,
  createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```

## Schema Generation

### Using CLI

```bash
# Generate schema files
npx @better-auth/cli generate

# Specify adapter
npx @better-auth/cli generate --adapter drizzle
npx @better-auth/cli generate --adapter prisma

# Specify output
npx @better-auth/cli generate --output ./db/schema.ts
```

### Auto-migrate (Kysely only)

```bash
npx @better-auth/cli migrate
```

For other ORMs, apply generated schema manually.

## Custom Fields

Add custom fields to user table:

```ts
export const auth = betterAuth({
  user: {
    additionalFields: {
      role: {
        type: "string",
        required: false,
        defaultValue: "user"
      },
      phoneNumber: {
        type: "string",
        required: false
      },
      subscriptionTier: {
        type: "string",
        required: false
      }
    }
  }
});
```

After adding fields:
```bash
npx @better-auth/cli generate
```

Update user with custom fields:
```ts
await authClient.updateUser({
  role: "admin",
  phoneNumber: "+1234567890"
});
```

## Plugin Schema Extensions

Plugins add their own tables/fields. Regenerate schema after adding plugins:

```bash
npx @better-auth/cli generate
```

### Two-Factor Plugin Tables

- `twoFactor`: Stores TOTP secrets, backup codes

### Passkey Plugin Tables

- `passkey`: Stores WebAuthn credentials

### Organization Plugin Tables

- `organization`: Organization data
- `member`: Organization members
- `invitation`: Pending invitations

## Migration Strategies

### Development

```bash
# Generate schema
npx @better-auth/cli generate

# Apply migrations (Kysely)
npx @better-auth/cli migrate

# Or manual (Prisma)
npx prisma migrate dev

# Or manual (Drizzle)
npx drizzle-kit push
```

### Production

```bash
# Review generated migration
npx @better-auth/cli generate

# Test in staging
# Apply to production with your ORM's migration tool

# Prisma
npx prisma migrate deploy

# Drizzle
npx drizzle-kit push

# Kysely
npx @better-auth/cli migrate
```

## Connection Pooling

### PostgreSQL

```ts
import { Pool } from "pg";

const pool = new Pool({
  connectionString: process.env.DATABASE_URL,
  max: 20, // Max connections
  idleTimeoutMillis: 30000,
  connectionTimeoutMillis: 2000,
});
```

### MySQL

```ts
import { createPool } from "mysql2/promise";

const pool = createPool({
  connectionString: process.env.DATABASE_URL,
  waitForConnections: true,
  connectionLimit: 10,
  queueLimit: 0
});
```

## Database URLs

### PostgreSQL

```env
DATABASE_URL=postgresql://user:password@localhost:5432/dbname
# Or with connection params
DATABASE_URL=postgresql://user:password@localhost:5432/dbname?schema=public&connection_limit=10
```

### MySQL

```env
DATABASE_URL=mysql://user:password@localhost:3306/dbname
```

### SQLite

```env
DATABASE_URL=file:./dev.db
# Or in-memory
DATABASE_URL=:memory:
```

### MongoDB

```env
MONGODB_URI=mongodb://localhost:27017/dbname
# Or Atlas
MONGODB_URI=mongodb+srv://user:[email protected]/dbname
```

## Performance Optimization

### Indexes

Better Auth CLI auto-generates essential indexes:
- `user.email` (unique)
- `session.userId`
- `account.userId`
- `account.providerId, accountId` (unique)

Add custom indexes for performance:
```sql
CREATE INDEX idx_session_expires ON session(expiresAt);
CREATE INDEX idx_user_created ON user(createdAt);
```

### Query Optimization

```ts
// Use connection pooling
// Enable query caching where applicable
// Monitor slow queries

export const auth = betterAuth({
  advanced: {
    defaultCookieAttributes: {
      sameSite: "lax",
      secure: true,
      httpOnly: true
    }
  }
});
```

## Backup Strategies

### PostgreSQL

```bash
# Backup
pg_dump dbname > backup.sql

# Restore
psql dbname < backup.sql
```

### MySQL

```bash
# Backup
mysqldump -u root -p dbname > backup.sql

# Restore
mysql -u root -p dbname < backup.sql
```

### SQLite

```bash
# Copy file
cp dev.db dev.db.backup

# Or use backup command
sqlite3 dev.db ".backup backup.db"
```

### MongoDB

```bash
# Backup
mongodump --db=dbname --out=./backup

# Restore
mongorestore --db=dbname ./backup/dbname
```

## Best Practices

1. **Environment Variables**: Store credentials in env vars, never commit
2. **Connection Pooling**: Use pools for PostgreSQL/MySQL in production
3. **Migrations**: Use ORM migration tools, not raw SQL in production
4. **Indexes**: Add indexes for frequently queried fields
5. **Backups**: Automate daily backups in production
6. **SSL**: Use SSL/TLS for database connections in production
7. **Schema Sync**: Keep schema in sync across environments
8. **Testing**: Use separate database for tests (in-memory SQLite ideal)
9. **Monitoring**: Monitor query performance and connection pool usage
10. **Cleanup**: Periodically clean expired sessions/verifications

## Troubleshooting

### Connection Errors

```ts
// Add connection timeout
const pool = new Pool({
  connectionString: process.env.DATABASE_URL,
  connectionTimeoutMillis: 5000
});
```

### Schema Mismatch

```bash
# Regenerate schema
npx @better-auth/cli generate

# Apply migrations
# For Prisma: npx prisma migrate dev
# For Drizzle: npx drizzle-kit push
```

### Migration Failures

- Check database credentials
- Verify database server is running
- Check for schema conflicts
- Review migration SQL manually

### Performance Issues

- Add indexes on foreign keys
- Enable connection pooling
- Monitor slow queries
- Consider read replicas for heavy read workloads

```

### references/advanced-features.md

```markdown
# Advanced Features

Better Auth plugins extend functionality beyond basic authentication.

## Two-Factor Authentication

### Server Setup

```ts
import { betterAuth } from "better-auth";
import { twoFactor } from "better-auth/plugins";

export const auth = betterAuth({
  plugins: [
    twoFactor({
      issuer: "YourAppName", // TOTP issuer name
      otpOptions: {
        period: 30, // OTP validity period (seconds)
        digits: 6, // OTP length
      }
    })
  ]
});
```

### Client Setup

```ts
import { createAuthClient } from "better-auth/client";
import { twoFactorClient } from "better-auth/client/plugins";

export const authClient = createAuthClient({
  plugins: [
    twoFactorClient({
      twoFactorPage: "/two-factor", // Redirect to 2FA verification page
      redirect: true // Auto-redirect if 2FA required
    })
  ]
});
```

### Enable 2FA for User

```ts
// Enable TOTP
const { data } = await authClient.twoFactor.enable({
  password: "userPassword" // Verify user identity
});

// data contains QR code URI for authenticator app
const qrCodeUri = data.totpURI;
const backupCodes = data.backupCodes; // Save these securely
```

### Verify TOTP Code

```ts
await authClient.twoFactor.verifyTOTP({
  code: "123456",
  trustDevice: true // Skip 2FA on this device for 30 days
});
```

### Disable 2FA

```ts
await authClient.twoFactor.disable({
  password: "userPassword"
});
```

### Backup Codes

```ts
// Generate new backup codes
const { data } = await authClient.twoFactor.generateBackupCodes({
  password: "userPassword"
});

// Use backup code instead of TOTP
await authClient.twoFactor.verifyBackupCode({
  code: "backup-code-123"
});
```

## Passkeys (WebAuthn)

### Server Setup

```ts
import { betterAuth } from "better-auth";
import { passkey } from "better-auth/plugins";

export const auth = betterAuth({
  plugins: [
    passkey({
      rpName: "YourApp", // Relying Party name
      rpID: "yourdomain.com" // Your domain
    })
  ]
});
```

### Client Setup

```ts
import { createAuthClient } from "better-auth/client";
import { passkeyClient } from "better-auth/client/plugins";

export const authClient = createAuthClient({
  plugins: [passkeyClient()]
});
```

### Register Passkey

```ts
// User must be authenticated first
await authClient.passkey.register({
  name: "My Laptop" // Optional: name for this passkey
});
```

### Sign In with Passkey

```ts
await authClient.passkey.signIn();
```

### List User Passkeys

```ts
const { data } = await authClient.passkey.list();
// data contains array of registered passkeys
```

### Delete Passkey

```ts
await authClient.passkey.delete({
  id: "passkey-id"
});
```

## Magic Link

### Server Setup

```ts
import { betterAuth } from "better-auth";
import { magicLink } from "better-auth/plugins";

export const auth = betterAuth({
  plugins: [
    magicLink({
      sendMagicLink: async ({ email, url, token }) => {
        await sendEmail({
          to: email,
          subject: "Sign in to YourApp",
          html: `Click <a href="${url}">here</a> to sign in.`
        });
      },
      expiresIn: 300, // Link expires in 5 minutes (seconds)
    })
  ]
});
```

### Client Setup

```ts
import { createAuthClient } from "better-auth/client";
import { magicLinkClient } from "better-auth/client/plugins";

export const authClient = createAuthClient({
  plugins: [magicLinkClient()]
});
```

### Send Magic Link

```ts
await authClient.magicLink.sendMagicLink({
  email: "[email protected]",
  callbackURL: "/dashboard"
});
```

### Verify Magic Link

```ts
// Called automatically when user clicks link
// Token in URL query params handled by Better Auth
await authClient.magicLink.verify({
  token: "token-from-url"
});
```

## Organizations (Multi-Tenancy)

### Server Setup

```ts
import { betterAuth } from "better-auth";
import { organization } from "better-auth/plugins";

export const auth = betterAuth({
  plugins: [
    organization({
      allowUserToCreateOrganization: true,
      organizationLimit: 5, // Max orgs per user
      creatorRole: "owner" // Role for org creator
    })
  ]
});
```

### Client Setup

```ts
import { createAuthClient } from "better-auth/client";
import { organizationClient } from "better-auth/client/plugins";

export const authClient = createAuthClient({
  plugins: [organizationClient()]
});
```

### Create Organization

```ts
await authClient.organization.create({
  name: "Acme Corp",
  slug: "acme", // Unique slug
  metadata: {
    industry: "Technology"
  }
});
```

### Invite Members

```ts
await authClient.organization.inviteMember({
  organizationId: "org-id",
  email: "[email protected]",
  role: "member", // owner, admin, member
  message: "Join our team!" // Optional
});
```

### Accept Invitation

```ts
await authClient.organization.acceptInvitation({
  invitationId: "invitation-id"
});
```

### List Organizations

```ts
const { data } = await authClient.organization.list();
// Returns user's organizations
```

### Update Member Role

```ts
await authClient.organization.updateMemberRole({
  organizationId: "org-id",
  userId: "user-id",
  role: "admin"
});
```

### Remove Member

```ts
await authClient.organization.removeMember({
  organizationId: "org-id",
  userId: "user-id"
});
```

### Delete Organization

```ts
await authClient.organization.delete({
  organizationId: "org-id"
});
```

## Session Management

### Configure Session Expiration

```ts
export const auth = betterAuth({
  session: {
    expiresIn: 60 * 60 * 24 * 7, // 7 days (seconds)
    updateAge: 60 * 60 * 24, // Update session every 24 hours
    cookieCache: {
      enabled: true,
      maxAge: 5 * 60 // Cache for 5 minutes
    }
  }
});
```

### Server-Side Session

```ts
// Next.js
import { auth } from "@/lib/auth";
import { headers } from "next/headers";

const session = await auth.api.getSession({
  headers: await headers()
});

if (!session) {
  // Not authenticated
}
```

### Client-Side Session

```tsx
// React
import { authClient } from "@/lib/auth-client";

function UserProfile() {
  const { data: session, isPending, error } = authClient.useSession();

  if (isPending) return <div>Loading...</div>;
  if (error) return <div>Error</div>;
  if (!session) return <div>Not logged in</div>;

  return <div>Hello, {session.user.name}!</div>;
}
```

### List Active Sessions

```ts
const { data: sessions } = await authClient.listSessions();
// Returns all active sessions for current user
```

### Revoke Session

```ts
await authClient.revokeSession({
  sessionId: "session-id"
});
```

### Revoke All Sessions

```ts
await authClient.revokeAllSessions();
```

## Rate Limiting

### Server Configuration

```ts
export const auth = betterAuth({
  rateLimit: {
    enabled: true,
    window: 60, // Time window in seconds
    max: 10, // Max requests per window
    storage: "memory", // "memory" or "database"
    customRules: {
      "/api/auth/sign-in": {
        window: 60,
        max: 5 // Stricter limit for sign-in
      },
      "/api/auth/sign-up": {
        window: 3600,
        max: 3 // 3 signups per hour
      }
    }
  }
});
```

### Custom Rate Limiter

```ts
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  rateLimit: {
    enabled: true,
    customLimiter: async ({ request, limit }) => {
      // Custom rate limiting logic
      const ip = request.headers.get("x-forwarded-for");
      const key = `ratelimit:${ip}`;

      // Use Redis, etc.
      const count = await redis.incr(key);
      if (count === 1) {
        await redis.expire(key, limit.window);
      }

      if (count > limit.max) {
        throw new Error("Rate limit exceeded");
      }
    }
  }
});
```

## Anonymous Sessions

Track users before they sign up.

### Server Setup

```ts
import { betterAuth } from "better-auth";
import { anonymous } from "better-auth/plugins";

export const auth = betterAuth({
  plugins: [anonymous()]
});
```

### Client Usage

```ts
// Create anonymous session
const { data } = await authClient.signIn.anonymous();

// Convert to full account
await authClient.signUp.email({
  email: "[email protected]",
  password: "password123",
  linkAnonymousSession: true // Link anonymous data
});
```

## Email OTP

One-time password via email (passwordless).

### Server Setup

```ts
import { betterAuth } from "better-auth";
import { emailOTP } from "better-auth/plugins";

export const auth = betterAuth({
  plugins: [
    emailOTP({
      sendVerificationOTP: async ({ email, otp }) => {
        await sendEmail({
          to: email,
          subject: "Your verification code",
          text: `Your code is: ${otp}`
        });
      },
      expiresIn: 300, // 5 minutes
      length: 6 // OTP length
    })
  ]
});
```

### Client Usage

```ts
// Send OTP to email
await authClient.emailOTP.sendOTP({
  email: "[email protected]"
});

// Verify OTP
await authClient.emailOTP.verifyOTP({
  email: "[email protected]",
  otp: "123456"
});
```

## Phone Number Authentication

Requires phone number plugin.

### Server Setup

```ts
import { betterAuth } from "better-auth";
import { phoneNumber } from "better-auth/plugins";

export const auth = betterAuth({
  plugins: [
    phoneNumber({
      sendOTP: async ({ phoneNumber, otp }) => {
        // Use Twilio, AWS SNS, etc.
        await sendSMS(phoneNumber, `Your code: ${otp}`);
      }
    })
  ]
});
```

### Client Usage

```ts
// Sign up with phone
await authClient.signUp.phoneNumber({
  phoneNumber: "+1234567890",
  password: "password123"
});

// Send OTP
await authClient.phoneNumber.sendOTP({
  phoneNumber: "+1234567890"
});

// Verify OTP
await authClient.phoneNumber.verifyOTP({
  phoneNumber: "+1234567890",
  otp: "123456"
});
```

## Best Practices

1. **2FA**: Offer 2FA as optional, make mandatory for admin users
2. **Passkeys**: Implement as progressive enhancement (fallback to password)
3. **Magic Links**: Set short expiration (5-15 minutes)
4. **Organizations**: Implement RBAC for org permissions
5. **Sessions**: Use short expiration for sensitive apps
6. **Rate Limiting**: Enable in production, adjust limits based on usage
7. **Anonymous Sessions**: Clean up old anonymous sessions periodically
8. **Backup Codes**: Force users to save backup codes before enabling 2FA
9. **Multi-Device**: Allow users to manage trusted devices
10. **Audit Logs**: Track sensitive operations (role changes, 2FA changes)

## Regenerate Schema After Plugins

After adding any plugin:

```bash
npx @better-auth/cli generate
npx @better-auth/cli migrate # if using Kysely
```

Or manually apply migrations for your ORM (Drizzle, Prisma).

```

### scripts/better_auth_init.py

```python
#!/usr/bin/env python3
"""
Better Auth Initialization Script

Interactive script to initialize Better Auth configuration.
Supports multiple databases, ORMs, and authentication methods.

.env loading order: process.env > skill/.env > skills/.env > .claude/.env
"""

import os
import sys
import json
import secrets
from pathlib import Path
from typing import Optional, Dict, Any, List
from dataclasses import dataclass


@dataclass
class EnvConfig:
    """Environment configuration holder."""
    secret: str
    url: str
    database_url: Optional[str] = None
    github_client_id: Optional[str] = None
    github_client_secret: Optional[str] = None
    google_client_id: Optional[str] = None
    google_client_secret: Optional[str] = None


class BetterAuthInit:
    """Better Auth configuration initializer."""

    def __init__(self, project_root: Optional[Path] = None):
        """
        Initialize the Better Auth configuration tool.

        Args:
            project_root: Project root directory. Auto-detected if not provided.
        """
        self.project_root = project_root or self._find_project_root()
        self.env_config: Optional[EnvConfig] = None

    @staticmethod
    def _find_project_root() -> Path:
        """
        Find project root by looking for package.json.

        Returns:
            Path to project root.

        Raises:
            RuntimeError: If project root cannot be found.
        """
        current = Path.cwd()
        while current != current.parent:
            if (current / "package.json").exists():
                return current
            current = current.parent

        raise RuntimeError("Could not find project root (no package.json found)")

    def _load_env_files(self) -> Dict[str, str]:
        """
        Load environment variables from .env files in order.

        Loading order: process.env > skill/.env > skills/.env > .claude/.env

        Returns:
            Dictionary of environment variables.
        """
        env_vars = {}

        # Define search paths in reverse priority order
        skill_dir = Path(__file__).parent.parent
        env_paths = [
            self.project_root / ".claude" / ".env",
            self.project_root / ".claude" / "skills" / ".env",
            skill_dir / ".env",
        ]

        # Load from files (lowest priority first)
        for env_path in env_paths:
            if env_path.exists():
                env_vars.update(self._parse_env_file(env_path))

        # Override with process environment (highest priority)
        env_vars.update(os.environ)

        return env_vars

    @staticmethod
    def _parse_env_file(path: Path) -> Dict[str, str]:
        """
        Parse .env file into dictionary.

        Args:
            path: Path to .env file.

        Returns:
            Dictionary of key-value pairs.
        """
        env_vars = {}
        try:
            with open(path, "r") as f:
                for line in f:
                    line = line.strip()
                    if line and not line.startswith("#") and "=" in line:
                        key, value = line.split("=", 1)
                        # Remove quotes if present
                        value = value.strip().strip('"').strip("'")
                        env_vars[key.strip()] = value
        except Exception as e:
            print(f"Warning: Could not parse {path}: {e}")

        return env_vars

    @staticmethod
    def generate_secret(length: int = 32) -> str:
        """
        Generate cryptographically secure random secret.

        Args:
            length: Length of secret in bytes.

        Returns:
            Hex-encoded secret string.
        """
        return secrets.token_hex(length)

    def prompt_database(self) -> Dict[str, Any]:
        """
        Prompt user for database configuration.

        Returns:
            Database configuration dictionary.
        """
        print("\nDatabase Configuration")
        print("=" * 50)
        print("1. Direct Connection (PostgreSQL/MySQL/SQLite)")
        print("2. Drizzle ORM")
        print("3. Prisma")
        print("4. Kysely")
        print("5. MongoDB")

        choice = input("\nSelect database option (1-5): ").strip()

        db_configs = {
            "1": self._prompt_direct_db,
            "2": self._prompt_drizzle,
            "3": self._prompt_prisma,
            "4": self._prompt_kysely,
            "5": self._prompt_mongodb,
        }

        handler = db_configs.get(choice)
        if not handler:
            print("Invalid choice. Defaulting to direct PostgreSQL.")
            return self._prompt_direct_db()

        return handler()

    def _prompt_direct_db(self) -> Dict[str, Any]:
        """Prompt for direct database connection."""
        print("\nDatabase Type:")
        print("1. PostgreSQL")
        print("2. MySQL")
        print("3. SQLite")

        db_type = input("Select (1-3): ").strip()

        if db_type == "3":
            db_path = input("SQLite file path [./dev.db]: ").strip() or "./dev.db"
            return {
                "type": "sqlite",
                "import": "import Database from 'better-sqlite3';",
                "config": f'database: new Database("{db_path}")'
            }
        elif db_type == "2":
            db_url = input("MySQL connection string: ").strip()
            return {
                "type": "mysql",
                "import": "import { createPool } from 'mysql2/promise';",
                "config": f"database: createPool({{ connectionString: process.env.DATABASE_URL }})",
                "env_var": ("DATABASE_URL", db_url)
            }
        else:
            db_url = input("PostgreSQL connection string: ").strip()
            return {
                "type": "postgresql",
                "import": "import { Pool } from 'pg';",
                "config": "database: new Pool({ connectionString: process.env.DATABASE_URL })",
                "env_var": ("DATABASE_URL", db_url)
            }

    def _prompt_drizzle(self) -> Dict[str, Any]:
        """Prompt for Drizzle ORM configuration."""
        print("\nDrizzle Provider:")
        print("1. PostgreSQL")
        print("2. MySQL")
        print("3. SQLite")

        provider = input("Select (1-3): ").strip()
        provider_map = {"1": "pg", "2": "mysql", "3": "sqlite"}
        provider_name = provider_map.get(provider, "pg")

        return {
            "type": "drizzle",
            "provider": provider_name,
            "import": "import { drizzleAdapter } from 'better-auth/adapters/drizzle';\nimport { db } from '@/db';",
            "config": f"database: drizzleAdapter(db, {{ provider: '{provider_name}' }})"
        }

    def _prompt_prisma(self) -> Dict[str, Any]:
        """Prompt for Prisma configuration."""
        print("\nPrisma Provider:")
        print("1. PostgreSQL")
        print("2. MySQL")
        print("3. SQLite")

        provider = input("Select (1-3): ").strip()
        provider_map = {"1": "postgresql", "2": "mysql", "3": "sqlite"}
        provider_name = provider_map.get(provider, "postgresql")

        return {
            "type": "prisma",
            "provider": provider_name,
            "import": "import { prismaAdapter } from 'better-auth/adapters/prisma';\nimport { PrismaClient } from '@prisma/client';\n\nconst prisma = new PrismaClient();",
            "config": f"database: prismaAdapter(prisma, {{ provider: '{provider_name}' }})"
        }

    def _prompt_kysely(self) -> Dict[str, Any]:
        """Prompt for Kysely configuration."""
        return {
            "type": "kysely",
            "import": "import { kyselyAdapter } from 'better-auth/adapters/kysely';\nimport { db } from '@/db';",
            "config": "database: kyselyAdapter(db, { provider: 'pg' })"
        }

    def _prompt_mongodb(self) -> Dict[str, Any]:
        """Prompt for MongoDB configuration."""
        mongo_uri = input("MongoDB connection string: ").strip()
        db_name = input("Database name: ").strip()

        return {
            "type": "mongodb",
            "import": "import { mongodbAdapter } from 'better-auth/adapters/mongodb';\nimport { client } from '@/db';",
            "config": f"database: mongodbAdapter(client, {{ databaseName: '{db_name}' }})",
            "env_var": ("MONGODB_URI", mongo_uri)
        }

    def prompt_auth_methods(self) -> List[str]:
        """
        Prompt user for authentication methods.

        Returns:
            List of selected auth method codes.
        """
        print("\nAuthentication Methods")
        print("=" * 50)
        print("Select authentication methods (space-separated, e.g., '1 2 3'):")
        print("1. Email/Password")
        print("2. GitHub OAuth")
        print("3. Google OAuth")
        print("4. Discord OAuth")
        print("5. Two-Factor Authentication (2FA)")
        print("6. Passkeys (WebAuthn)")
        print("7. Magic Link")
        print("8. Username")

        choices = input("\nYour selection: ").strip().split()
        return [c for c in choices if c in "12345678"]

    def generate_auth_config(
        self,
        db_config: Dict[str, Any],
        auth_methods: List[str],
    ) -> str:
        """
        Generate auth.ts configuration file content.

        Args:
            db_config: Database configuration.
            auth_methods: Selected authentication methods.

        Returns:
            Generated TypeScript configuration code.
        """
        imports = ["import { betterAuth } from 'better-auth';"]
        plugins = []
        plugin_imports = []
        config_parts = []

        # Database import
        if db_config.get("import"):
            imports.append(db_config["import"])

        # Email/Password
        if "1" in auth_methods:
            config_parts.append("""  emailAndPassword: {
    enabled: true,
    autoSignIn: true
  }""")

        # OAuth providers
        social_providers = []
        if "2" in auth_methods:
            social_providers.append("""    github: {
      clientId: process.env.GITHUB_CLIENT_ID!,
      clientSecret: process.env.GITHUB_CLIENT_SECRET!,
    }""")

        if "3" in auth_methods:
            social_providers.append("""    google: {
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    }""")

        if "4" in auth_methods:
            social_providers.append("""    discord: {
      clientId: process.env.DISCORD_CLIENT_ID!,
      clientSecret: process.env.DISCORD_CLIENT_SECRET!,
    }""")

        if social_providers:
            config_parts.append(f"  socialProviders: {{\n{',\\n'.join(social_providers)}\n  }}")

        # Plugins
        if "5" in auth_methods:
            plugin_imports.append("import { twoFactor } from 'better-auth/plugins';")
            plugins.append("twoFactor()")

        if "6" in auth_methods:
            plugin_imports.append("import { passkey } from 'better-auth/plugins';")
            plugins.append("passkey()")

        if "7" in auth_methods:
            plugin_imports.append("import { magicLink } from 'better-auth/plugins';")
            plugins.append("""magicLink({
      sendMagicLink: async ({ email, url }) => {
        // TODO: Implement email sending
        console.log(`Magic link for ${email}: ${url}`);
      }
    })""")

        if "8" in auth_methods:
            plugin_imports.append("import { username } from 'better-auth/plugins';")
            plugins.append("username()")

        # Combine all imports
        all_imports = imports + plugin_imports

        # Build config
        config_body = ",\n".join(config_parts)

        if plugins:
            plugins_str = ",\n    ".join(plugins)
            config_body += f",\n  plugins: [\n    {plugins_str}\n  ]"

        # Final output
        return f"""{chr(10).join(all_imports)}

export const auth = betterAuth({{
  {db_config["config"]},
{config_body}
}});
"""

    def generate_env_file(
        self,
        db_config: Dict[str, Any],
        auth_methods: List[str]
    ) -> str:
        """
        Generate .env file content.

        Args:
            db_config: Database configuration.
            auth_methods: Selected authentication methods.

        Returns:
            Generated .env file content.
        """
        env_vars = [
            f"BETTER_AUTH_SECRET={self.generate_secret()}",
            "BETTER_AUTH_URL=http://localhost:3000",
        ]

        # Database URL
        if db_config.get("env_var"):
            key, value = db_config["env_var"]
            env_vars.append(f"{key}={value}")

        # OAuth credentials
        if "2" in auth_methods:
            env_vars.extend([
                "GITHUB_CLIENT_ID=your_github_client_id",
                "GITHUB_CLIENT_SECRET=your_github_client_secret",
            ])

        if "3" in auth_methods:
            env_vars.extend([
                "GOOGLE_CLIENT_ID=your_google_client_id",
                "GOOGLE_CLIENT_SECRET=your_google_client_secret",
            ])

        if "4" in auth_methods:
            env_vars.extend([
                "DISCORD_CLIENT_ID=your_discord_client_id",
                "DISCORD_CLIENT_SECRET=your_discord_client_secret",
            ])

        return "\n".join(env_vars) + "\n"

    def run(self) -> None:
        """Run interactive initialization."""
        print("=" * 50)
        print("Better Auth Configuration Generator")
        print("=" * 50)

        # Load existing env
        env_vars = self._load_env_files()

        # Prompt for configuration
        db_config = self.prompt_database()
        auth_methods = self.prompt_auth_methods()

        # Generate files
        auth_config = self.generate_auth_config(db_config, auth_methods)
        env_content = self.generate_env_file(db_config, auth_methods)

        # Display output
        print("\n" + "=" * 50)
        print("Generated Configuration")
        print("=" * 50)

        print("\n--- auth.ts ---")
        print(auth_config)

        print("\n--- .env ---")
        print(env_content)

        # Offer to save
        save = input("\nSave configuration files? (y/N): ").strip().lower()
        if save == "y":
            self._save_files(auth_config, env_content)
        else:
            print("Configuration not saved.")

    def _save_files(self, auth_config: str, env_content: str) -> None:
        """
        Save generated configuration files.

        Args:
            auth_config: auth.ts content.
            env_content: .env content.
        """
        # Save auth.ts
        auth_locations = [
            self.project_root / "lib" / "auth.ts",
            self.project_root / "src" / "lib" / "auth.ts",
            self.project_root / "utils" / "auth.ts",
            self.project_root / "auth.ts",
        ]

        print("\nWhere to save auth.ts?")
        for i, loc in enumerate(auth_locations, 1):
            print(f"{i}. {loc}")
        print("5. Custom path")

        choice = input("Select (1-5): ").strip()
        if choice == "5":
            custom_path = input("Enter path: ").strip()
            auth_path = Path(custom_path)
        else:
            idx = int(choice) - 1 if choice.isdigit() else 0
            auth_path = auth_locations[idx]

        auth_path.parent.mkdir(parents=True, exist_ok=True)
        auth_path.write_text(auth_config)
        print(f"Saved: {auth_path}")

        # Save .env
        env_path = self.project_root / ".env"
        if env_path.exists():
            backup = self.project_root / ".env.backup"
            env_path.rename(backup)
            print(f"Backed up existing .env to {backup}")

        env_path.write_text(env_content)
        print(f"Saved: {env_path}")

        print("\nNext steps:")
        print("1. Run: npx @better-auth/cli generate")
        print("2. Apply database migrations")
        print("3. Mount API handler in your framework")
        print("4. Create client instance")


def main() -> int:
    """
    Main entry point.

    Returns:
        Exit code (0 for success, 1 for error).
    """
    try:
        initializer = BetterAuthInit()
        initializer.run()
        return 0
    except KeyboardInterrupt:
        print("\n\nOperation cancelled.")
        return 1
    except Exception as e:
        print(f"\nError: {e}", file=sys.stderr)
        return 1


if __name__ == "__main__":
    sys.exit(main())

```

better-auth | SkillHub