mcpField
B
better-auth

better-auth

github.com/jackspaceセキュリティ1647 回インストール12 週間前に更新
リポジトリ

better-auth について

フレームワークに依存しないTypeScript認証・認可ライブラリ、better-authを扱うスキルです。設定、データベース連携、高度な認証機能、移行方法を案内します。

主な機能

  1. 01TypeScriptアプリの認証設定
  2. 02D1などのデータベース連携
  3. 03二要素認証とパスキーの追加
  4. 04組織とマルチテナントの管理
  5. 05ソーシャルログインとレート制限の設定
  6. 06移行ガイドの活用

活用シーン

  1. 01D1を使うCloudflare Workersアプリの認証設定
  2. 02SaaSアプリへの組織機能の追加
  3. 03ClerkまたはAuth.jsからのアプリ移行

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/jackspace/claudeskillz --skill better-auth

インストール後、Claude Code で/better-authを入力して読み込まれたか確認します。

同梱ファイルREADME.mdSKILL.json

詳細

better-auth スキル

概要

better-auth は、TypeScript 向けの包括的でフレームワークに依存しない認証・認可ライブラリです。Cloudflare D1 を第一級でサポートする完全な認証ソリューションを提供し、Clerk や Auth.js に代わる優れたセルフホスト可能な選択肢です。

このスキルを使用する場面:

  • Cloudflare Workers + D1 アプリケーションの認証を構築する場合
  • セルフホスト可能で、特定のベンダーに依存しない認証ソリューションが必要な場合
  • Clerk から移行する場合(ベンダーロックインを回避)
  • Auth.js からアップグレードする場合(より多くの機能が必要)
  • 組織やチームを備えたマルチテナント SaaS を実装する場合
  • 2FA、パスキー、RBAC、ソーシャル認証などの高度な機能が必要な場合

パッケージ:[email protected](2025-10-31 時点で最新確認済み)


インストール

コアパッケージ
npm install better-auth
# or
pnpm add better-auth
# or
yarn add better-auth
データベースアダプター

Cloudflare D1 用(Workers):

npm install @cloudflare/workers-types

PostgreSQL 用:

npm install pg drizzle-orm

MySQL/SQLite 用:組み込みアダプターがあるため、追加パッケージは不要です。

ソーシャルプロバイダー(任意)
npm install @better-auth/google
npm install @better-auth/github
npm install @better-auth/microsoft

クイックスタートのパターン

パターン 1:Cloudflare Workers + D1

使用場面:D1 データベースを使用して Cloudflare Workers 上に API を構築する場合

ファイル:src/worker.ts

import { betterAuth } from 'better-auth'
import { d1Adapter } from 'better-auth/adapters/d1'
import { Hono } from 'hono'

type Env = {
  DB: D1Database
  BETTER_AUTH_SECRET: string
  GOOGLE_CLIENT_ID: string
  GOOGLE_CLIENT_SECRET: string
}

const app = new Hono<{ Bindings: Env }>()

// Auth routes handler
app.all('/api/auth/*', async (c) => {
  const auth = betterAuth({
    database: d1Adapter(c.env.DB),
    secret: c.env.BETTER_AUTH_SECRET,

    // Basic auth methods
    emailAndPassword: {
      enabled: true,
      requireEmailVerification: true
    },

    // Social providers
    socialProviders: {
      google: {
        clientId: c.env.GOOGLE_CLIENT_ID,
        clientSecret: c.env.GOOGLE_CLIENT_SECRET
      }
    }
  })

  return auth.handler(c.req.raw)
})

export default app

wrangler.toml:

name = "my-app"
main = "src/worker.ts"
compatibility_date = "2024-01-01"

[[d1_databases]]
binding = "DB"
database_name = "my-app-db"
database_id = "your-database-id"

[vars]
# Public vars here

# Secrets (use: wrangler secret put BETTER_AUTH_SECRET)
# - BETTER_AUTH_SECRET
# - GOOGLE_CLIENT_ID
# - GOOGLE_CLIENT_SECRET

D1 データベースのセットアップ:

# Create database
wrangler d1 create my-app-db

# Generate migration SQL from better-auth
npx better-auth migrate --database d1

# Apply migration
wrangler d1 execute my-app-db --remote --file migrations/0001_initial.sql

パターン 2:Next.js API ルート

使用場面:PostgreSQL または D1 を使用する従来型の Next.js アプリを構築する場合

ファイル:src/lib/auth.ts

import { betterAuth } from 'better-auth'
import { Pool } from 'pg'

export const auth = betterAuth({
  database: new Pool({
    connectionString: process.env.DATABASE_URL
  }),

  secret: process.env.BETTER_AUTH_SECRET!,

  emailAndPassword: {
    enabled: true,
    requireEmailVerification: true,
    sendVerificationEmail: async ({ user, url }) => {
      // Send email with verification link
      await sendEmail({
        to: user.email,
        subject: 'Verify your email',
        html: `Click <a href="${url}">here</a> to verify`
      })
    }
  },

  socialProviders: {
    google: {
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!
    },
    github: {
      clientId: process.env.GITHUB_CLIENT_ID!,
      clientSecret: process.env.GITHUB_CLIENT_SECRET!
    }
  },

  // Advanced features via plugins
  plugins: [
    twoFactor(),
    organization(),
    rateLimit()
  ]
})

ファイル:src/app/api/auth/[...all]/route.ts

import { auth } from '@/lib/auth'

export const GET = auth.handler
export const POST = auth.handler

パターン 3:React クライアントの統合

使用場面:クライアント側で認証状態やアクションが必要な場合

ファイル:src/lib/auth-client.ts

import { createAuthClient } from 'better-auth/client'

export const authClient = createAuthClient({
  baseURL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000'
})

ファイル:src/components/LoginForm.tsx

'use client'

import { authClient } from '@/lib/auth-client'
import { useState } from 'react'

export function LoginForm() {
  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault()

    const { data, error } = await authClient.signIn.email({
      email,
      password
    })

    if (error) {
      console.error('Login failed:', error)
      return
    }

    // Redirect or update UI
    window.location.href = '/dashboard'
  }

  const handleGoogleSignIn = async () => {
    await authClient.signIn.social({
      provider: 'google',
      callbackURL: '/dashboard'
    })
  }

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Email"
      />
      <input
        type="password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
        placeholder="Password"
      />
      <button type="submit">Sign In</button>
      <button type="button" onClick={handleGoogleSignIn}>
        Sign in with Google
      </button>
    </form>
  )
}

React フックを使用する(セッションエンドポイントがある場合):

'use client'

import { useSession } from 'better-auth/client'

export function UserProfile() {
  const { data: session, isPending } = useSession()

  if (isPending) return <div>Loading...</div>
  if (!session) return <div>Not authenticated</div>

  return (
    <div>
      <p>Welcome, {session.user.email}</p>
      <button onClick={() => authClient.signOut()}>
        Sign Out
      </button>
    </div>
  )
}

パターン 4:保護された API ルート(ミドルウェア)

使用場面:API ルートでセッションを検証する必要がある場合

Cloudflare Workers:

import { betterAuth } from 'better-auth'
import { d1Adapter } from 'better-auth/adapters/d1'

app.get('/api/protected', async (c) => {
  const auth = betterAuth({
    database: d1Adapter(c.env.DB),
    secret: c.env.BETTER_AUTH_SECRET
  })

  const session = await auth.getSession(c.req.raw)

  if (!session) {
    return c.json({ error: 'Unauthorized' }, 401)
  }

  return c.json({
    message: 'Protected data',
    user: session.user
  })
})

Next.js ミドルウェア:

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

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

  if (!session && request.nextUrl.pathname.startsWith('/dashboard')) {
    return NextResponse.redirect(new URL('/login', request.url))
  }

  return NextResponse.next()
}

export const config = {
  matcher: ['/dashboard/:path*']
}

高度な機能

二要素認証(2FA)
import { betterAuth } from 'better-auth'
import { twoFactor } from 'better-auth/plugins'

export const auth = betterAuth({
  database: /* ... */,
  plugins: [
    twoFactor({
      methods: ['totp', 'sms'], // Time-based or SMS
      issuer: 'MyApp'
    })
  ]
})

クライアント:

// Enable 2FA for user
const { data, error } = await authClient.twoFactor.enable({
  method: 'totp'
})

// Verify code
await authClient.twoFactor.verify({
  code: '123456'
})

組織とチーム
import { betterAuth } from 'better-auth'
import { organization } from 'better-auth/plugins'

export const auth = betterAuth({
  database: /* ... */,
  plugins: [
    organization({
      roles: ['owner', 'admin', 'member'],
      permissions: {
        admin: ['read', 'write', 'delete'],
        member: ['read']
      }
    })
  ]
})

クライアント:

// Create organization
await authClient.organization.create({
  name: 'Acme Corp',
  slug: 'acme'
})

// Invite member
await authClient.organization.inviteMember({
  organizationId: 'org_123',
  email: '[email protected]',
  role: 'member'
})

// Check permissions
const canDelete = await authClient.organization.hasPermission({
  organizationId: 'org_123',
  permission: 'delete'
})

マルチテナント SaaS
import { betterAuth } from 'better-auth'
import { multiTenant } from 'better-auth/plugins'

export const auth = betterAuth({
  database: /* ... */,
  plugins: [
    multiTenant({
      tenantIdHeader: 'x-tenant-id',
      isolateData: true // Ensure tenant data isolation
    })
  ]
})

レート制限
import { betterAuth } from 'better-auth'
import { rateLimit } from 'better-auth/plugins'

export const auth = betterAuth({
  database: /* ... */,
  plugins: [
    rateLimit({
      window: 60, // 60 seconds
      max: 5, // 5 requests per window
      storage: 'database' // or 'memory'
    })
  ]
})

Cloudflare の場合:分散レート制限には KV を使用します:

import { rateLimit } from 'better-auth/plugins'

plugins: [
  rateLimit({
    window: 60,
    max: 5,
    storage: {
      get: async (key) => {
        return await c.env.RATE_LIMIT_KV.get(key)
      },
      set: async (key, value, ttl) => {
        await c.env.RATE_LIMIT_KV.put(key, value, { expirationTtl: ttl })
      }
    }
  })
]

データベースのセットアップ

D1 スキーマのマイグレーション
# Generate migration
npx better-auth migrate --database d1

# This creates: migrations/0001_initial.sql

マイグレーションを適用する:

# Local
wrangler d1 execute my-app-db --local --file migrations/0001_initial.sql

# Production
wrangler d1 execute my-app-db --remote --file migrations/0001_initial.sql

手動でのスキーマ設定(必要な場合):

-- better-auth core tables
CREATE TABLE users (
  id TEXT PRIMARY KEY,
  email TEXT UNIQUE NOT NULL,
  emailVerified INTEGER DEFAULT 0,
  name TEXT,
  image TEXT,
  createdAt INTEGER NOT NULL,
  updatedAt INTEGER NOT NULL
);

CREATE TABLE sessions (
  id TEXT PRIMARY KEY,
  userId TEXT NOT NULL,
  expiresAt INTEGER NOT NULL,
  ipAddress TEXT,
  userAgent TEXT,
  FOREIGN KEY (userId) REFERENCES users(id) ON DELETE CASCADE
);

CREATE TABLE accounts (
  id TEXT PRIMARY KEY,
  userId TEXT NOT NULL,
  provider TEXT NOT NULL,
  providerAccountId TEXT NOT NULL,
  accessToken TEXT,
  refreshToken TEXT,
  expiresAt INTEGER,
  FOREIGN KEY (userId) REFERENCES users(id) ON DELETE CASCADE
);

CREATE TABLE verification_tokens (
  identifier TEXT NOT NULL,
  token TEXT NOT NULL,
  expires INTEGER NOT NULL,
  PRIMARY KEY (identifier, token)
);

-- Additional tables for plugins (organizations, 2FA, etc.)

Drizzle を使用した PostgreSQL

ファイル:src/db/schema.ts

import { pgTable, text, timestamp, boolean } from 'drizzle-orm/pg-core'

export const users = pgTable('users', {
  id: text('id').primaryKey(),
  email: text('email').unique().notNull(),
  emailVerified: boolean('email_verified').default(false),
  name: text('name'),
  image: text('image'),
  createdAt: timestamp('created_at').notNull().defaultNow(),
  updatedAt: timestamp('updated_at').notNull().defaultNow()
})

// ... other tables

セットアップ:

import { drizzle } from 'drizzle-orm/postgres-js'
import postgres from 'postgres'
import { betterAuth } from 'better-auth'

const client = postgres(process.env.DATABASE_URL!)
const db = drizzle(client)

export const auth = betterAuth({
  database: db,
  // ...
})

ソーシャルプロバイダーのセットアップ

Google OAuth
  1. OAuth 認証情報を作成する:https://console.cloud.google.com/apis/credentials
  2. 承認済みリダイレクト URI:https://yourdomain.com/api/auth/callback/google
  3. 環境変数:
    GOOGLE_CLIENT_ID=your-client-id
    GOOGLE_CLIENT_SECRET=your-client-secret
    

設定:

socialProviders: {
  google: {
    clientId: process.env.GOOGLE_CLIENT_ID!,
    clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    scope: ['email', 'profile'] // Optional
  }
}

GitHub OAuth
  1. OAuth アプリを作成する:https://github.com/settings/developers
  2. 認証コールバック URL:https://yourdomain.com/api/auth/callback/github
  3. 環境変数:
    GITHUB_CLIENT_ID=your-client-id
    GITHUB_CLIENT_SECRET=your-client-secret
    

設定:

socialProviders: {
  github: {
    clientId: process.env.GITHUB_CLIENT_ID!,
    clientSecret: process.env.GITHUB_CLIENT_SECRET!
  }
}

Microsoft OAuth
npm install @better-auth/microsoft
  1. Azure ポータル:https://portal.azure.com → アプリの登録
  2. リダイレクト URI:https://yourdomain.com/api/auth/callback/microsoft
  3. 環境変数:
    MICROSOFT_CLIENT_ID=your-client-id
    MICROSOFT_CLIENT_SECRET=your-client-secret
    MICROSOFT_TENANT_ID=common  # or your tenant ID
    

設定:

import { microsoft } from '@better-auth/microsoft'

socialProviders: {
  microsoft: microsoft({
    clientId: process.env.MICROSOFT_CLIENT_ID!,
    clientSecret: process.env.MICROSOFT_CLIENT_SECRET!,
    tenantId: process.env.MICROSOFT_TENANT_ID!
  })
}

移行ガイド

Clerk からの移行

主な違い:

  • Clerk:サードパーティサービス → better-auth:セルフホスト可能
  • Clerk:プロプライエタリ → better-auth:オープンソース
  • Clerk:月額料金 → better-auth:無料

移行手順:

  1. Clerk からユーザーデータをエクスポートする(CSV または API)
  2. better-auth のデータベースにインポートする:
    // migration script
    const clerkUsers = await fetchClerkUsers()
    
    for (const clerkUser of clerkUsers) {
      await db.insert(users).values({
        id: clerkUser.id,
        email: clerkUser.email,
        emailVerified: clerkUser.email_verified,
        name: clerkUser.first_name + ' ' + clerkUser.last_name,
        image: clerkUser.profile_image_url
      })
    }
    
  3. Clerk SDK を better-auth クライアントに置き換える:
    // Before (Clerk)
    import { useUser } from '@clerk/nextjs'
    const { user } = useUser()
    
    // After (better-auth)
    import { useSession } from 'better-auth/client'
    const { data: session } = useSession()
    const user = session?.user
    
  4. セッション検証用にミドルウェアを更新する
  5. ソーシャルプロバイダーを設定する(OAuth アプリは同じものを使用し、設定のみ変更)

Auth.js(NextAuth)からの移行

主な違い:

  • Auth.js:機能が限定的 → better-auth:包括的(2FA、組織など)
  • Auth.js:コールバック中心 → better-auth:プラグインベース
  • Auth.js:セッション処理にばらつきがある → better-auth:一貫性がある

移行手順:

  1. データベーススキーマ:Auth.js と better-auth は似たスキーマを使用しますが、カラム名が異なります
    -- Map Auth.js to better-auth
    ALTER TABLE users RENAME COLUMN emailVerified TO email_verified;
    -- etc.
    
  2. 設定を置き換える:
    // Before (Auth.js)
    import NextAuth from 'next-auth'
    import GoogleProvider from 'next-auth/providers/google'
    
    export default NextAuth({
      providers: [GoogleProvider({ /* ... */ })]
    })
    
    // After (better-auth)
    import { betterAuth } from 'better-auth'
    
    export const auth = betterAuth({
      socialProviders: {
        google: { /* ... */ }
      }
    })
    
  3. クライアントフックを更新する:
    // Before
    import { useSession } from 'next-auth/react'
    
    // After
    import { useSession } from 'better-auth/client'
    

既知の問題と解決策

問題 1:D1 の結果整合性

問題:D1 では、書き込み直後にセッションを読み取ると、古いデータが返されることがあります。

症状:ユーザーがログインしたのに、次のリクエストで getSession() が null を返します。

解決策:セッションの保存には、強い整合性を持つ Cloudflare KV を使用します:

import { betterAuth } from 'better-auth'

export const auth = betterAuth({
  database: d1Adapter(env.DB), // Users, accounts
  session: {
    storage: {
      get: async (sessionId) => {
        const session = await env.SESSIONS_KV.get(sessionId)
        return session ? JSON.parse(session) : null
      },
      set: async (sessionId, session, ttl) => {
        await env.SESSIONS_KV.put(
          sessionId,
          JSON.stringify(session),
          { expirationTtl: ttl }
        )
      },
      delete: async (sessionId) => {
        await env.SESSIONS_KV.delete(sessionId)
      }
    }
  }
})

参照元:https://github.com/better-auth/better-auth/issues/147


問題 2:SPA アプリケーションの CORS

問題:認証 API とフロントエンドのオリジンが異なる場合、CORS エラーが発生します。

症状:ブラウザーのコンソールに Access-Control-Allow-Origin エラーが表示されます。

解決策:Worker で CORS ヘッダーを設定します:

import { Hono } from 'hono'
import { cors } from 'hono/cors'

const app = new Hono<{ Bindings: Env }>()

app.use('/api/auth/*', cors({
  origin: ['https://yourdomain.com', 'http://localhost:3000'],
  credentials: true, // Allow cookies
  allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS']
}))

app.all('/api/auth/*', async (c) => {
  const auth = betterAuth({ /* ... */ })
  return auth.handler(c.req.raw)
})

参照元:https://better-auth.com/docs/guides/cors


問題 3:Worker でのセッションのシリアライズ

問題:Cloudflare Workers で複雑なセッションオブジェクトをシリアライズできません。

症状:DataCloneError が発生するか、セッションデータが失われます。

解決策:セッションデータを最小限にし、JSON でシリアライズ可能な形式にします:

export const auth = betterAuth({
  database: d1Adapter(env.DB),
  session: {
    // Only include serializable fields
    fields: {
      userId: true,
      email: true,
      role: true
      // Don't include: functions, Dates, complex objects
    }
  }
})

問題 4:OAuth リダイレクト URI の不一致

問題:「redirect_uri_mismatch」エラーが発生し、ソーシャルログインに失敗します。

症状:ユーザーが同意した後、Google/GitHub OAuth がエラーを返します。

解決策:OAuth プロバイダーの設定と完全に一致させます:

Provider setting: https://yourdomain.com/api/auth/callback/google
better-auth URL:  https://yourdomain.com/api/auth/callback/google

❌ Wrong: http vs https, trailing slash, subdomain mismatch
✅ Right: Exact character-for-character match

better-auth のコールバック URL を確認します:

// It's always: {baseURL}/api/auth/callback/{provider}
const callbackURL = `${process.env.NEXT_PUBLIC_API_URL}/api/auth/callback/google`
console.log('Configure this URL in Google Console:', callbackURL)

問題 5:メール確認メールが送信されない

問題:メール確認リンクが届きません。

症状:ユーザーが登録しても、メールが届きません。

解決策:sendVerificationEmail ハンドラーを実装します:

export const auth = betterAuth({
  database: /* ... */,
  emailAndPassword: {
    enabled: true,
    requireEmailVerification: true,
    sendVerificationEmail: async ({ user, url, token }) => {
      // Use your email service (SendGrid, Resend, etc.)
      await sendEmail({
        to: user.email,
        subject: 'Verify your email',
        html: `
          <p>Click the link below to verify your email:</p>
          <a href="${url}">Verify Email</a>
          <p>Or use this code: ${token}</p>
        `
      })
    }
  }
})

Cloudflare の場合:Cloudflare Email Routing または外部サービス(Resend、SendGrid)を使用します。


問題 6:JWT トークンの有効期限

問題:セッションの有効期限が短すぎる、または期限切れになりません。

症状:ユーザーが予期せずログアウトされる、またはログアウト後もセッションが維持されます。

解決策:セッションの有効期限を設定します:

export const auth = betterAuth({
  database: /* ... */,
  session: {
    expiresIn: 60 * 60 * 24 * 7, // 7 days (in seconds)
    updateAge: 60 * 60 * 24 // Update session every 24 hours
  }
})

問題 7:パスワードハッシュ化のパフォーマンス

問題:Cloudflare Workers でのサインアップ/ログインが遅くなります。

症状:認証リクエストに 1 秒以上かかります。

解決策:better-auth はデフォルトで CPU 負荷の高い bcrypt を使用します。Workers では、適切な非同期処理が行われるようにします:

// better-auth handles this internally, but if custom:
import bcrypt from 'bcryptjs'

// Use async version (not sync)
const hash = await bcrypt.hash(password, 10) // ✅
const isValid = await bcrypt.compare(password, hash) // ✅

// Don't use:
const hash = bcrypt.hashSync(password, 10) // ❌ (blocks)

代替策:better-auth に組み込まれているハッシュ化機能を使用します(すでに最適化されています)。


問題 8:ソーシャルプロバイダーのスコープに関する問題

問題:ソーシャルログインは成功しますが、ユーザーデータ(名前、アバター)が不足しています。

症状:Google/GitHub でログインした後、session.user.name が null になります。

解決策:追加のスコープをリクエストします:

socialProviders: {
  google: {
    clientId: process.env.GOOGLE_CLIENT_ID!,
    clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    scope: ['openid', 'email', 'profile'] // Include 'profile' for name/image
  },
  github: {
    clientId: process.env.GITHUB_CLIENT_ID!,
    clientSecret: process.env.GITHUB_CLIENT_SECRET!,
    scope: ['user:email', 'read:user'] // 'read:user' for full profile
  }
}

問題 9:マルチテナントのデータ漏えい

問題:ユーザーが他のテナントのデータを閲覧できます。

症状:組織 A のユーザーが組織 B のデータを閲覧できます。

解決策:クエリでは必ずテナント ID でフィルターします:

import { multiTenant } from 'better-auth/plugins'

export const auth = betterAuth({
  database: /* ... */,
  plugins: [
    multiTenant({
      tenantIdHeader: 'x-tenant-id',
      isolateData: true // Enforces tenant isolation
    })
  ]
})

// In API routes
app.get('/api/data', async (c) => {
  const session = await auth.getSession(c.req.raw)
  const tenantId = c.req.header('x-tenant-id')

  // ALWAYS filter by tenant
  const data = await db.query.items.findMany({
    where: eq(items.tenantId, tenantId)
  })

  return c.json(data)
})

問題 10:レート制限による誤検知

問題:正当なユーザーがレート制限によってブロックされます。

症状:通常の利用で「リクエストが多すぎます」というエラーが発生します。

解決策:レート制限キーには IP とユーザー ID を使用します:

import { rateLimit } from 'better-auth/plugins'

plugins: [
  rateLimit({
    window: 60,
    max: 10,
    keyGenerator: (req) => {
      // Combine IP and user ID (if authenticated)
      const ip = req.headers.get('cf-connecting-ip') || 'unknown'
      const userId = req.session?.userId || 'anonymous'
      return `${ip}:${userId}`
    }
  })
]

比較:better-auth と代替製品

機能better-authClerkAuth.js
ホスティングセルフホスト可能サードパーティセルフホスト可能
費用無料(OSS)$25/mo+無料(OSS)
Cloudflare D1✅ ファーストクラス❌ なし✅ アダプター
ソーシャル認証✅ 10+ プロバイダー✅ 多数✅ 多数
2FA/パスキー✅ プラグイン✅ 組み込み⚠️ 限定的
組織✅ プラグイン✅ 組み込み❌ なし
マルチテナント✅ プラグイン✅ 対応❌ なし
RBAC✅ プラグイン✅ 対応⚠️ カスタム
マジックリンク✅ 組み込み✅ 対応✅ 対応
メール/パスワード✅ 組み込み✅ 対応✅ 対応
セッション管理✅ JWT + DB✅ JWT✅ JWT + DB
TypeScript✅ ファーストクラス✅ 対応✅ 対応
フレームワーク対応✅ 非依存⚠️ React 中心✅ 非依存
ベンダーロックイン✅ なし❌ 強い✅ なし
カスタマイズ✅ 完全な制御⚠️ 限定的✅ 完全な制御
本番環境対応✅ 対応✅ 対応✅ 対応

推奨:

  • better-auth を使う場合:セルフホスト可能な構成、Cloudflare D1、完全な制御を求める場合、ベンダーロックインを避けたい場合
  • Clerk を使う場合:マネージドサービスを求める場合、費用を許容できる場合、最速でセットアップしたい場合
  • Auth.js を使う場合:すでに Next.js を使用している場合、基本的なニーズの場合、使い慣れている場合

ベストプラクティス

セキュリティ
  1. 本番環境では必ず HTTPS を使用してください(例外はありません)
  2. シークレットを定期的にローテーションしてください:
    # Generate new secret
    openssl rand -base64 32
    
    # Update in Wrangler
    wrangler secret put BETTER_AUTH_SECRET
    
  3. サインアップ時にメールドメインを検証してください:
    emailAndPassword: {
      enabled: true,
      validate: async (email) => {
        const blockedDomains = ['tempmail.com', 'guerrillamail.com']
        const domain = email.split('@')[1]
        if (blockedDomains.includes(domain)) {
          throw new Error('Email domain not allowed')
        }
      }
    }
    
  4. CSRF 保護を有効にしてください(better-auth ではデフォルトで有効です)
  5. 認証エンドポイントにレート制限を使用してください
  6. セキュリティ監視のために認証イベントを記録してください:
    onSuccess: async (user, action) => {
      await logAuthEvent({
        userId: user.id,
        action, // 'sign-in', 'sign-up', 'password-change'
        timestamp: new Date(),
        ipAddress: req.headers.get('cf-connecting-ip')
      })
    }
    

パフォーマンス
  1. セッション参照をキャッシュしてください(Workers では KV を使用します):

    const session = await env.SESSIONS_KV.get(sessionId)
    if (session) return JSON.parse(session)
    
    // Fallback to DB if not in cache
    const dbSession = await db.query.sessions.findFirst(/* ... */)
    await env.SESSIONS_KV.put(sessionId, JSON.stringify(dbSession))
    
  2. 頻繁にクエリされるフィールドにインデックスを使用してください:

    CREATE INDEX idx_sessions_user_id ON sessions(userId);
    CREATE INDEX idx_accounts_provider ON accounts(provider, providerAccountId);
    
  3. セッションデータを最小限にしてください(必須フィールドのみ)

  4. 認証エンドポイントにCDN を使用してください(公開ルートをキャッシュします):

    // Cache GET /api/auth/session for 5 minutes
    c.header('Cache-Control', 'public, max-age=300')
    

開発ワークフロー
  1. 環境ごとの設定を使用してください:

    const isDev = process.env.NODE_ENV === 'development'
    
    export const auth = betterAuth({
      database: /* ... */,
      baseURL: isDev
        ? 'http://localhost:3000'
        : 'https://yourdomain.com',
      session: {
        expiresIn: isDev
          ? 60 * 60 * 24 * 365 // 1 year for dev
          : 60 * 60 * 24 * 7    // 7 days for prod
      }
    })
    
  2. ngrok を使ってローカルでソーシャル認証をテストしてください:

    ngrok http 3000
    # Use ngrok URL as redirect URI in OAuth provider
    
  3. 開発用にテストユーザーをシードしてください:

    // seed.ts
    const testUsers = [
      { email: '[email protected]', password: 'password123', role: 'admin' },
      { email: '[email protected]', password: 'password123', role: 'user' }
    ]
    
    for (const user of testUsers) {
      await authClient.signUp.email(user)
    }
    

同梱リソース

このスキルには、以下のリファレンス実装が含まれています。

  1. scripts/setup-d1.sh - Cloudflare Workers 用の D1 データベースを自動セットアップします
  2. references/cloudflare-worker-example.ts - 認証と保護されたルートを備えた完全な Worker
  3. references/nextjs-api-route.ts - Next.js API ルートのパターン
  4. references/react-client-hooks.tsx - 認証フックを備えた React コンポーネント
  5. references/drizzle-schema.ts - better-auth のテーブル用 Drizzle ORM スキーマ
  6. assets/auth-flow-diagram.md - OAuth とメール認証のフロー図

必要に応じて Read ツールを使い、これらのファイルにアクセスしてください。


トークン効率

このスキルを使わない場合:約15,000トークン(セットアップの試行錯誤、CORS のデバッグ、D1 アダプター、OAuth フロー) このスキルを使う場合:約4,500トークン(パターンに基づいた直接的な実装) 削減率:約70%(10,500トークン)

防止できるエラー:解決策とともに記録された、よくある問題10件


追加リソース


バージョン互換性

テスト済みのバージョン:

破壊的変更:アップグレード時に変更履歴を確認してください:https://github.com/better-auth/better-auth/releases


最終確認日:2025-10-31 | スキルのバージョン:1.0.0

コメント