Claude
Skills
Sign in
Back

better-auth

Included with Lifetime
$97 forever

Build authentication systems for TypeScript/Cloudflare Workers with social auth, 2FA, passkeys, organizations, and RBAC. Self-hosted alternative to Clerk/Auth.js. IMPORTANT: Requires Drizzle ORM or Kysely for D1 - no direct D1 adapter. v1.4.0 (Nov 2025) adds stateless sessions, ESM-only (breaking), JWT key rotation, SCIM provisioning. v1.3 adds SSO/SAML, multi-team support. Use when: self-hosting auth on Cloudflare D1, migrating from Clerk, implementing multi-tenant SaaS, or troubleshooting D1 adapter errors, session serialization, OAuth flows, TanStack Start cookie issues, nanostore session invalidation.

Cloud & DevOpsscriptsassets

What this skill does


# better-auth - D1 Adapter & Error Prevention Guide

**Package**: [email protected] (Nov 22, 2025)
**Breaking Changes**: ESM-only (v1.4.0), Multi-team table changes (v1.3), D1 requires Drizzle/Kysely (no direct adapter)

---

## ⚠️ CRITICAL: D1 Adapter Requirement

better-auth **DOES NOT** have `d1Adapter()`. You **MUST** use:
- **Drizzle ORM** (recommended): `drizzleAdapter(db, { provider: "sqlite" })`
- **Kysely**: `new Kysely({ dialect: new D1Dialect({ database: env.DB }) })`

See Issue #1 below for details.

---

## What's New in v1.4.0 (Nov 22, 2025)

**Major Features:**
- **Stateless session management** - Sessions without database storage
- **ESM-only package** ⚠️ Breaking: CommonJS no longer supported
- **JWT key rotation** - Automatic key rotation for enhanced security
- **SCIM provisioning** - Enterprise user provisioning protocol
- **@standard-schema/spec** - Replaces ZodType for validation
- **CaptchaFox integration** - Built-in CAPTCHA support
- Automatic server-side IP detection
- Cookie-based account data storage
- Multiple passkey origins support
- RP-Initiated Logout endpoint (OIDC)

📚 **Docs**: https://www.better-auth.com/changelogs

---

## What's New in v1.3 (July 2025)

**Major Features:**
- **SSO with SAML 2.0** - Enterprise single sign-on (moved to separate `@better-auth/sso` package)
- **Multi-team support** ⚠️ Breaking: `teamId` removed from member table, new `teamMembers` table required
- **Additional fields** - Custom fields for organization/member/invitation models
- Performance improvements and bug fixes

📚 **Docs**: https://www.better-auth.com/blog/1-3

---

## Alternative: Kysely Adapter Pattern

If you prefer Kysely over Drizzle:

**File**: `src/auth.ts`

```typescript
import { betterAuth } from "better-auth";
import { Kysely, CamelCasePlugin } from "kysely";
import { D1Dialect } from "kysely-d1";

type Env = {
  DB: D1Database;
  BETTER_AUTH_SECRET: string;
  // ... other env vars
};

export function createAuth(env: Env) {
  return betterAuth({
    secret: env.BETTER_AUTH_SECRET,

    // Kysely with D1Dialect
    database: {
      db: new Kysely({
        dialect: new D1Dialect({
          database: env.DB,
        }),
        plugins: [
          // CRITICAL: Required if using Drizzle schema with snake_case
          new CamelCasePlugin(),
        ],
      }),
      type: "sqlite",
    },

    emailAndPassword: {
      enabled: true,
    },

    // ... other config
  });
}
```

**Why CamelCasePlugin?**

If your Drizzle schema uses `snake_case` column names (e.g., `email_verified`), but better-auth expects `camelCase` (e.g., `emailVerified`), the `CamelCasePlugin` automatically converts between the two.

---

## Framework Integrations

### TanStack Start

**⚠️ CRITICAL**: TanStack Start requires the `reactStartCookies` plugin to handle cookie setting properly.

```typescript
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { reactStartCookies } from "better-auth/react-start";

export const auth = betterAuth({
  database: drizzleAdapter(db, { provider: "sqlite" }),
  plugins: [
    twoFactor(),
    organization(),
    reactStartCookies(), // ⚠️ MUST be LAST plugin
  ],
});
```

**Why it's needed**: TanStack Start uses a special cookie handling system. Without this plugin, auth functions like `signInEmail()` and `signUpEmail()` won't set cookies properly, causing authentication to fail.

**Important**: The `reactStartCookies` plugin **must be the last plugin in the array**.

**API Route Setup** (`/src/routes/api/auth/$.ts`):
```typescript
import { auth } from '@/lib/auth'
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/api/auth/$')({
  server: {
    handlers: {
      GET: ({ request }) => auth.handler(request),
      POST: ({ request }) => auth.handler(request),
    },
  },
})
```

📚 **Official Docs**: https://www.better-auth.com/docs/integrations/tanstack

---

## Available Plugins (v1.3+)

Better Auth provides plugins for advanced authentication features:

| Plugin | Import | Description | Docs |
|--------|--------|-------------|------|
| **OIDC Provider** | `better-auth/plugins` | Build your own OpenID Connect provider (become an OAuth provider for other apps) | [📚](https://www.better-auth.com/docs/plugins/oidc-provider) |
| **SSO** | `better-auth/plugins` | Enterprise Single Sign-On with OIDC, OAuth2, and SAML 2.0 support | [📚](https://www.better-auth.com/docs/plugins/sso) |
| **Stripe** | `better-auth/plugins` | Payment and subscription management (stable as of v1.3+) | [📚](https://www.better-auth.com/docs/plugins/stripe) |
| **MCP** | `better-auth/plugins` | Act as OAuth provider for Model Context Protocol (MCP) clients | [📚](https://www.better-auth.com/docs/plugins/mcp) |
| **Expo** | `better-auth/expo` | React Native/Expo integration with secure cookie management | [📚](https://www.better-auth.com/docs/integrations/expo) |

---

## API Reference

### Overview: What You Get For Free

When you call `auth.handler()`, better-auth automatically exposes **80+ production-ready REST endpoints** at `/api/auth/*`. Every endpoint is also available as a **server-side method** via `auth.api.*` for programmatic use.

This dual-layer API system means:
- **Clients** (React, Vue, mobile apps) call HTTP endpoints directly
- **Server-side code** (middleware, background jobs) uses `auth.api.*` methods
- **Zero boilerplate** - no need to write auth endpoints manually

**Time savings**: Building this from scratch = ~220 hours. With better-auth = ~4-8 hours. **97% reduction.**

---

### Auto-Generated HTTP Endpoints

All endpoints are automatically exposed at `/api/auth/*` when using `auth.handler()`.

#### Core Authentication Endpoints

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/sign-up/email` | POST | Register with email/password |
| `/sign-in/email` | POST | Authenticate with email/password |
| `/sign-out` | POST | Logout user |
| `/change-password` | POST | Update password (requires current password) |
| `/forget-password` | POST | Initiate password reset flow |
| `/reset-password` | POST | Complete password reset with token |
| `/send-verification-email` | POST | Send email verification link |
| `/verify-email` | GET | Verify email with token (`?token=<token>`) |
| `/get-session` | GET | Retrieve current session |
| `/list-sessions` | GET | Get all active user sessions |
| `/revoke-session` | POST | End specific session |
| `/revoke-other-sessions` | POST | End all sessions except current |
| `/revoke-sessions` | POST | End all user sessions |
| `/update-user` | POST | Modify user profile (name, image) |
| `/change-email` | POST | Update email address |
| `/set-password` | POST | Add password to OAuth-only account |
| `/delete-user` | POST | Remove user account |
| `/list-accounts` | GET | Get linked authentication providers |
| `/link-social` | POST | Connect OAuth provider to account |
| `/unlink-account` | POST | Disconnect provider |

#### Social OAuth Endpoints

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/sign-in/social` | POST | Initiate OAuth flow (provider specified in body) |
| `/callback/:provider` | GET | OAuth callback handler (e.g., `/callback/google`) |
| `/get-access-token` | GET | Retrieve provider access token |

**Example OAuth flow**:
```typescript
// Client initiates
await authClient.signIn.social({
  provider: "google",
  callbackURL: "/dashboard",
});

// better-auth handles redirect to Google
// Google redirects back to /api/auth/callback/google
// better-auth creates session automatically
```

---

#### Plugin Endpoints

##### Two-Factor Authentication (2FA Plugin)

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

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/two-factor/enable` | POST | Activate 2FA for user |
| `/two-factor/disable` | POST | Deactivate 2FA |
| `/two-factor/
Files: 11
Size: 130.0 KB
Complexity: 87/100
Category: Cloud & DevOps

Related in Cloud & DevOps