jpskill.com
🛠️ 開発・MCP コミュニティ

api-auth-better-auth-drizzle-hono

より良い認証パターン、セッション管理、OAuth認証を実装し、安全で使いやすい認証機能をWebアプリケーションに組み込むことを支援するSkill。

📜 元の英語説明(参考)

Better Auth patterns, sessions, OAuth

🇯🇵 日本人クリエイター向け解説

一言でいうと

より良い認証パターン、セッション管理、OAuth認証を実装し、安全で使いやすい認証機能をWebアプリケーションに組み込むことを支援するSkill。

※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。

⚡ おすすめ: コマンド1行でインストール(60秒)

下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。

🍎 Mac / 🐧 Linux
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o api-auth-better-auth-drizzle-hono.zip https://jpskill.com/download/10221.zip && unzip -o api-auth-better-auth-drizzle-hono.zip && rm api-auth-better-auth-drizzle-hono.zip
🪟 Windows (PowerShell)
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/10221.zip -OutFile "$d\api-auth-better-auth-drizzle-hono.zip"; Expand-Archive "$d\api-auth-better-auth-drizzle-hono.zip" -DestinationPath $d -Force; ri "$d\api-auth-better-auth-drizzle-hono.zip"

完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。

💾 手動でダウンロードしたい(コマンドが難しい人向け)
  1. 1. 下の青いボタンを押して api-auth-better-auth-drizzle-hono.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → api-auth-better-auth-drizzle-hono フォルダができる
  3. 3. そのフォルダを C:\Users\あなたの名前\.claude\skills\(Win)または ~/.claude/skills/(Mac)へ移動
  4. 4. Claude Code を再起動

⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。

🎯 このSkillでできること

下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。

📦 インストール方法 (3ステップ)

  1. 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
  2. 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
  3. 3. 展開してできたフォルダを、ホームフォルダの .claude/skills/ に置く
    • · macOS / Linux: ~/.claude/skills/
    • · Windows: %USERPROFILE%\.claude\skills\

Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。

詳しい使い方ガイドを見る →
最終更新
2026-05-18
取得日時
2026-05-18
同梱ファイル
1

📖 Skill本文(日本語訳)

※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。

Better Auth を用いた認証

クイックガイド: Better Auth (v1.5+) を使用して、TypeScript アプリで型安全な自己ホスト認証を実現します。メール/パスワード、OAuth、2FA、セッション、ステートレス認証、および組織のマルチテナンシーを提供します。プラグインアーキテクチャにより、段階的な複雑さを実現できます。セッションに依存するミドルウェアの前に認証ハンドラーをマウントし、クロスオリジンのデプロイメントのために最初に CORS を構成し、プラグインを追加した後は常にスキーマ生成を実行してください。


<critical_requirements>

重要: この Skill を使用する前に

すべてのコードは、CLAUDE.md のプロジェクト規約に従う必要があります (kebab-case、名前付きエクスポート、インポート順序、import type、名前付き定数)

(セッションに依存する他のミドルウェアの前に、認証ルートに Better Auth ハンドラーをマウントする必要があります)

(クライアントとサーバーが異なるオリジンにある場合は、認証ルートの前に CORS ミドルウェアを構成する必要があります)

(すべてのシークレット (clientId、clientSecret、BETTER_AUTH_SECRET) には環境変数を使用する必要があります - ハードコードしないでください)

(プラグインを追加した後、npx auth@latest generate を実行し、ORM マイグレーションツールを実行する必要があります)

(ミドルウェアで型安全なセッションアクセスを行うには、auth.$Infer.Session 型を使用する必要があります)

</critical_requirements>


自動検出: Better Auth, betterAuth, createAuthClient, auth.handler, auth.api.getSession, socialProviders, twoFactor plugin, organization plugin, drizzleAdapter, セッション管理, OAuth プロバイダー, ステートレスセッション, cookieCache, genericOAuth, oAuthProvider, passkey, SCIM

使用する場面:

  • 自己ホスト認証を構築する場合 (ベンダーロックインなし)
  • メール/パスワード + OAuth + 2FA を 1 つのソリューションで必要とする場合
  • 組織/チーム管理によるマルチテナント SaaS
  • 型安全なセッション管理
  • データベースに保存されたセッションまたはステートレスセッションを必要とするプロジェクト

使用しない場面:

  • メンテナンス不要のマネージド認証が必要な場合 (ホスト型認証ソリューションを検討してください)
  • ユーザーアカウントのない単純な静的サイト
  • サーバーレスのコールドスタートが重要なプロジェクト (ただし、ステートレスモードが役立ちます)

カバーされる主なパターン:

  • プラグインを使用したサーバー構成 (auth.ts)
  • セッションミドルウェアと型安全なルート保護
  • メール/パスワード認証フロー
  • OAuth プロバイダー (GitHub、Google、Generic OAuth)
  • 2 要素認証 (TOTP)
  • 組織とマルチテナンシー
  • セッション戦略: データベース、Cookie キャッシュ、ステートレス
  • データベースアダプターの統合
  • クライアント側の useSession フック
  • パフォーマンス: 実験的な結合、Cookie キャッシュ、ステートレスセッション

詳細なリソース:


<philosophy>

哲学

Better Auth は、認証に対する TypeScript ファースト、自己ホスト アプローチに従います。ユーザーデータはデータベースに残り、ベンダーロックインはありません。プラグインアーキテクチャにより、段階的な複雑さを実現できます。シンプルに始めて、必要に応じて機能を追加します。

コア原則:

  1. 全体を通して型安全性 - セッション型は auth.$Infer.Session を介してサーバーからクライアントに流れます
  2. データベースを信頼できる情報源として - セッションは DB に保存されます (オプションのステートレスモード付き)
  3. プラグインベースの拡張性 - 必要に応じて 2FA、組織、パスキー、SCIM、OAuth プロバイダーを追加します
  4. フレームワークに依存しない - あらゆる TypeScript Web フレームワークで動作します
  5. パフォーマンス重視 - 実験的な結合 (2〜3 倍高速)、Cookie キャッシュ、ステートレスセッション

</philosophy>


<patterns>

コアパターン

パターン 1: サーバー構成 (auth.ts)

データベースアダプターを使用して認証インスタンスを作成します。すべての認証構成の単一の情報源。

// lib/auth.ts
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { db } from "@/lib/db";

const SESSION_EXPIRES_IN_SECONDS = 60 * 60 * 24 * 7; // 7 日
const SESSION_UPDATE_AGE_SECONDS = 60 * 60 * 24; // 毎日更新

export const auth = betterAuth({
  database: drizzleAdapter(db, { provider: "pg" }),
  emailAndPassword: {
    enabled: true,
    minPasswordLength: 8,
    maxPasswordLength: 128,
  },
  session: {
    expiresIn: SESSION_EXPIRES_IN_SECONDS,
    updateAge: SESSION_UPDATE_AGE_SECONDS,
  },
  trustedOrigins: [process.env.APP_URL || "http://localhost:3000"],
});

良い理由: 名前付き定数によりセッションポリシーの監査が可能になり、URL には環境変数を使用し、単一のエクスポートされたインスタンスを使用します

// 悪い例: マジックナンバー、ハードコードされたシークレット、デフォルトエクスポート
const auth = betterAuth({
  database: { url: "postgres://user:pass@localhost/db" },
  session: { expiresIn: 604800 },
});
export default auth;

悪い理由: ハードコードされた認証情報がソース管理でリークし、マジックナンバーがポリシーを不明瞭にし、デフォルトエクスポートを使用します

メール検証と Drizzle アダプター構成による完全なセットアップについては、examples/core.md を参照してください。


パターン 2: 型安全性のあるセッションミドルウェア

認証ハンドラーをマウントし、ルートでセッションアクセスするための型付きミドルウェアを作成します。

// 重要: CORS は認証ルートの前に構成する必要があります
app.use("/auth/*", cors({ origin: APP_URL, credentials: true }));
app.on(["POST", "GET"], "/auth/*", (c) => auth.handler(c.req.raw));

// middleware/auth-middleware.ts - 型安全なセッションアクセス
type AuthVariables = {
  user: typeof auth.$Infer.Session.user | null;
  session: typeof auth.$Infer.Session.session | null;
};

export const authMiddleware = createMiddleware<{ Variables: AuthVariables }>(
  async (c, next) => {
    const session = await auth.api.getSession({ headers: c.req.raw.headers });
    c.set("user", session?.user ?? null);
    c.set("session", session?.session 

(原文がここで切り詰められています)
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開

Authentication with Better Auth

Quick Guide: Use Better Auth (v1.5+) for type-safe, self-hosted authentication in TypeScript apps. It provides email/password, OAuth, 2FA, sessions, stateless auth, and organization multi-tenancy. Plugin architecture enables progressive complexity. Mount auth handler before session-dependent middleware, configure CORS first for cross-origin deployments, and always run schema generation after adding plugins.


<critical_requirements>

CRITICAL: Before Using This Skill

All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering, import type, named constants)

(You MUST mount Better Auth handler on the auth route BEFORE any other middleware that depends on session)

(You MUST configure CORS middleware BEFORE auth routes when client and server are on different origins)

(You MUST use environment variables for ALL secrets (clientId, clientSecret, BETTER_AUTH_SECRET) - NEVER hardcode)

(You MUST run npx auth@latest generate then your ORM migration tool after adding plugins)

(You MUST use auth.$Infer.Session types for type-safe session access in middleware)

</critical_requirements>


Auto-detection: Better Auth, betterAuth, createAuthClient, auth.handler, auth.api.getSession, socialProviders, twoFactor plugin, organization plugin, drizzleAdapter, session management, OAuth providers, stateless sessions, cookieCache, genericOAuth, oAuthProvider, passkey, SCIM

When to use:

  • Building self-hosted authentication (no vendor lock-in)
  • Need email/password + OAuth + 2FA in one solution
  • Multi-tenant SaaS with organization/team management
  • Type-safe session management
  • Projects requiring database-stored or stateless sessions

When NOT to use:

  • Need managed authentication with zero maintenance (consider hosted auth solutions)
  • Simple static sites without user accounts
  • Projects where serverless cold starts are critical (though stateless mode helps)

Key patterns covered:

  • Server configuration (auth.ts) with plugins
  • Session middleware and type-safe route protection
  • Email/password authentication flows
  • OAuth providers (GitHub, Google, Generic OAuth)
  • Two-factor authentication (TOTP)
  • Organization and multi-tenancy
  • Session strategies: database, cookie cache, stateless
  • Database adapter integration
  • Client-side useSession hook
  • Performance: experimental joins, cookie caching, stateless sessions

Detailed Resources:


<philosophy>

Philosophy

Better Auth follows a TypeScript-first, self-hosted approach to authentication. Your user data stays in your database, with no vendor lock-in. The plugin architecture enables progressive complexity - start simple and add features as needed.

Core principles:

  1. Type safety throughout - Session types flow from server to client via auth.$Infer.Session
  2. Database as source of truth - Sessions stored in your DB (with optional stateless mode)
  3. Plugin-based extensibility - Add 2FA, organizations, passkeys, SCIM, OAuth provider when needed
  4. Framework-agnostic - Works with any TypeScript web framework
  5. Performance-focused - Experimental joins (2-3x faster), cookie caching, stateless sessions

</philosophy>


<patterns>

Core Patterns

Pattern 1: Server Configuration (auth.ts)

Create the auth instance with database adapter. Single source of truth for all authentication config.

// lib/auth.ts
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { db } from "@/lib/db";

const SESSION_EXPIRES_IN_SECONDS = 60 * 60 * 24 * 7; // 7 days
const SESSION_UPDATE_AGE_SECONDS = 60 * 60 * 24; // Refresh daily

export const auth = betterAuth({
  database: drizzleAdapter(db, { provider: "pg" }),
  emailAndPassword: {
    enabled: true,
    minPasswordLength: 8,
    maxPasswordLength: 128,
  },
  session: {
    expiresIn: SESSION_EXPIRES_IN_SECONDS,
    updateAge: SESSION_UPDATE_AGE_SECONDS,
  },
  trustedOrigins: [process.env.APP_URL || "http://localhost:3000"],
});

Why good: Named constants make session policy auditable, env vars for URLs, single exported instance

// BAD: Magic numbers, hardcoded secrets, default export
const auth = betterAuth({
  database: { url: "postgres://user:pass@localhost/db" },
  session: { expiresIn: 604800 },
});
export default auth;

Why bad: Hardcoded credentials leak in source control, magic numbers obscure policy, default export

See examples/core.md for full setup with email verification and Drizzle adapter configuration.


Pattern 2: Session Middleware with Type Safety

Mount auth handler and create typed middleware for session access in routes.

// CRITICAL: CORS must be configured BEFORE auth routes
app.use("/auth/*", cors({ origin: APP_URL, credentials: true }));
app.on(["POST", "GET"], "/auth/*", (c) => auth.handler(c.req.raw));
// middleware/auth-middleware.ts - Type-safe session access
type AuthVariables = {
  user: typeof auth.$Infer.Session.user | null;
  session: typeof auth.$Infer.Session.session | null;
};

export const authMiddleware = createMiddleware<{ Variables: AuthVariables }>(
  async (c, next) => {
    const session = await auth.api.getSession({ headers: c.req.raw.headers });
    c.set("user", session?.user ?? null);
    c.set("session", session?.session ?? null);
    await next();
  },
);

Why good: auth.$Infer.Session ensures c.get("user") is correctly typed, CORS before auth prevents preflight failures, c.req.raw provides the Web Standard Request that Better Auth expects

// BAD: No type annotation - c.user is any, bypasses type system
app.use("*", async (c, next) => {
  const session = await auth.api.getSession({ headers: c.req.raw.headers });
  c.user = session?.user; // any - no autocomplete
  await next();
});

Why bad: No AuthVariables type = any access, direct property assignment bypasses typed Variables

See examples/core.md for protected route patterns.


Pattern 3: Schema Generation After Plugins

Every plugin adds database tables. Run the CLI after adding or modifying plugins:

# Step 1: Generate Better Auth schema (outputs ORM-specific files)
npx auth@latest generate

# Step 2: Generate migration with your ORM tool
npx drizzle-kit generate

# Step 3: Apply migration
npx drizzle-kit migrate

Always run all 3 steps. The Better Auth migrate command only works with the Kysely adapter - for Drizzle, use generate + Drizzle Kit.


Pattern 4: Email/Password with Verification

Configure email/password auth with verification and password reset callbacks.

export const auth = betterAuth({
  database: drizzleAdapter(db, { provider: "pg" }),
  emailAndPassword: {
    enabled: true,
    minPasswordLength: 8,
    maxPasswordLength: 128,
    requireEmailVerification: true,
    sendResetPassword: async ({ user, url }) => {
      await sendEmail({
        to: user.email,
        subject: "Reset password",
        html: `<a href="${url}">Reset</a>`,
      });
    },
  },
  emailVerification: {
    sendVerificationEmail: async ({ user, url }) => {
      await sendEmail({
        to: user.email,
        subject: "Verify email",
        html: `<a href="${url}">Verify</a>`,
      });
    },
  },
});

Why good: Email verification prevents fake signups, password requirements enforced server-side

See examples/core.md for client-side sign up/in hooks with error handling.


Pattern 5: Session Strategies

Three session approaches with different trade-offs:

Strategy DB Required Revocable Best For
Database (default) Yes Yes Most apps
Cookie cache + DB Yes Yes (delayed) Reduce DB load
Stateless No No (version-only) Edge/serverless
// Cookie cache: reduces DB hits by caching session in signed cookie
session: {
  cookieCache: { enabled: true, maxAge: CACHE_SECONDS, strategy: "compact" },
}

// Stateless: omit database option entirely
const auth = betterAuth({
  // No database = fully stateless
  session: { cookieCache: { enabled: true, strategy: "jwe" } },
});

Cookie cache strategies: compact (smallest, internal), jwt (standard, third-party verifiable), jwe (encrypted, hides data).

See examples/sessions.md for full configuration and revocation patterns.

</patterns>


<red_flags>

RED FLAGS

  • Hardcoded secrets (clientId/clientSecret in source) - must use environment variables
  • CORS configured after auth routes - preflight requests will fail
  • Missing BETTER_AUTH_SECRET env var - sessions will not work
  • No schema generation after adding plugins - database errors at runtime
  • Untyped session middleware - loses TypeScript safety, c.user becomes any
  • Using auth.migrate() with Drizzle adapter - only works with Kysely, use generate + Drizzle Kit
  • Missing c.req.raw when calling auth.handler() - must pass the raw Web Standard Request

Gotchas & Edge Cases:

  • Google only issues refresh tokens on first consent - use accessType: "offline" and prompt: "consent"
  • GitHub OAuth apps don't issue refresh tokens (access tokens are long-lived)
  • Stateless sessions cannot be revoked individually - increment version to invalidate all
  • Cookie cache revocation is delayed until maxAge expires on other devices
  • Session cookies need SameSite=None + Secure for cross-domain deployments
  • authClient.forgotPassword was renamed to authClient.requestPasswordReset in v1.4
  • InferUser/InferSession removed in v1.5 - use generic User and Session types from better-auth

See reference.md for anti-patterns with code examples, decision frameworks, and version notes.

</red_flags>


<critical_reminders>

CRITICAL REMINDERS

All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering, import type, named constants)

(You MUST mount Better Auth handler on the auth route BEFORE any other middleware that depends on session)

(You MUST configure CORS middleware BEFORE auth routes when client and server are on different origins)

(You MUST use environment variables for ALL secrets (clientId, clientSecret, BETTER_AUTH_SECRET) - NEVER hardcode)

(You MUST run npx auth@latest generate then your ORM migration tool after adding plugins)

(You MUST use auth.$Infer.Session types for type-safe session access in middleware)

Failure to follow these rules will cause authentication failures, security vulnerabilities, or runtime errors.

</critical_reminders>