
Better Auth
- 3 installs
- 2 repo stars
- Updated August 3, 2026
- fandhe-ai/agent-reference-skills
Helps with ai & agent building tasks.
About
better-auth is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- better-auth
- AI & Agent Building
- AI-coding skill
Better Auth by the numbers
- 3 all-time installs (skills.sh)
- Ranked #13,677 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/fandhe-ai/agent-reference-skills --skill better-authAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 3 |
|---|---|
| repo stars | ★ 2 |
| Last updated | August 3, 2026 |
| Repository | fandhe-ai/agent-reference-skills ↗ |
What it does
Helps with ai & agent building tasks.
Files
Better Auth API リファレンス
Better Auth — TypeScript 向けフレームワーク非依存の認証・認可フレームワーク。 ユーザーのタスクに応じて適切な README.md を読み、そこから個別ファイルへ辿ること。
ディレクトリ構造
.claude/skills/better-auth/
├── SKILL.md ← このファイル(エントリーポイント)
└── references/
├── getting-started/README.md ← インストール・基本設定(2ページ)
├── concepts/README.md ← コア概念(14ページ)
├── adapters/README.md ← DB アダプター(8ページ)
├── authentication/README.md ← 認証方式(39ページ)
├── plugins/README.md ← プラグイン(32ページ)
├── reference/README.md ← 設定・セキュリティ・エラー(17ページ)
└── guides/README.md ← ガイド(4ページ)探索手順
1. ユーザーのタスクに最も関連するカテゴリを特定する 2. そのカテゴリの README.md を読む 3. README.md 内の一覧から必要な個別ファイルを選んで読む 4. 必要に応じて関連ページのリンクを辿る
カテゴリ → README.md マッピング
| タスク例 | カテゴリ | README パス |
|---|---|---|
| インストール、環境変数、auth インスタンス作成 | getting-started | references/getting-started/README.md |
| auth.api、クライアント、セッション、Cookie、DB、フック、OAuth、レートリミット、型安全性 | concepts | references/concepts/README.md |
| Prisma、Drizzle、MongoDB、SQLite、PostgreSQL、MySQL 等 | adapters | references/adapters/README.md |
| Email/Password、Google、GitHub、Apple 等のソーシャルログイン | authentication | references/authentication/README.md |
| 2FA、Organization、Admin、Passkey、Magic Link、API Key 等 | plugins | references/plugins/README.md |
| 設定オプション一覧、セキュリティ、FAQ、エラーコード | reference | references/reference/README.md |
| プラグイン作成、パフォーマンス最適化、DB アダプター作成 | guides | references/guides/README.md |
Drizzle ORM Adapter
Drizzle ORM アダプターは Better Auth と Drizzle ORM(MySQL, PostgreSQL, SQLite などをサポートする TypeScript ORM)の統合を提供する。
インストール
npm install @better-auth/drizzle-adapterセットアップ
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { db } from "./database.ts";
export const auth = betterAuth({
database: drizzleAdapter(db, {
provider: "sqlite", // or "pg" or "mysql"
}),
// ... 残りの設定
});スキーマ生成
Better Auth CLI で必要なデータベーススキーマを生成:
npx auth@latest generateその後、Drizzle Kit でマイグレーションを適用:
npx drizzle-kit generate
npx drizzle-kit migrate設定オプション
| Option | Type | Description |
|---|---|---|
provider | `"sqlite" \ | "pg" \ |
schema | object | カスタムテーブルスキーママッピング |
usePlural | boolean | 全テーブル名を自動複数形化 |
高度な機能
実験的 Joins (v1.4.0+)
マルチテーブルクエリで 2-3 倍のパフォーマンス改善:
export const auth = betterAuth({
experimental: { joins: true },
});Drizzle スキーマで relation() 関数によるリレーション定義が必要。
カスタマイズ
テーブル名の変更
database: drizzleAdapter(db, {
provider: "sqlite",
schema: {
...schema,
user: schema.users,
},
})フィールド名の変更
// Drizzle スキーマ内
email: varchar("email_address", { length: 255 }).notNull().unique()複数形名の使用
database: drizzleAdapter(db, {
usePlural: true,
})注意点
- セットアップ前に Drizzle のインストールと設定を確認
- Joins 機能ではリレーションをアダプタースキーマを通じて明示的に渡す必要がある
- データベース Joins はデータベースレイテンシに応じて 2-3 倍のパフォーマンス改善を提供
- 追加のガイダンスは Drizzle ドキュメント を参照
MongoDB Adapter
MongoDB アダプターは Better Auth と MongoDB(人気の NoSQL データベース)の統合を提供する。柔軟なスキーマ処理とスケーラブルな認証インフラを MongoDB ベースのアプリケーションに提供。
インストール
npm install @better-auth/mongo-adapterセットアップ
import { betterAuth } from "better-auth";
import { MongoClient } from "mongodb";
import { mongodbAdapter } from "better-auth/adapters/mongodb";
const client = new MongoClient("mongodb://localhost:27017/database");
const db = client.db();
export const auth = betterAuth({
database: mongodbAdapter(db, {
// オプション: client を提供しない場合、データベーストランザクションは有効にならない
client,
}),
});設定オプション
mongodbAdapter(db, options)
| Parameter | Type | Required | Description |
|---|---|---|---|
db | MongoDB Database | Yes | MongoDB データベースインスタンス |
options.client | MongoClient | No | MongoDB クライアントインスタンス — 提供するとトランザクションが有効 |
主要機能
スキーマ管理: MongoDB ではスキーマ生成やマイグレーションは不要。アダプターがコレクションの作成と管理を自動的に処理する。
データベース Joins(実験的、v1.4.0+): 関連データフェッチのクエリパフォーマンスを改善:
export const auth = betterAuth({
experimental: { joins: true },
});データベースレイテンシに応じて 2-3 倍のパフォーマンス改善が見られる。
注意点
- MongoDB はスキーママイグレーションの明示的設定なしで事前設定されている
- データベーストランザクションにはアダプター設定に MongoDB クライアントを渡す必要がある
- パフォーマンス最適化のガイダンスはパフォーマンス最適化ドキュメントを参照
MS SQL Server
MS SQL Server は Microsoft のエンタープライズグレードリレーショナルデータベースシステムで、堅牢なセキュリティとスケーラビリティ機能を持つデータストレージ、管理、分析向けに設計されている。Better Auth は Kysely アダプターを通じて MS SQL と統合する。
セットアップ
import { betterAuth } from "better-auth";
import { MssqlDialect } from "kysely";
import * as Tedious from "tedious";
import * as Tarn from "tarn";
const dialect = new MssqlDialect({
tarn: {
...Tarn,
options: {
min: 0,
max: 10,
},
},
tedious: {
...Tedious,
connectionFactory: () =>
new Tedious.Connection({
authentication: {
options: {
password: "password",
userName: "username",
},
type: "default",
},
options: {
database: "some_db",
port: 1433,
trustServerCertificate: true,
},
server: "localhost",
}),
},
TYPES: {
...Tedious.TYPES,
DateTime: Tedious.TYPES.DateTime2,
},
});
export const auth = betterAuth({
database: {
dialect,
type: "mssql",
},
});設定詳細
接続プール設定:
min: 0— 最小プール接続数max: 10— 最大プール接続数- ポート: 1433(MS SQL デフォルトポート)
型マッピング: DateTime フィールドは適切なタイムスタンプ処理のため DateTime2 にマップされる。
スキーマ管理
マイグレーションサポート
npx auth@latest migrate # サポート済み
npx auth@latest generate # サポート済みBetter Auth CLI は設定と有効なプラグインに基づいてデータベーススキーマの生成とマイグレーションを処理する。
パフォーマンス機能
データベース Joins(実験的)
特定のエンドポイントで 2-3 倍のパフォーマンス改善:
export const auth = betterAuth({
experimental: { joins: true },
});利用条件: Kysely MS SQL ダイアレクト v1.4.0 以降が必要。有効化後のマイグレーション実行が必要な場合がある。
影響を受けるエンドポイント:
/get-session/get-full-organization- その他のデータフェッチエンドポイント
注意点
- 実装は Kysely の MS SQL ダイアレクトに依存。Kysely がサポートする任意のデータベースが Better Auth と互換
- 追加のパフォーマンスガイダンスは「パフォーマンス最適化」ドキュメントを参照
- Kysely の公式ドキュメントで詳細な MssqlDialect 設定オプション を参照
MySQL
MySQL は広く使われるオープンソースリレーショナルデータベース管理システム(RDBMS)で、Web アプリケーションに適している。Better Auth は Kysely アダプターを通じて MySQL との直接統合を提供する。
セットアップ
MySQL がインストール・設定済みであることを確認し、以下のコードで Better Auth に接続:
import { betterAuth } from "better-auth";
import { createPool } from "mysql2/promise";
export const auth = betterAuth({
database: createPool({
host: "localhost",
user: "root",
password: "password",
database: "database",
timezone: "Z", // 一貫したタイムゾーン値を確保するために重要
}),
});MySQL ダイアレクトオプションの詳細は Kysely の MySQLDialect ドキュメント を参照。
スキーマ管理
Better Auth CLI は設定とプラグインに基づいた自動スキーマ生成とマイグレーションを提供:
npx auth@latest generate # スキーマ生成
npx auth@latest migrate # スキーママイグレーションMySQL では両操作が完全にサポートされている。
データベース Joins(実験的)
複数テーブルにまたがる関連データフェッチ時のパフォーマンスを改善し、データベースレイテンシに応じて 2-3 倍のパフォーマンス改善を提供。
import { betterAuth } from "better-auth";
export const auth = betterAuth({
experimental: { joins: true },
});Kysely MySQL ダイアレクトは v1.4.0 以降でネイティブに Joins をサポート。この機能を有効にした後、マイグレーションの実行が必要な場合がある。
注意点
- MySQL 統合は Kysely アダプターを通じて動作する — Kysely がサポートする任意のデータベースが互換
- パフォーマンス最適化のガイダンスは パフォーマンス最適化ガイド を参照
timezone: "Z"の設定で一貫したタイムゾーン値を確保することが重要
Other Relational Databases
Better Auth は Kysely のおかげで幅広いデータベースダイアレクトを標準でサポートしている。Kysely がサポートする任意のダイアレクトが Better Auth で利用可能で、CLI を通じたデータベーススキーマの生成とマイグレーション機能も含まれる。
コアダイアレクト
フレームワークが組み込みサポートする 4 つの主要データベースシステム:
- MySQL
- SQLite
- PostgreSQL
- MS SQL
拡張ダイアレクトサポート
Kysely 公式ダイアレクト
- Postgres.js
- SingleStore Data API
- Supabase
Kysely コミュニティダイアレクト
Better Auth は多数のコミュニティ維持ダイアレクトをサポート:
- PlanetScale Serverless Driver
- Cloudflare D1
- AWS RDS Data API
- Prisma Postgres
- SurrealDB
- Neon
- Xata
- AWS S3 Select
- libSQL/sqld
- Fetch driver
- SQLite WASM
- Deno SQLite
- TiDB Cloud Serverless Driver
- Capacitor SQLite Kysely
- BigQuery
- Clickhouse
- PGLite
コミュニティアダプター
| Adapter | Database | Author |
|---|---|---|
| convex-better-auth | Convex Database | erquhart |
| surreal-better-auth | SurrealDB | Oskar Gmerek |
| surrealdb-better-auth | SurrealDB | Necmttn |
| better-auth-surrealdb | SurrealDB | msanchezdev |
| @payload-auth/better-auth-plugin | Payload CMS | forrestdevs |
| better-auth-instantdb | InstantDB | daveycodez |
| @nerdfolio/remult-better-auth | Remult | Tai Vo |
| pocketbase-better-auth | PocketBase | LightInn |
| better-auth-firestore | Firebase Firestore | yultyyev |
| @zenstackhq/better-auth | ZenStack | zenstackhq |
| @strapi-community/plugin-better-auth | Strapi CMS | boazpoolman |
セットアップ
CLI ドキュメントを参照してデータベーススキーマの生成とマイグレーションを行い、個別のアダプターガイドで具体的なデータベース設定を参照。
サポートされている Kysely ダイアレクトの完全なリストは Kysely 公式ドキュメントサイトで確認できる。
注意点
- Kysely がサポートする任意のダイアレクトが Better Auth で利用可能
- コミュニティアダプターは各 GitHub リポジトリにリンクされており、実装の詳細とインストール要件を確認できる
- サポートされていないデータベース用のアダプター作成が推奨されている
PostgreSQL
Better Auth は PostgreSQL(強力なオープンソースリレーショナルデータベース管理システム)と統合する。Kysely PostgreSQL ダイアレクトを通じてデータベース操作を行う。
セットアップ
import { betterAuth } from "better-auth";
import { Pool } from "pg";
export const auth = betterAuth({
database: new Pool({
connectionString: "postgres://user:password@localhost:5432/database",
}),
});追加の詳細は Kysely の PostgresDialect ドキュメント を参照。
スキーマ管理
Better Auth CLI はスキーマ生成とマイグレーションの両方をサポート:
npx auth@latest migrate
npx auth@latest generate実験的 Joins 機能
/get-session や /get-full-organization などのエンドポイントでパフォーマンスを改善し、データベースレイテンシに応じて 2-3 倍のパフォーマンス改善を提供。
export const auth = betterAuth({
experimental: { joins: true },
});Kysely PostgreSQL ダイアレクトは v1.4.0 以降でこの機能をサポート。有効化後のマイグレーション実行を推奨。
非デフォルトスキーマ設定
オプション 1: 接続文字列(推奨)
options パラメーターを追加:
connectionString: "postgres://user:password@localhost:5432/database?options=-c search_path=auth"URL エンコード版: ?options=-c%20search_path%3Dauth
オプション 2: Pool オプション
database: new Pool({
host: "localhost",
port: 5432,
user: "postgres",
password: "password",
database: "my-db",
options: "-c search_path=auth",
})オプション 3: ユーザーデフォルトスキーマ
ALTER USER your_user SET search_path TO auth;カスタムスキーマの前提条件
CREATE SCHEMA IF NOT EXISTS auth;
GRANT ALL PRIVILEGES ON SCHEMA auth TO your_user;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA auth TO your_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA auth GRANT ALL ON TABLES TO your_user;動作の仕組み
Better Auth CLI は設定された search_path を自動検出する。マイグレーション時、指定スキーマ内のテーブルのみを検査し、他のスキーマを無視して競合を防止。新しいテーブルはすべて指定スキーマに作成される。
トラブルシューティング
エラー: マイグレーション中の「relation does not exist」
解決方法: スキーマが存在し、ユーザーが適切な権限を持っていることを確認(前提条件セクションを参照)。
検証: Better Auth が使用するスキーマを確認:
SHOW search_path;カスタムスキーマ(例: auth)が最初の値として返されるべき。
注意点
- PostgreSQL サポートは Kysely アダプター経由で実装される
- Kysely がサポートする任意のデータベースが Better Auth と互換
- パフォーマンス改善については パフォーマンス最適化ガイド を参照
Prisma Adapter
Prisma アダプターは Better Auth と Prisma ORM(型安全なクエリビルダーと直感的なデータモデリングインターフェース)の統合を提供する。
インストール
npm install @better-auth/prisma-adapterセットアップ
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: "sqlite",
}),
});Prisma 7+ の設定
Prisma バージョン 7 以降では、schema.prisma ファイルの output パスフィールドが必要。カスタム出力パス(例: output = "../src/generated/prisma")を設定した場合、デフォルトの @prisma/client の代わりにカスタムロケーションから Prisma クライアントをインポートする。
スキーマ生成 & マイグレーション
スキーマ生成(サポート済み)
npx auth@latest generateスキーママイグレーション(非サポート)
Prisma アダプターでは Better Auth CLI によるマイグレーションは非サポート。Prisma 自身のマイグレーションツールを使用する。
実験的 Joins 機能
v1.4.0 以降で利用可能。データベース Joins が単一操作で関連データを取得するクエリを最適化し、データベースレイテンシに応じて 2-3 倍のパフォーマンス改善を提供。
export const auth = betterAuth({
experimental: { joins: true },
});要件: Prisma スキーマに @relation ディレクティブによる必要なリレーションが含まれていること、または npx auth@latest generate で再生成する。
注意点
- Prisma 7+ ではカスタム出力パスの設定が必要
- マイグレーションは Prisma 自身のツールで行う(Better Auth CLI のマイグレーションは Kysely のみ対応)
- Joins 機能にはリレーション定義が必要
- 公式 Prisma + Better Auth 統合ガイド: https://www.prisma.io/docs/guides/betterauth-nextjs
Adapters
Better Auth のデータベースアダプター。
| アダプター | 説明 | パス |
|---|---|---|
| Drizzle ORM | Drizzle ORM アダプター設定 | drizzle.md |
| Prisma | Prisma アダプター設定 | prisma.md |
| MongoDB | MongoDB アダプター設定 | mongodb.md |
| SQLite | SQLite (+ Cloudflare D1) アダプター設定 | sqlite.md |
| PostgreSQL | PostgreSQL アダプター設定 | postgresql.md |
| MySQL | MySQL アダプター設定 | mysql.md |
| MS SQL | MS SQL Server アダプター設定 | mssql.md |
| Other Relational | その他 RDB・カスタムアダプター作成 | other-relational.md |
SQLite
SQLite は軽量でサーバーレスの SQL データベースエンジンで、ローカルデータストレージに最適。Better Auth は複数の SQLite ドライバーをサポートし、環境のニーズに応じた選択が可能。
インストールオプション
Better-SQLite3(推奨)
最も安定した人気の Node.js SQLite ドライバー:
import { betterAuth } from "better-auth";
import Database from "better-sqlite3";
export const auth = betterAuth({
database: new Database("database.sqlite"),
});Node.js 組み込み SQLite(実験的)
Node.js 22.5.0+ で利用可能:
import { betterAuth } from "better-auth";
import { DatabaseSync } from "node:sqlite";
export const auth = betterAuth({
database: new DatabaseSync("database.sqlite"),
});実行: node your-app.js
Bun 組み込み SQLite
CLI コマンドでは型エラーを避けるため bunx --bun フラグを使用:
import { betterAuth } from "better-auth";
import { Database } from "bun:sqlite";
export const auth = betterAuth({
database: new Database("database.sqlite"),
});主要機能
スキーマ生成 & マイグレーション
Better Auth CLI は生成とマイグレーションの両方をサポート:
npx auth@latest generate
npx auth@latest migrateJoins(実験的)
関連データクエリのパフォーマンス改善のためデータベース Joins を有効化。/get-session, /get-full-organization などのエンドポイントで、データベースレイテンシに応じて 2-3 倍のパフォーマンス改善。
export const auth = betterAuth({
experimental: { joins: true },
});データベースサポート
SQLite 統合は Kysely アダプターを通じて動作し、Kysely の SqliteDialect(v1.4.0+)との互換性を維持。
注意点
node:sqliteモジュールは実験的であり、変更される可能性がある- 本番デプロイメントにはパフォーマンス最適化ガイドを参照
- 追加の Kysely 設定詳細は公式ドキュメントの SqliteDialect を参照
Email & Password
Built-in authenticator that does not require external provider credentials.
サーバー設定
import { betterAuth } from "better-auth";
export const auth = betterAuth({
emailAndPassword: {
enabled: true,
},
});設定オプション
| Name | Type | Description |
|---|---|---|
enabled | boolean | Enable email/password authentication |
disableSignUp | boolean | Disable user registration |
minPasswordLength | number | Minimum password length (default: 8) |
maxPasswordLength | number | Maximum password length (default: 128) |
sendResetPassword | function | Email sending handler for password resets |
onPasswordReset | function | Callback after successful password reset |
onExistingUserSignUp | function | Callback when signup attempted with existing email |
autoSignIn | boolean | Auto sign-in after signup |
requireEmailVerification | boolean | Require email verification before login |
revokeSessionsOnPasswordReset | boolean | Invalidate all sessions on password change |
resetPasswordTokenExpiresIn | number | Token expiration time |
password | object | Custom hashing algorithm configuration |
クライアント操作
Sign Up
const { data, error } = await authClient.signUp.email({
name: "John Doe",
email: "john.doe@example.com",
password: "password1234",
image: "https://example.com/image.png",
callbackURL: "https://example.com/callback",
});Sign In
const { data, error } = await authClient.signIn.email({
email: "john.doe@example.com",
password: "password1234",
rememberMe: true,
callbackURL: "https://example.com/callback",
});Sign Out
await authClient.signOut({
fetchOptions: {
onSuccess: () => {
router.push("/login");
},
},
});Change Password (authenticated users)
const { data, error } = await authClient.changePassword({
newPassword: "newpassword1234",
currentPassword: "oldpassword1234",
revokeOtherSessions: true,
});Request Password Reset
const { data, error } = await authClient.requestPasswordReset({
email: "john.doe@example.com",
redirectTo: "https://example.com/reset-password",
});Complete Password Reset
const { data, error } = await authClient.resetPassword({
newPassword: "password1234",
token, // from URL parameter
});コード例
Email Verification Configuration
export const auth = betterAuth({
emailVerification: {
sendVerificationEmail: async ({ user, url, token }, request) => {
void sendEmail({
to: user.email,
subject: "Verify your email address",
text: `Click the link to verify your email: ${url}`,
});
},
},
emailAndPassword: {
requireEmailVerification: true,
},
});Password Reset Configuration
emailAndPassword: {
enabled: true,
sendResetPassword: async ({ user, url, token }, request) => {
void sendEmail({
to: user.email,
subject: "Reset your password",
text: `Click the link to reset your password: ${url}`,
});
},
onPasswordReset: async ({ user }, request) => {
console.log(`Password for user ${user.email} has been reset.`);
},
}Custom Password Hashing
Better Auth uses scrypt by default. To use a custom algorithm (e.g., Argon2):
import { hash, verify } from "@node-rs/argon2";
export const auth = betterAuth({
emailAndPassword: {
enabled: true,
password: {
hash: hashPassword,
verify: verifyPassword,
},
},
});注意点
- Default password length: minimum 8, maximum 128 characters
- Avoid awaiting the email sending function to prevent timing attacks
- Email enumeration protection: returns identical responses whether email exists or not when verification is required
- No external OAuth credentials needed
- Use
customSyntheticUseroption when plugins add user fields to maintain security
Other Social Providers (Generic OAuth)
Better Auth supports any OAuth2 or OpenID Connect provider through the Generic OAuth Plugin, with pre-configured helpers for popular services.
Installation
Server Configuration
import { betterAuth } from "better-auth"
import { genericOAuth } from "better-auth/plugins"
export const auth = betterAuth({
plugins: [
genericOAuth({
config: [
{
providerId: "provider-id",
clientId: "test-client-id",
clientSecret: "test-client-secret",
discoveryUrl: "https://auth.example.com/.well-known/openid-configuration"
}
]
})
]
})Client Setup
import { createAuthClient } from "better-auth/client"
import { genericOAuthClient } from "better-auth/client/plugins"
const authClient = createAuthClient({
plugins: [genericOAuthClient()]
})Pre-configured Providers
Example using Slack (also available: Auth0, Keycloak, Okta, Microsoft Entra ID):
import { genericOAuth, slack } from "better-auth/plugins"
export const auth = betterAuth({
plugins: [
genericOAuth({
config: [
slack({
clientId: process.env.SLACK_CLIENT_ID,
clientSecret: process.env.SLACK_CLIENT_SECRET
})
]
})
]
})Sign In
const response = await authClient.signIn.oauth2({
providerId: "slack",
callbackURL: "/dashboard"
})Manual Configuration Examples
- Environment variables:
INSTAGRAM_CLIENT_ID,INSTAGRAM_CLIENT_SECRET - Auth URL:
https://api.instagram.com/oauth/authorize - Token URL:
https://api.instagram.com/oauth/access_token - Scopes:
user_profile,user_media
Coinbase
- Environment variables:
COINBASE_CLIENT_ID,COINBASE_CLIENT_SECRET - Auth URL:
https://www.coinbase.com/oauth/authorize - Token URL:
https://api.coinbase.com/oauth/token - Scopes:
wallet:user:read
Both manual providers follow identical configuration and sign-in patterns as the Slack example above.
Authentication
Better Auth の認証方式。
コア認証
| 方式 | 説明 | パス |
|---|---|---|
| Email & Password | メール・パスワード認証 | email-password.md |
| Social Providers Common | ソーシャルプロバイダー共通設定パターン | social-providers-common.md |
| Other Social Providers | Generic OAuth (40+ プロバイダー) | other-social-providers.md |
Social Providers
| プロバイダー | パス |
|---|---|
| Apple | social-apple.md |
| Atlassian | social-atlassian.md |
| Cognito | social-cognito.md |
| Discord | social-discord.md |
| Dropbox | social-dropbox.md |
| social-facebook.md | |
| Figma | social-figma.md |
| GitHub | social-github.md |
| GitLab | social-gitlab.md |
| social-google.md | |
| Hugging Face | social-huggingface.md |
| Kakao | social-kakao.md |
| Kick | social-kick.md |
| LINE | social-line.md |
| Linear | social-linear.md |
| social-linkedin.md | |
| Microsoft | social-microsoft.md |
| Naver | social-naver.md |
| Notion | social-notion.md |
| Paybin | social-paybin.md |
| PayPal | social-paypal.md |
| Polar | social-polar.md |
| Railway | social-railway.md |
| social-reddit.md | |
| Roblox | social-roblox.md |
| Salesforce | social-salesforce.md |
| Slack | social-slack.md |
| Spotify | social-spotify.md |
| TikTok | social-tiktok.md |
| Twitch | social-twitch.md |
| Twitter (X) | social-twitter.md |
| Vercel | social-vercel.md |
| VK | social-vk.md |
| social-wechat.md | |
| Zoom | social-zoom.md |
Apple
Credentials
APPLE_CLIENT_ID- Service ID (reverse domain format, e.g.,com.yourcompany.yourapp.si)APPLE_CLIENT_SECRET- JWT generated from.p8key fileAPPLE_APP_BUNDLE_IDENTIFIER(optional) - App ID for native iOS implementations
サーバー設定
import { betterAuth } from "better-auth"
export const auth = betterAuth({
socialProviders: {
apple: {
clientId: process.env.APPLE_CLIENT_ID as string,
clientSecret: process.env.APPLE_CLIENT_SECRET as string,
appBundleIdentifier: process.env.APPLE_APP_BUNDLE_IDENTIFIER as string,
},
},
trustedOrigins: ["https://appleid.apple.com"],
})クライアントサインイン
Standard OAuth Flow
import { createAuthClient } from "better-auth/client"
const authClient = createAuthClient()
const signIn = async () => {
const data = await authClient.signIn.social({
provider: "apple"
})
}With ID Token
await authClient.signIn.social({
provider: "apple",
idToken: {
token: // Apple ID Token,
nonce: // Nonce (optional),
accessToken: // Access Token (optional)
}
})リダイレクト URL
https://yourdomain.com/api/auth/callback/apple
Apple Developer Portal の Return URLs に追加する。
プロバイダー固有の設定・注意点
- Service ID Setup: Use reverse domain format distinct from App ID (e.g.,
.sisuffix for service identifier) - Client Secret Requirements: Apple allows a maximum expiration of 6 months (180 days) for the client secret JWT
- Native iOS Consideration: When using ID Token authentication on native iOS, provide
appBundleIdentifierto avoid JWT claim validation failures - Development Limitation: Apple Sign In does not support
localhostor non-HTTPS URLs; valid HTTPS/TLS certificates required - Scope: The documentation does not specify explicit scope configuration examples for Apple OAuth, though standard OAuth scope handling through Better Auth's plugin system applies
Atlassian
Credentials
ATLASSIAN_CLIENT_IDATLASSIAN_CLIENT_SECRET
Obtain from the Atlassian Developer Console.
サーバー設定
import { betterAuth } from "better-auth"
export const auth = betterAuth({
socialProviders: {
atlassian: {
clientId: process.env.ATLASSIAN_CLIENT_ID as string,
clientSecret: process.env.ATLASSIAN_CLIENT_SECRET as string,
},
},
})クライアントサインイン
import { createAuthClient } from "better-auth/client"
const authClient = createAuthClient()
const signIn = async () => {
const data = await authClient.signIn.social({
provider: "atlassian"
})
}リダイレクト URL
https://yourdomain.com/api/auth/callback/atlassian
Atlassian Developer Console でコールバック URL を設定する。auth ルートのベースパスを変更した場合は、リダイレクト URI も更新する。
プロバイダー固有の設定・注意点
- Default scopes:
read:jira-userandoffline_access - For additional scopes, consult the Atlassian OAuth 2.0 (3LO) apps documentation
Cognito (Amazon Cognito)
Credentials
COGNITO_CLIENT_IDCOGNITO_CLIENT_SECRETCOGNITO_DOMAINCOGNITO_REGIONCOGNITO_USERPOOL_ID
サーバー設定
import { betterAuth } from "better-auth";
export const auth = betterAuth({
socialProviders: {
cognito: {
clientId: process.env.COGNITO_CLIENT_ID as string,
clientSecret: process.env.COGNITO_CLIENT_SECRET as string,
domain: process.env.COGNITO_DOMAIN as string,
region: process.env.COGNITO_REGION as string,
userPoolId: process.env.COGNITO_USERPOOL_ID as string,
},
},
})クライアントサインイン
import { createAuthClient } from "better-auth/client"
const authClient = createAuthClient()
const signIn = async () => {
const data = await authClient.signIn.social({
provider: "cognito"
})
}リダイレクト URL
http://localhost:3000/api/auth/callback/cognito (ローカル開発)
プロバイダー固有の設定・注意点
Setup Prerequisites
User Pool is required for Cognito authentication. Callback URL must match exactly.
Configuration steps: 1. Create User Pool in AWS Cognito Console 2. Configure App client (note Client ID and Secret) 3. Set Cognito Hosted UI domain 4. Enable OAuth flows: "Authorization code grant" 5. Enable OAuth scopes: "openid", "profile", "email" 6. Add callback URL (e.g., http://localhost:3000/api/auth/callback/cognito)
Scopes
Common Cognito scopes:
openid: Required for OpenID Connectprofile: Basic profile information accessemail: User email accessphone: Phone number accessaws.cognito.signin.user.admin: Cognito-specific APIs
Custom Options
scope: Additional OAuth2 scopes (array format)getUserInfo: Custom function retrieving user information from Cognito UserInfo endpoint
Scopes must be configured in the Cognito App Client settings before use.
Discord
Credentials
DISCORD_CLIENT_IDDISCORD_CLIENT_SECRET
Obtain from the Discord Developer Portal.
サーバー設定
import { betterAuth } from "better-auth"
export const auth = betterAuth({
socialProviders: {
discord: {
clientId: process.env.DISCORD_CLIENT_ID as string,
clientSecret: process.env.DISCORD_CLIENT_SECRET as string,
},
},
})クライアントサインイン
import { createAuthClient } from "better-auth/client"
const authClient = createAuthClient()
const signIn = async () => {
const data = await authClient.signIn.social({
provider: "discord"
})
}リダイレクト URL
- Development:
http://localhost:3000/api/auth/callback/discord - Production: Update to match your application's domain
- Custom base paths: Adjust the redirect URL if you modify the auth route base path
プロバイダー固有の設定・注意点
Bot Permissions
If utilizing the bot scope, specify permissions via bitwise values or specific permission codes:
discord: {
clientId: process.env.DISCORD_CLIENT_ID as string,
clientSecret: process.env.DISCORD_CLIENT_SECRET as string,
permissions: 2048 | 16384, // Send Messages + Embed Links
}The permissions parameter only works when the bot scope is included in your OAuth2 scopes. Consult Discord's permissions documentation for additional details.
For the complete list of supported options across all social providers, refer to the Provider Options documentation.
Dropbox
Credentials
DROPBOX_CLIENT_IDDROPBOX_CLIENT_SECRET
Obtain from the Dropbox Developer Portal.
サーバー設定
import { betterAuth } from "better-auth"
export const auth = betterAuth({
socialProviders: {
dropbox: {
clientId: process.env.DROPBOX_CLIENT_ID as string,
clientSecret: process.env.DROPBOX_CLIENT_SECRET as string,
},
},
})クライアントサインイン
import { createAuthClient } from "better-auth/client"
const authClient = createAuthClient()
const signIn = async () => {
const data = await authClient.signIn.social({
provider: "dropbox"
})
}リダイレクト URL
- Development:
http://localhost:3000/api/auth/callback/dropbox - Production: Adjust to your application's domain
プロバイダー固有の設定・注意点
- OAuth Flow: The provider supports "Implicit Grant & PKCE" flow configuration in the Dropbox App Console
- Consult the official Dropbox OAuth documentation for deeper implementation details
Credentials
FACEBOOK_CLIENT_ID(App ID from Facebook Developer Portal, App Settings > Basic)FACEBOOK_CLIENT_SECRET(App Secret from Facebook Developer Portal, App Settings > Basic)
Security Note: Avoid exposing the clientSecret in client-side code (e.g., frontend apps) because it's sensitive information.
サーバー設定
import { betterAuth } from "better-auth"
export const auth = betterAuth({
socialProviders: {
facebook: {
clientId: process.env.FACEBOOK_CLIENT_ID as string,
clientSecret: process.env.FACEBOOK_CLIENT_SECRET as string,
},
},
})Facebook Login for Business
When using Business apps, add the configId alongside credentials:
facebook: {
clientId: process.env.FACEBOOK_CLIENT_ID as string,
clientSecret: process.env.FACEBOOK_CLIENT_SECRET as string,
configId: "your-config-id"
}Must be "User access token" type; "System-user access token" is unsupported.
クライアントサインイン
import { createAuthClient } from "better-auth/auth-client"
const authClient = createAuthClient()
const signIn = async () => {
const data = await authClient.signIn.social({
provider: "facebook"
})
}ID Token Sign-In
const data = await authClient.signIn.social({
provider: "facebook",
idToken: {
...(platform === 'ios' ?
{ token: idToken }
: { token: accessToken, accessToken: accessToken }),
},
})リダイレクト URL
- Development:
http://localhost:3000/api/auth/callback/facebook - Production: Update to your application's domain
プロバイダー固有の設定・注意点
Scopes & Fields Configuration
facebook: {
clientId: process.env.FACEBOOK_CLIENT_ID as string,
clientSecret: process.env.FACEBOOK_CLIENT_SECRET as string,
scopes: ["email", "public_profile", "user_friends"],
fields: ["user_friends"],
}| Option | Purpose | Default |
|---|---|---|
scopes | Access basic account information (overwrites defaults) | "email", "public_profile" |
fields | Extend retrieved user profile fields | "id", "name", "email", "picture" |
Reference the Facebook Permissions Documentation for the complete list of available permissions.
Figma
Credentials
FIGMA_CLIENT_IDFIGMA_CLIENT_SECRET
Obtain from Figma Developer Apps.
Getting Your Credentials
1. Sign in to your Figma account 2. Navigate to the Developer Apps page 3. Click "Create new app" 4. Complete app details (name, description, etc.) 5. Configure your redirect URI: https://yourdomain.com/api/auth/callback/figma 6. Copy your Client ID and Client Secret
サーバー設定
import { betterAuth } from "better-auth"
export const auth = betterAuth({
socialProviders: {
figma: {
clientId: process.env.FIGMA_CLIENT_ID as string,
clientSecret: process.env.FIGMA_CLIENT_SECRET as string,
},
},
})クライアントサインイン
import { createAuthClient } from "better-auth/client"
const authClient = createAuthClient()
const signIn = async () => {
const data = await authClient.signIn.social({
provider: "figma"
})
}リダイレクト URL
https://yourdomain.com/api/auth/callback/figma
プロバイダー固有の設定・注意点
- Default scope:
current_user:read - For additional scopes like
file_content:read, consult the Figma OAuth scopes documentation - Ensure your redirect URI matches your application's callback URL exactly
- Reference the official Figma API documentation for comprehensive OAuth details
GitHub
Credentials
GITHUB_CLIENT_IDGITHUB_CLIENT_SECRET
サーバー設定
import { betterAuth } from "better-auth"
export const auth = betterAuth({
socialProviders: {
github: {
clientId: process.env.GITHUB_CLIENT_ID as string,
clientSecret: process.env.GITHUB_CLIENT_SECRET as string,
},
},
})クライアントサインイン
import { createAuthClient } from "better-auth/client"
const authClient = createAuthClient()
const signIn = async () => {
const data = await authClient.signIn.social({
provider: "github"
})
}リダイレクト URL
- Local development:
http://localhost:3000/api/auth/callback/github - Production: Your application's production URL
GitHub Developer Portal で設定する。
プロバイダー固有の設定・注意点
Email Scope Requirement
You MUST include the user:email scope in your GitHub app. This is essential for proper functionality.
GitHub App vs OAuth App Setup
For GitHub Apps, enable email reading: 1. Navigate to Permissions and Events > Account Permissions > Email Addresses 2. Select "Read-Only" 3. Save changes
If you encounter an "email_not_found" error, verify you've configured email permissions for GitHub Apps.
Token Behavior
GitHub does not issue refresh tokens. Access tokens remain valid indefinitely unless the user revokes them, the app revokes them, or they go unused for a year.
GitLab
Credentials
GITLAB_CLIENT_IDGITLAB_CLIENT_SECRETGITLAB_ISSUER(Optional) - URL for self-hosted GitLab instances; defaults to"https://gitlab.com"
サーバー設定
import { betterAuth } from "better-auth"
export const auth = betterAuth({
socialProviders: {
gitlab: {
clientId: process.env.GITLAB_CLIENT_ID as string,
clientSecret: process.env.GITLAB_CLIENT_SECRET as string,
issuer: process.env.GITLAB_ISSUER as string,
},
},
})Self-Hosted GitLab Configuration
export const auth = betterAuth({
socialProviders: {
gitlab: {
clientId: process.env.GITLAB_CLIENT_ID as string,
clientSecret: process.env.GITLAB_CLIENT_SECRET as string,
issuer: "https://gitlab.company.com",
},
},
})クライアントサインイン
import { createAuthClient } from "better-auth/client"
const authClient = createAuthClient()
const signIn = async () => {
const data = await authClient.signIn.social({
provider: "gitlab"
})
}リダイレクト URL
- Local development:
http://localhost:3000/api/auth/callback/gitlab - Production: Adjust to your application's URL
プロバイダー固有の設定・注意点
- The
issuerparameter enables flexibility for organizations using self-hosted GitLab instances separate from the public GitLab.com service
Credentials
GOOGLE_CLIENT_ID- OAuth client ID from Google Cloud ConsoleGOOGLE_CLIENT_SECRET- OAuth client secret from Google Cloud Console
サーバー設定
import { betterAuth } from "better-auth"
export const auth = betterAuth({
baseURL: process.env.BETTER_AUTH_URL,
socialProviders: {
google: {
clientId: process.env.GOOGLE_CLIENT_ID as string,
clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
},
},
})Critical Note: Setting baseURL is mandatory to avoid redirect URI mismatches. Configure via environment variable: BETTER_AUTH_URL=https://your-domain.com
クライアントサインイン
Basic sign-in
import { createAuthClient } from "better-auth/client"
const authClient = createAuthClient()
const signIn = async () => {
const data = await authClient.signIn.social({
provider: "google",
})
}ID token-based sign-in (no redirection)
const data = await authClient.signIn.social({
provider: "google",
idToken: {
token: // Google ID Token,
accessToken: // Google Access Token
}
})リダイレクト URL
Google Cloud Console で設定する。
プロバイダー固有の設定・注意点
Always prompt account selection
google: {
prompt: "select_account",
clientId: process.env.GOOGLE_CLIENT_ID as string,
clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
}Obtain refresh tokens reliably
google: {
clientId: process.env.GOOGLE_CLIENT_ID as string,
clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
accessType: "offline",
prompt: "select_account consent",
}Additional Scopes Configuration
Request additional scopes after initial signup:
const requestGoogleDriveAccess = async () => {
await authClient.linkSocial({
provider: "google",
scopes: ["https://www.googleapis.com/auth/drive.file"],
});
}Requirement: Better Auth version 1.2.7 or later prevents "Social account already linked" errors when requesting additional scopes.
Hugging Face
Credentials
HUGGINGFACE_CLIENT_IDHUGGINGFACE_CLIENT_SECRET
Obtain from the Hugging Face OAuth documentation.
サーバー設定
import { betterAuth } from "better-auth"
export const auth = betterAuth({
socialProviders: {
huggingface: {
clientId: process.env.HUGGINGFACE_CLIENT_ID as string,
clientSecret: process.env.HUGGINGFACE_CLIENT_SECRET as string,
},
},
})クライアントサインイン
import { createAuthClient } from "better-auth/client"
const authClient = createAuthClient()
const signIn = async () => {
const data = await authClient.signIn.social({
provider: "huggingface"
})
}リダイレクト URL
- Local development:
http://localhost:3000/api/auth/callback/huggingface - Production: Use your application's actual URL
- If using custom auth route base paths, adjust the callback URL accordingly
プロバイダー固有の設定・注意点
- Required Scope: Ensure the OAuth application includes the "email" scope for proper functionality
Kakao
A social authentication provider for East Asian users, particularly popular in South Korea.
Credentials
KAKAO_CLIENT_IDKAKAO_CLIENT_SECRET
Obtain from the Kakao Developer Portal.
サーバー設定
import { betterAuth } from "better-auth"
export const auth = betterAuth({
socialProviders: {
kakao: {
clientId: process.env.KAKAO_CLIENT_ID as string,
clientSecret: process.env.KAKAO_CLIENT_SECRET as string,
},
},
})クライアントサインイン
import { createAuthClient } from "better-auth/client"
const authClient = createAuthClient()
const signIn = async () => {
const data = await authClient.signIn.social({
provider: "kakao"
})
}リダイレクト URL
- Local development:
http://localhost:3000/api/auth/callback/kakao - Production: Update to your application's actual domain
Kakao Developer Portal で設定する。
プロバイダー固有の設定・注意点
Default Scopes
account_emailprofile_imageprofile_nickname
Email Access Requirement
Retrieving account_email requires your application to be a "Biz App" -- an app that has completed business verification through Kakao. For scope details, consult the Kakao Login scopes documentation.
This restriction means standard apps may not access verified email addresses without completing Kakao's business verification process.
Kick
Credentials
KICK_CLIENT_IDKICK_CLIENT_SECRET
Obtain from the Kick Developer Portal.
サーバー設定
import { betterAuth } from "better-auth"
export const auth = betterAuth({
socialProviders: {
kick: {
clientId: process.env.KICK_CLIENT_ID as string,
clientSecret: process.env.KICK_CLIENT_SECRET as string,
},
},
})クライアントサインイン
import { createAuthClient } from "better-auth/client"
const authClient = createAuthClient()
const signIn = async () => {
const data = await authClient.signIn.social({
provider: "kick"
})
}リダイレクト URL
- Local Development:
http://localhost:3000/api/auth/callback/kick - Production: Update to match your application's URL
- Adjust the path if you've customized your auth route base path
プロバイダー固有の設定・注意点
- For additional scopes or provider-specific options beyond the standard configuration, refer to the official Kick OAuth documentation or the Better Auth "Other Social Providers" guide for extended customization patterns
LINE
A messaging platform popular in Asia for social authentication.
Credentials
LINE_CLIENT_ID- Your Channel IDLINE_CLIENT_SECRET- Your Channel secret
Obtain from the LINE Developers Console.
サーバー設定
import { betterAuth } from "better-auth";
export const auth = betterAuth({
socialProviders: {
line: {
clientId: process.env.LINE_CLIENT_ID as string,
clientSecret: process.env.LINE_CLIENT_SECRET as string,
// redirectURI: "https://your.app/api/auth/callback/line",
// scope: ["custom"],
// disableDefaultScope: true,
},
},
});クライアントサインイン
Standard OAuth Flow
import { createAuthClient } from "better-auth/client";
const authClient = createAuthClient();
async function signInWithLINE() {
const res = await authClient.signIn.social({ provider: "line" });
}Direct ID Token Sign-In
await authClient.signIn.social({
provider: "line",
idToken: {
token: "<LINE_ID_TOKEN>",
accessToken: "<LINE_ACCESS_TOKEN>",
},
});リダイレクト URL
LINE Developers Console で設定する。Redirect URI must match exactly what's configured.
プロバイダー固有の設定・注意点
- Default Scopes:
openid profile email(customizable via provider options) - ID Token Verification: Uses the official endpoint and checks audience and optional nonce per spec
Multi-Channel Support
LINE requires separate OAuth channels for different countries (Japan, Thailand, Taiwan). Use the Generic OAuth plugin with the line() helper:
import { betterAuth } from "better-auth";
import { genericOAuth, line } from "better-auth/plugins";
export const auth = betterAuth({
plugins: [
genericOAuth({
config: [
line({
providerId: "line-jp",
clientId: process.env.LINE_JP_CLIENT_ID,
clientSecret: process.env.LINE_JP_CLIENT_SECRET,
}),
// Additional channels...
],
}),
],
});Sign in using the appropriate providerId like "line-jp", "line-th", or "line-tw".
Linear
Credentials
LINEAR_CLIENT_IDLINEAR_CLIENT_SECRET
Obtain from the Linear Developer Portal.
サーバー設定
import { betterAuth } from "better-auth"
export const auth = betterAuth({
socialProviders: {
linear: {
clientId: process.env.LINEAR_CLIENT_ID as string,
clientSecret: process.env.LINEAR_CLIENT_SECRET as string,
},
},
})クライアントサインイン
import { createAuthClient } from "better-auth/client"
const authClient = createAuthClient()
const signIn = async () => {
const data = await authClient.signIn.social({
provider: "linear"
})
}リダイレクト URL
- Local Development:
http://localhost:3000/api/auth/callback/linear - Production: Your application's URL with the same callback path
プロバイダー固有の設定・注意点
Available Scopes
| Scope | Purpose |
|---|---|
read | Default scope; read access for user account |
write | Write access for user account |
issues:create | Create new issues and attachments |
comments:create | Create issue comments |
timeSchedule:write | Create and modify time schedules |
admin | Full admin-level endpoint access (use cautiously) |
Configuring Custom Scopes
export const auth = betterAuth({
socialProviders: {
linear: {
clientId: process.env.LINEAR_CLIENT_ID as string,
clientSecret: process.env.LINEAR_CLIENT_SECRET as string,
scope: ["read", "write"]
},
},
})Specify your desired scopes in the scope array to request additional permissions beyond the default read access.
Credentials
LINKEDIN_CLIENT_IDLINKEDIN_CLIENT_SECRET
Obtain from the LinkedIn Developer Portal.
サーバー設定
import { betterAuth } from "better-auth"
export const auth = betterAuth({
socialProviders: {
linkedin: {
clientId: process.env.LINKEDIN_CLIENT_ID as string,
clientSecret: process.env.LINKEDIN_CLIENT_SECRET as string,
},
},
})クライアントサインイン
import { createAuthClient } from "better-auth/client"
const authClient = createAuthClient()
const signIn = async () => {
const data = await authClient.signIn.social({
provider: "linkedin"
})
}リダイレクト URL
- Local development:
http://localhost:3000/api/auth/callback/linkedin - Production: Update to your application's actual URL
プロバイダー固有の設定・注意点
- Required LinkedIn Product: You must enable "Sign In with LinkedIn using OpenID Connect" in your LinkedIn Developer Portal under products
- Review the official Sign In with LinkedIn using OpenID Connect documentation for implementation details
Microsoft
Via Azure Entra ID (formerly Active Directory).
Credentials
MICROSOFT_CLIENT_IDMICROSOFT_CLIENT_SECRET
Generate through the Microsoft Entra ID dashboard. See the Microsoft Entra ID documentation for detailed setup instructions.
サーバー設定
import { betterAuth } from "better-auth"
export const auth = betterAuth({
socialProviders: {
microsoft: {
clientId: process.env.MICROSOFT_CLIENT_ID as string,
clientSecret: process.env.MICROSOFT_CLIENT_SECRET as string,
// Optional configuration
tenantId: 'common',
authority: "https://login.microsoftonline.com",
prompt: "select_account",
},
},
})クライアントサインイン
import { createAuthClient } from "better-auth/client";
const authClient = createAuthClient();
const signIn = async () => {
const data = await authClient.signIn.social({
provider: "microsoft",
callbackURL: "/dashboard",
});
};リダイレクト URL
http://localhost:3000/api/auth/callback/microsoft (ローカル開発)
プロバイダー固有の設定・注意点
- Authority URL: Use
https://login.microsoftonline.comfor standard Entra ID scenarios orhttps://<tenant-id>.ciamlogin.comfor CIAM (Customer Identity and Access Management) implementations - Tenant ID: Defaults to
'common'for multi-tenant applications - Prompt Parameter: Set to
"select_account"to force account selection during authentication - The
signIn.socialfunction accepts the provider name and optional callback URL for post-authentication redirection
Naver
A South Korean authentication provider.
Credentials
NAVER_CLIENT_IDNAVER_CLIENT_SECRET
Obtain from the Naver Developers portal.
サーバー設定
import { betterAuth } from "better-auth"
export const auth = betterAuth({
socialProviders: {
naver: {
clientId: process.env.NAVER_CLIENT_ID as string,
clientSecret: process.env.NAVER_CLIENT_SECRET as string,
},
},
})クライアントサインイン
import { createAuthClient } from "better-auth/client"
const authClient = createAuthClient()
const signIn = async () => {
const data = await authClient.signIn.social({
provider: "naver"
})
}リダイレクト URL
- Development:
http://localhost:3000/api/auth/callback/naver - Production: Update to your application's URL
- If you change the base path of the auth routes, you should update the redirect URL accordingly
プロバイダー固有の設定・注意点
- Beyond the basic clientId and clientSecret configuration, specific scope requests and provider-specific options are not detailed in the documentation
Notion
Credentials
NOTION_CLIENT_IDNOTION_CLIENT_SECRET
Obtain from the Notion Developers Portal.
サーバー設定
import { betterAuth } from "better-auth"
export const auth = betterAuth({
socialProviders: {
notion: {
clientId: process.env.NOTION_CLIENT_ID as string,
clientSecret: process.env.NOTION_CLIENT_SECRET as string,
},
},
})クライアントサインイン
import { createAuthClient } from "better-auth/client"
const authClient = createAuthClient()
const signIn = async () => {
const data = await authClient.signIn.social({
provider: "notion"
})
}リダイレクト URL
Notion integration settings の OAuth Domain & URIs で設定:
- Local development:
http://localhost:3000/api/auth/callback/notion - Production:
https://example.com/api/auth/callback/notion
プロバイダー固有の設定・注意点
Required Capabilities
Enable the "Read user information including email addresses" capability in your Notion integration for user authentication.
Integration Types
Notion supports two integration models:
- Public integrations: Installable by any Notion workspace
- Internal integrations: Limited to your own workspace
Choose public for multi-workspace authentication scenarios.
Additional Scopes
Request additional Notion capabilities post-signup using the linkSocial method:
const requestNotionAccess = async () => {
await authClient.linkSocial({
provider: "notion",
});
};After authentication, leverage the access token to interact with the Notion API for managing pages, databases, and other workspace content.
Paybin
An OAuth 2.0 social authentication provider.
Credentials
PAYBIN_CLIENT_IDPAYBIN_CLIENT_SECRET
Obtain from your Paybin Portfolio application's Developer Settings or OAuth Applications section.
サーバー設定
import { betterAuth } from "better-auth"
export const auth = betterAuth({
socialProviders: {
paybin: {
clientId: process.env.PAYBIN_CLIENT_ID as string,
clientSecret: process.env.PAYBIN_CLIENT_SECRET as string,
},
},
})クライアントサインイン
import { createAuthClient } from "better-auth/client"
const authClient = createAuthClient()
const signIn = async () => {
const data = await authClient.signIn.social({
provider: "paybin"
})
}リダイレクト URL
- Local Development:
http://localhost:3000/api/auth/callback/paybin - Production:
https://yourdomain.com/api/auth/callback/paybin
プロバイダー固有の設定・注意点
Default Scopes
openid, email, profile
Custom Scopes Example
export const auth = betterAuth({
socialProviders: {
paybin: {
clientId: process.env.PAYBIN_CLIENT_ID as string,
clientSecret: process.env.PAYBIN_CLIENT_SECRET as string,
scope: ["openid", "email", "profile", "transactions"],
},
},
})User Profile Mapping
Paybin follows OpenID Connect standards and automatically extracts:
- id from
subclaim - name from
name,preferred_username, oremail(priority order) - email from
emailclaim - image from
pictureclaim - emailVerified from
email_verifiedclaim
PayPal
Credentials
PAYPAL_CLIENT_IDPAYPAL_CLIENT_SECRET
Obtain by creating an application in the PayPal Developer Portal, configuring "Log in with PayPal" under "Other features," and setting your Return URL.
サーバー設定
import { betterAuth } from "better-auth"
export const auth = betterAuth({
socialProviders: {
paypal: {
clientId: process.env.PAYPAL_CLIENT_ID as string,
clientSecret: process.env.PAYPAL_CLIENT_SECRET as string,
environment: "sandbox", // or "live" for production
},
},
})Advanced Configuration
export const auth = betterAuth({
socialProviders: {
paypal: {
clientId: process.env.PAYPAL_CLIENT_ID as string,
clientSecret: process.env.PAYPAL_CLIENT_SECRET as string,
environment: "live",
requestShippingAddress: true,
},
},
})クライアントサインイン
import { createAuthClient } from "better-auth/client"
const authClient = createAuthClient()
const signIn = async () => {
const data = await authClient.signIn.social({
provider: "paypal"
})
}リダイレクト URL
Return URL を PayPal Developer Portal で設定する。
プロバイダー固有の設定・注意点
Configuration Options
| Option | Type | Default | Description |
|---|---|---|---|
environment | `'sandbox' \ | 'live'` | "sandbox" |
requestShippingAddress | boolean | false | Request shipping address information |
scope | string[] | Dashboard-configured | Additional permission scopes |
mapProfileToUser | function | Default mapping | Custom profile-to-user transformation |
getUserInfo | function | Default retrieval | Custom user information retrieval |
verifyIdToken | function | Default verification | Custom ID token verification |
Important Notes
- Environments: PayPal provides Sandbox (testing) and Live (production) environments
- Testing: Create sandbox test accounts in the Developer Dashboard; real accounts don't work in sandbox mode
- URL Matching: The Return URL must exactly match your configured redirect URI
- Local Testing: PayPal API requires a public domain; use NGROK or similar for HTTPS localhost testing
- Permissions: PayPal doesn't use traditional OAuth2 scopes; configure permissions directly in the Developer Dashboard
- Approval: Live applications require PayPal review before deployment, typically taking several weeks
- Scope Configuration: Permissions set in Developer Dashboard rather than authorization URL
Polar
A provider for OAuth 2.0 social authentication.
Credentials
POLAR_CLIENT_IDPOLAR_CLIENT_SECRET
Obtain from Polar User Settings.
Getting Credentials
1. Navigate to your Polar User Settings OAuth section 2. Create a new OAuth Client 3. Configure the following fields:
- Application Name: Display name during authorization
- Client Type: Select appropriate type for your application
- Redirect URIs:
http://localhost:3000/api/auth/callback/polar(development) orhttps://yourdomain.com/api/auth/callback/polar(production) - Scopes: openid, profile, email (defaults)
- Homepage URL: Your application's main URL
4. Optionally add: Logo, Terms of Service URL, Privacy Policy URL
サーバー設定
import { betterAuth } from "better-auth"
export const auth = betterAuth({
socialProviders: {
polar: {
clientId: process.env.POLAR_CLIENT_ID as string,
clientSecret: process.env.POLAR_CLIENT_SECRET as string,
},
},
})クライアントサインイン
import { createAuthClient } from "better-auth/client"
const authClient = createAuthClient()
const signIn = async () => {
const data = await authClient.signIn.social({
provider: "polar"
})
}リダイレクト URL
- Development:
http://localhost:3000/api/auth/callback/polar - Production:
https://yourdomain.com/api/auth/callback/polar
プロバイダー固有の設定・注意点
- Update redirect URIs if changing the base path of auth routes
- Keep Client Secret secure (never expose in client code)
- Default scopes include openid, profile, and email permissions
Social Providers 共通設定
基本パターン
サーバー側共通コード
import { betterAuth } from "better-auth";
export const auth = betterAuth({
socialProviders: {
providerName: {
clientId: process.env.PROVIDER_CLIENT_ID as string,
clientSecret: process.env.PROVIDER_CLIENT_SECRET as string,
},
},
});クライアント側共通コード
import { createAuthClient } from "better-auth/client";
const authClient = createAuthClient();
const signIn = async () => {
const data = await authClient.signIn.social({
provider: "providerName",
});
};共通オプション
全プロバイダーで使える設定:
| Option | Type | Description |
|---|---|---|
clientId | string | OAuth アプリケーションの Client ID |
clientSecret | string | OAuth アプリケーションの Client Secret |
scope | string[] | 追加のスコープを指定 |
redirectURI | string | コールバック URL をオーバーライド |
disableDefaultScope | boolean | デフォルトスコープを無効にする |
mapProfileToUser | function | プロバイダーのプロフィールをユーザーオブジェクトにマッピング |
getUserInfo | function | カスタムユーザー情報取得 |
verifyIdToken | function | カスタム ID トークン検証 |
コールバック URL パターン
全プロバイダー共通:
/api/auth/callback/{provider}- ローカル開発:
http://localhost:3000/api/auth/callback/{provider} - 本番環境:
https://yourdomain.com/api/auth/callback/{provider} - auth ルートのベースパスを変更した場合は、リダイレクト URL も合わせて更新する
アカウントリンク
追加スコープのリクエストやアカウントリンクには linkSocial を使用:
const requestAdditionalAccess = async () => {
await authClient.linkSocial({
provider: "providerName",
scopes: ["additional-scope"],
});
};Better Auth version 1.2.7 以降では、追加スコープリクエスト時の "Social account already linked" エラーが防止される。
アクセストークン取得
認証後、アクセストークンはサーバー側に安全に保存される。サーバー側からプロバイダー API へのリクエストに使用可能。
ID Token サインイン
一部のプロバイダーでは、リダイレクトなしで ID トークンを使用したサインインが可能:
const data = await authClient.signIn.social({
provider: "providerName",
idToken: {
token: "ID_TOKEN",
accessToken: "ACCESS_TOKEN",
},
});対応プロバイダー: Google, Apple, Facebook, LINE など。
Railway
Credentials
RAILWAY_CLIENT_IDRAILWAY_CLIENT_SECRET
Getting Credentials
Navigate to Railway Developer Settings and: 1. Create a new OAuth App 2. Select "Web Application" as the type 3. Set redirect URL to http://localhost:3000/api/auth/callback/railway (development) or your production domain
サーバー設定
import { betterAuth } from "better-auth"
export const auth = betterAuth({
socialProviders: {
railway: {
clientId: process.env.RAILWAY_CLIENT_ID as string,
clientSecret: process.env.RAILWAY_CLIENT_SECRET as string,
},
},
})クライアントサインイン
import { createAuthClient } from "better-auth/client"
const authClient = createAuthClient()
const signIn = async () => {
const data = await authClient.signIn.social({
provider: "railway"
})
}リダイレクト URL
- Development:
http://localhost:3000/api/auth/callback/railway - Production: Your application's domain
プロバイダー固有の設定・注意点
Available Scopes
| Scope | Purpose |
|---|---|
openid | Required (default) |
email | User email access (default) |
profile | User name/picture (default) |
offline_access | Refresh tokens |
workspace:viewer | Workspace read access |
workspace:member | Workspace member access |
workspace:admin | Workspace admin access |
project:viewer | Project read access |
project:member | Project member access |
Scope Configuration
railway: {
clientId: process.env.RAILWAY_CLIENT_ID as string,
clientSecret: process.env.RAILWAY_CLIENT_SECRET as string,
scope: ["workspace:viewer", "project:viewer"],
}Special Requirements
For offline_access scope, include prompt: "consent":
railway: {
clientId: process.env.RAILWAY_CLIENT_ID as string,
clientSecret: process.env.RAILWAY_CLIENT_SECRET as string,
scope: ["offline_access"],
prompt: "consent",
}Security Notes
- Railway implements PKCE, which Better Auth handles automatically
- Update redirect URL if you modify auth base path
- Store credentials securely in environment variables
Credentials
REDDIT_CLIENT_ID- Available under the app name in Reddit Developer PortalREDDIT_CLIENT_SECRET- Generated when creating the app
Getting Credentials
1. Navigate to the Reddit Developer Portal 2. Select "Create App" or "Create Another App" 3. Choose "web app" as the application type 4. Set redirect URL to http://localhost:3000/api/auth/callback/reddit (local development) or your production domain (e.g., https://example.com/api/auth/callback/reddit) 5. Retrieve the client ID (displayed below app name) and client secret
サーバー設定
import { betterAuth } from "better-auth"
export const auth = betterAuth({
socialProviders: {
reddit: {
clientId: process.env.REDDIT_CLIENT_ID as string,
clientSecret: process.env.REDDIT_CLIENT_SECRET as string,
},
},
})クライアントサインイン
import { createAuthClient } from "better-auth/client"
const authClient = createAuthClient()
const signIn = async () => {
const data = await authClient.signIn.social({
provider: "reddit"
})
}リダイレクト URL
- Local development:
http://localhost:3000/api/auth/callback/reddit - Production:
https://example.com/api/auth/callback/reddit - If you change the base path of the auth routes, make sure to update the redirect URL accordingly
プロバイダー固有の設定・注意点
Optional Configuration: Scopes and Duration
export const auth = betterAuth({
socialProviders: {
reddit: {
clientId: process.env.REDDIT_CLIENT_ID as string,
clientSecret: process.env.REDDIT_CLIENT_SECRET as string,
duration: "permanent",
scope: ["read", "submit"]
},
},
})Available Scopes
identity: Access basic account informationread: Access posts and commentssubmit: Submit posts and commentssubscribe: Manage subreddit subscriptionshistory: Access voting history
For comprehensive scope options, consult the Reddit OAuth2 documentation.
Roblox
Credentials
ROBLOX_CLIENT_IDROBLOX_CLIENT_SECRET
Obtain from Roblox Creator Hub.
サーバー設定
import { betterAuth } from "better-auth"
export const auth = betterAuth({
socialProviders: {
roblox: {
clientId: process.env.ROBLOX_CLIENT_ID as string,
clientSecret: process.env.ROBLOX_CLIENT_SECRET as string,
},
},
})クライアントサインイン
import { createAuthClient } from "better-auth/client"
const authClient = createAuthClient()
const signIn = async () => {
const data = await authClient.signIn.social({
provider: "roblox"
})
}リダイレクト URL
- Development:
http://localhost:3000/api/auth/callback/roblox - Production: Update to your application's domain
- Adjust if you've customized the auth route base path
プロバイダー固有の設定・注意点
- Email Limitation: The Roblox API does not provide email addresses. As a workaround, the user's
emailfield uses thepreferred_usernamevalue instead. This means the email field will contain the user's Roblox username rather than an actual email address.
Salesforce
Credentials
SALESFORCE_CLIENT_ID(labeled as "Consumer Key" in Salesforce)SALESFORCE_CLIENT_SECRET(labeled as "Consumer Secret" in Salesforce)
Obtain from your Salesforce Connected App.
Environment Variables
Add to .env.local (development) or .env (production):
SALESFORCE_CLIENT_ID=your_consumer_key_here
SALESFORCE_CLIENT_SECRET=your_consumer_secret_here
BETTER_AUTH_URL=http://localhost:3000サーバー設定
import { betterAuth } from "better-auth"
export const auth = betterAuth({
socialProviders: {
salesforce: {
clientId: process.env.SALESFORCE_CLIENT_ID as string,
clientSecret: process.env.SALESFORCE_CLIENT_SECRET as string,
environment: "production",
},
},
})クライアントサインイン
import { createAuthClient } from "better-auth/client"
const authClient = createAuthClient()
const signIn = async () => {
const data = await authClient.signIn.social({
provider: "salesforce"
})
}リダイレクト URL
Callback URL must match exactly between Salesforce and Better Auth configuration.
プロバイダー固有の設定・注意点
Configuration Options
environment: Select"production"(default) or"sandbox"for testingloginUrl: Custom My Domain URL withouthttps://prefixredirectURI: Override auto-generated callback URI if needed
Key Notes
- PKCE is required and automatically handled by the provider
- Default scopes:
openid,email,profile, andid - Use HTTPS for production; HTTP acceptable for local development
Slack
Credentials
SLACK_CLIENT_IDSLACK_CLIENT_SECRET
Setup Instructions
Follow these steps at Your Apps on Slack API:
1. Create a new app by selecting "From scratch" 2. Name your app and choose a development workspace 3. Navigate to "OAuth & Permissions" 4. Register your redirect URLs:
- Development:
http://localhost:3000/api/auth/callback/slack - Production:
https://yourdomain.com/api/auth/callback/slack
5. Retrieve Client ID and Client Secret from "Basic Information"
Production environments require HTTPS. Use ngrok for local HTTPS tunneling.
サーバー設定
import { betterAuth } from "better-auth"
export const auth = betterAuth({
socialProviders: {
slack: {
clientId: process.env.SLACK_CLIENT_ID as string,
clientSecret: process.env.SLACK_CLIENT_SECRET as string,
},
},
})クライアントサインイン
Basic Sign-In
import { createAuthClient } from "better-auth/client";
const authClient = createAuthClient();
const signIn = async () => {
const data = await authClient.signIn.social({ provider: "slack" });
};Request Additional Scopes
By default, Slack uses OpenID Connect scopes: openid, profile, email. Request extra permissions:
const signInWithSlack = async () => {
await authClient.signIn.social({
provider: "slack",
scopes: ["channels:read", "chat:write"],
});
};リダイレクト URL
- Development:
http://localhost:3000/api/auth/callback/slack - Production:
https://yourdomain.com/api/auth/callback/slack
プロバイダー固有の設定・注意点
Workspace-Specific Sign-In
Restrict authentication to a single Slack workspace:
socialProviders: {
slack: {
clientId: process.env.SLACK_CLIENT_ID as string,
clientSecret: process.env.SLACK_CLIENT_SECRET as string,
team: "T1234567890",
},
}Post-Authentication
After successful sign-in, access user information through the session. The access token is stored securely on the server for making subsequent API requests to Slack endpoints. Request appropriate scopes if accessing additional Slack APIs beyond basic profile data.
Spotify
Credentials
SPOTIFY_CLIENT_IDSPOTIFY_CLIENT_SECRET
Obtain from the Spotify Developer Portal.
Environment Configuration
Set your base URL in .env:
BETTER_AUTH_URL=http://127.0.0.1:3000Important Note: Spotify no longer supports localhost as a redirect URI. You must use 127.0.0.1 for local development.
Set the redirect URL in Spotify Dashboard to: http://127.0.0.1:3000/api/auth/callback/spotify
サーバー設定
import { betterAuth } from "better-auth"
export const auth = betterAuth({
socialProviders: {
spotify: {
clientId: process.env.SPOTIFY_CLIENT_ID as string,
clientSecret: process.env.SPOTIFY_CLIENT_SECRET as string,
},
},
})クライアントサインイン
import { createAuthClient } from "better-auth/client"
const authClient = createAuthClient()
const signIn = async () => {
const data = await authClient.signIn.social({
provider: "spotify"
})
}リダイレクト URL
- Development:
http://127.0.0.1:3000/api/auth/callback/spotify(NOTlocalhost) - Production: Use HTTPS redirect URLs matching your application domain
プロバイダー固有の設定・注意点
- Spotify no longer supports
localhostas a redirect URI; use127.0.0.1instead - Ensure browser access uses matching loopback IP (not
localhost:3000) - Update redirect URLs if changing auth route base paths
TikTok
Credentials
TIKTOK_CLIENT_KEY- OAuth application identifierTIKTOK_CLIENT_SECRET- OAuth application secret
Obtain from the TikTok Developer Portal by creating an application.
サーバー設定
import { betterAuth } from "better-auth"
export const auth = betterAuth({
socialProviders: {
tiktok: {
clientSecret: process.env.TIKTOK_CLIENT_SECRET as string,
clientKey: process.env.TIKTOK_CLIENT_KEY as string,
},
},
})クライアントサインイン
import { createAuthClient } from "better-auth/client"
const authClient = createAuthClient()
const signIn = async () => {
const data = await authClient.signIn.social({
provider: "tiktok"
})
}リダイレクト URL
Must be HTTPS and configured in developer settings. Update if auth route base paths change.
プロバイダー固有の設定・注意点
- HTTPS Requirement: The TikTok API does not work with localhost. Use public domains or tools like NGROK for local testing
- Sandbox Mode: Required for testing -- enable via TikTok Developer Portal
- Default Scope:
user.info.profile(required because TikTok doesn't provide emails; username serves as the email field) - Production: Requires TikTok approval for requested scopes
Twitch
Credentials
TWITCH_CLIENT_IDTWITCH_CLIENT_SECRET
Obtain from the Twitch Developer Portal.
サーバー設定
import { betterAuth } from "better-auth"
export const auth = betterAuth({
socialProviders: {
twitch: {
clientId: process.env.TWITCH_CLIENT_ID as string,
clientSecret: process.env.TWITCH_CLIENT_SECRET as string,
},
},
})クライアントサインイン
import { createAuthClient } from "better-auth/client"
const authClient = createAuthClient()
const signIn = async () => {
const data = await authClient.signIn.social({
provider: "twitch"
})
}リダイレクト URL
- Local Development:
http://localhost:3000/api/auth/callback/twitch - Production: Use your application's production URL
- Update the redirect URL if you change your auth routes' base path
プロバイダー固有の設定・注意点
- Email Requirement: Twitch users who do not have an email address will not be able to sign in. Ensure your implementation handles this limitation by requiring verified email addresses during the authentication flow.
Twitter (X)
Credentials
TWITTER_CLIENT_IDTWITTER_CLIENT_SECRET
Obtain from the Twitter Developer Portal.
サーバー設定
import { betterAuth } from "better-auth"
export const auth = betterAuth({
socialProviders: {
twitter: {
clientId: process.env.TWITTER_CLIENT_ID as string,
clientSecret: process.env.TWITTER_CLIENT_SECRET as string,
},
},
})クライアントサインイン
import { createAuthClient } from "better-auth/client"
const authClient = createAuthClient()
const signIn = async () => {
const data = await authClient.signIn.social({
provider: "twitter"
})
}リダイレクト URL
- Local development:
http://localhost:3000/api/auth/callback/twitter - Production: Update to your production domain URL
- Adjust the redirect URL if you modify the base path of auth routes
プロバイダー固有の設定・注意点
- Email Scope: Twitter API v2 now supports email address retrieval. Ensure the
user.emailscope is requested when configuring your Twitter application to enable email functionality during authentication.
Vercel
Credentials
VERCEL_CLIENT_IDVERCEL_CLIENT_SECRET
Obtain by creating a Vercel App in the Vercel Dashboard.
サーバー設定
import { betterAuth } from "better-auth"
export const auth = betterAuth({
socialProviders: {
vercel: {
clientId: process.env.VERCEL_CLIENT_ID as string,
clientSecret: process.env.VERCEL_CLIENT_SECRET as string,
},
},
})クライアントサインイン
import { createAuthClient } from "better-auth/client"
const authClient = createAuthClient()
const signIn = async () => {
const data = await authClient.signIn.social({
provider: "vercel"
})
}リダイレクト URL
- Local Development:
http://localhost:3000/api/auth/callback/vercel - Production: Set to your application's URL
- Adjust the redirect path if you've customized your auth route base path
プロバイダー固有の設定・注意点
Available Scopes
Vercel supports these OpenID Connect scopes:
openid(default)emailprofileoffline_access
Scopes are configured at the Vercel App level. Optional scope parameter can request a subset:
vercel: {
clientId: process.env.VERCEL_CLIENT_ID as string,
clientSecret: process.env.VERCEL_CLIENT_SECRET as string,
scope: ["openid", "email", "profile"],
}Security Note
Vercel requires PKCE (Proof Key for Code Exchange) for enhanced security -- this is automatically handled by Better Auth.
VK (VK ID Provider)
Credentials
VK_CLIENT_IDVK_CLIENT_SECRET
Obtain from the VK ID Developer Portal.
サーバー設定
import { betterAuth } from "better-auth";
export const auth = betterAuth({
socialProviders: {
vk: {
clientId: process.env.VK_CLIENT_ID as string,
clientSecret: process.env.VK_CLIENT_SECRET as string,
},
},
});クライアントサインイン
import { createAuthClient } from "better-auth/client";
const authClient = createAuthClient();
const signIn = async () => {
const data = await authClient.signIn.social({
provider: "vk",
});
};リダイレクト URL
- Local Development:
http://localhost:3000/api/auth/callback/vk - Production: Update to your application's URL
- If you modify the base path of auth routes, adjust the redirect URL accordingly
プロバイダー固有の設定・注意点
- The
signIn.socialfunction initiates the authentication flow with the VK provider - Provider value must be set to
"vk" - Environment variables should be securely stored and loaded from your configuration system
Credentials
WECHAT_CLIENT_ID(App ID)WECHAT_CLIENT_SECRET
Obtain by registering a website application on the WeChat Open Platform. You must also set the authorization callback domain to your Better Auth domain.
サーバー設定
import { betterAuth } from "better-auth"
export const auth = betterAuth({
socialProviders: {
wechat: {
clientId: process.env.WECHAT_CLIENT_ID,
clientSecret: process.env.WECHAT_CLIENT_SECRET,
},
},
})Optional Configuration
export const auth = betterAuth({
socialProviders: {
wechat: {
clientId: process.env.WECHAT_CLIENT_ID,
clientSecret: process.env.WECHAT_CLIENT_SECRET,
lang: "cn", // or "en" for English UI
scope: [], // "snsapi_login" for web QR code login
},
},
})クライアントサインイン
import { createAuthClient } from "better-auth/client"
const authClient = createAuthClient()
const signIn = async () => {
const data = await authClient.signIn.social({
provider: "wechat"
})
}リダイレクト URL
The redirect URL domain must match the domain configured in the WeChat Open Platform.
プロバイダー固有の設定・注意点
- Platform Type: Website Application
- Capability: Enables WeChat QR code login for web applications
- Critical Requirement: The redirect URL domain must match the domain configured in the WeChat Open Platform
- Language Support: Customize the login UI language via the
langparameter ("cn"or"en") - Scope Options: Use
snsapi_loginscope specifically for web QR code login flows
Zoom
Credentials
ZOOM_CLIENT_IDZOOM_CLIENT_SECRET
Setup Instructions
1. Visit Zoom Marketplace 2. Hover on the Develop button and select Build App 3. Select General App and click Create 4. Under "Select how the app is managed," choose User-managed 5. Under "App Credentials," copy your Client ID and Client Secret
サーバー設定
import { betterAuth } from "better-auth"
export const auth = betterAuth({
socialProviders: {
zoom: {
clientId: process.env.ZOOM_CLIENT_ID as string,
clientSecret: process.env.ZOOM_CLIENT_SECRET as string,
},
},
})クライアントサインイン
import { createAuthClient } from "better-auth/client"
const authClient = createAuthClient()
const signIn = async () => {
const data = await authClient.signIn.social({
provider: "zoom"
})
}リダイレクト URL
Set your OAuth Redirect URL in the Zoom app settings under "OAuth Information" > "OAuth Redirect URL":
- Development:
http://localhost:3000/api/auth/callback/zoom - Production: Update to your application's actual URL
- Adjust the path if you've customized your auth route base path
プロバイダー固有の設定・注意点
Required Scopes
The minimum required scope is:
- `user:read:user` (View a user)
Add any additional scopes your application needs through the Zoom app dashboard.
API
Better Auth はサーバー側 API アクセスを、auth インスタンスに公開される api オブジェクトを通じて提供する。HTTP リクエストではなく通常の関数呼び出しとして認証エンドポイントと直接やり取りできる。
概要
auth インスタンスを作成すると、api オブジェクトが提供される。このオブジェクトは Better Auth インスタンスに存在するすべてのエンドポイントを公開する。API は better-call を活用し、REST エンドポイントを標準関数として呼び出せる軽量ウェブフレームワーク。
API パラメーター
| Parameter | Type | Required | Description |
|---|---|---|---|
body | Object | No | リクエストボディデータ |
headers | Headers | Conditional | HTTP ヘッダー(認証トークン、IP 情報) |
query | Object | No | URL クエリパラメーター |
returnHeaders | boolean | No | レスポンスヘッダーを戻り値に含める |
asResponse | boolean | No | 生の Response オブジェクトを返す |
コード例
セッション取得
import { auth } from "@/lib/auth";
await auth.api.getSession({
headers: await headers(),
});Body パラメーター付き
await auth.api.signInEmail({
body: {
email: "john@doe.com",
password: "password",
},
headers: await headers(),
});Query パラメーター付き
await auth.api.verifyEmail({
query: {
token: "my_token",
},
});メールサインアップ
await auth.api.signUpEmail({
returnHeaders: true,
body: {
email: "john@doe.com",
password: "password",
name: "John Doe",
},
});レスポンスヘッダーの取得
const { headers, response } = await auth.api.signUpEmail({
returnHeaders: true,
body: {
email: "john@doe.com",
password: "password",
name: "John Doe",
},
});
const cookies = headers.getSetCookie();
const customHeader = headers.get("x-custom-header");Response オブジェクトの取得
const response = await auth.api.signInEmail({
body: {
email: "",
password: "",
},
asResponse: true,
});エラーハンドリング
型定義
import { APIError, isAPIError } from "better-auth/api";実装
try {
await auth.api.signInEmail({
body: {
email: "",
password: "",
},
});
} catch (error) {
if (isAPIError(error)) {
console.log(error.message, error.status);
}
}注意点
- クライアント側の呼び出しとは異なり、サーバー実装はプレーンな JavaScript オブジェクトを直接受け取る
returnHeadersオプションは Cookie 抽出用の標準Headersオブジェクトを取得するasResponseオプションは完全な HTTP レスポンスメタデータが必要な場合に使用- コア機能やプラグインで定義されたすべてのエンドポイントが自動的に利用可能になる
- エラーインスタンスは
APIErrorを継承し、一貫したエラーハンドリングパターンを提供
セキュリティ考慮事項
- 一部のエンドポイントはヘッダーが必要(セッショントークン、IP 検出のため)
- ヘッダーはユーザーセッショントークンや IP アドレス情報など、レート制限や不正検知に必要なメタデータを提供
- レスポンスヘッダーには認証 Cookie が含まれ、セキュリティ上の慎重な取り扱いが必要
- すべてのサーバー側呼び出しは信頼されたコードとして実行される
CLI
Better Auth には、データベーススキーマの管理、プロジェクトの初期化、シークレットキーの生成、認証セットアップの診断情報収集のための組み込み CLI が含まれている。
コマンド一覧
| Command | Purpose | Key Flags |
|---|---|---|
generate | DB スキーマの生成 | --output, --config, --yes |
migrate | DB へのスキーマ適用(Kysely アダプターのみ) | --config, --yes |
init | プロジェクトの初期化 | --name, --framework, --plugins, --database, --package-manager |
info | 環境診断情報の表示 | --config, --json |
secret | 秘密鍵の生成 | なし |
コマンド詳細
generate コマンド
npx auth@latest generateオプション:
--output: 生成されたスキーマの保存先を指定。デフォルトは ORM タイプに依存(Prisma:prisma/schema.prisma、Drizzle:schema.ts、Kysely:schema.sql)--config: Better Auth 設定ファイルのパス。デフォルトでは./, ./utils, ./libまたはそれらのsrc/相当を検索--yes: 確認プロンプトをスキップし、直接スキーマを生成
migrate コマンド
npx auth@latest migrateオプション:
--config: Better Auth 設定ファイルのパス--yes: 確認をスキップし、直接スキーマを適用
特別機能: PostgreSQL で設定された search_path を自動検出し、正しいスキーマにテーブルを作成する。
init コマンド
npx auth@latest initオプション:
--name: アプリケーション名(デフォルト:package.jsonのname)--framework: 使用フレームワーク(現在: Next.js のみ)--plugins: インストールするプラグインのカンマ区切りリスト--database: データベース選択(現在: SQLite のみ)--package-manager: npm, pnpm, yarn, または bun(デフォルト: 検出されたマネージャー)
info コマンド
npx auth@latest info出力内容:
- システム詳細(OS、CPU、メモリ、Node.js バージョン)
- パッケージマネージャー情報
- Better Auth バージョンと設定(機密データは自動マスク)
- 検出されたフレームワーク(Next.js, React, Vue など)
- データベースクライアントと ORM(Prisma, Drizzle など)
オプション:
--config: カスタム設定ファイルパス--json: JSON 形式で結果を出力(共有やプログラム処理用)
npx auth@latest info --json > auth-info.jsonsecret コマンド
npx auth@latest secretBetter Auth インスタンス用の暗号化秘密鍵を生成する。
セキュリティ・トラブルシューティング
- データ保護:
infoコマンドでは、シークレット、API キー、データベース URL などの機密データは自動的に[REDACTED]に置換される - モジュール解決エラー: 「Cannot find module X」エラーが発生した場合、設定ファイルのインポートエイリアスを一時的に削除し、相対パスを使用する。CLI 実行後にエイリアスに戻す
- PostgreSQL 非デフォルトスキーマ: migrate コマンドは PostgreSQL のカスタム検索パスを自動的に処理する
Client
Better Auth はフロントエンド認証用のフレームワーク非依存クライアントライブラリを提供する。コアクライアントは React, Vue, Svelte, Solid, バニラ JavaScript をフレームワーク固有のインポートを通じてサポートする。
セットアップ
基本セットアップ
import { createAuthClient } from "better-auth/client";
const authClient = createAuthClient({
baseURL: "http://localhost:3000",
});フレームワーク別インポート
| Framework | Import Path | Usage |
|---|---|---|
| React | better-auth/react | Hooks とクライアントメソッド |
| Vue | better-auth/vue | Composition API サポート |
| Svelte | better-auth/svelte | ストアとリアクティブデータ |
| Solid | better-auth/solid | シグナルとプリミティブ |
| Vanilla JS | better-auth/client | コア機能 |
設定オプション
| Option | Type | Description |
|---|---|---|
baseURL | string | 認証サーバーのベース URL(同一ドメインなら省略可) |
fetchOptions | object | デフォルト fetch 設定 |
disableDefaultFetchPlugins | boolean | ブラウザ固有動作を無効化(React Native/Expo 向け) |
plugins | array | クライアントプラグインで機能拡張 |
認証メソッド
サインイン (Email)
const { data, error } = await authClient.signIn.email({
email: "user@example.com",
password: "password1234",
});サインイン (Social)
await authClient.signIn.social({
provider: "github",
});マジックリンク
await authClient.signIn.magicLink({
email: "test@email.com",
});Hooks
useSession Hook
React:
import { createAuthClient } from "better-auth/react";
const { useSession } = createAuthClient();
export function User() {
const { data: session, isPending, error, refetch } = useSession();
return (
<div>
{session && <p>Logged in as {session.user.name}</p>}
{error && <p>Error: {error.message}</p>}
</div>
);
}Vue:
const session = authClient.useSession();
// Returns: { data, isPending, error, refetch }Svelte:
const session = authClient.useSession();
// リアクティブ更新付きストアを返すFetch オプション
デフォルト Fetch オプションの設定
const authClient = createAuthClient({
fetchOptions: {
// better-fetch オプション
},
});リクエスト毎の Fetch オプション
await authClient.signIn.email(
{ email: "test@email.com", password: "pass" },
{ onSuccess(ctx) { /* 成功処理 */ } }
);
// または fetchOptions プロパティ内で
await authClient.signIn.email({
email: "test@email.com",
password: "pass",
fetchOptions: { onSuccess(ctx) { /* */ } },
});Hook リレンダー制御
エンドポイント成功時に UI 更新をトリガーすべきでない場合、自動 Hook 更新を無効化:
await authClient.updateUser(
{ name: "New Name" },
{ disableSignal: true }
);
// 必要に応じて手動リフェッチ
const { refetch } = authClient.useSession();
await authClient.updateUser(
{ name: "New Name" },
{ disableSignal: true, onSuccess() { refetch(); } }
);エラーハンドリング
レスポンスオブジェクト構造
const { data, error } = await authClient.signIn.email({
email: "user@email.com",
password: "pass",
});
// error オブジェクトのプロパティ:
// - message: string (ユーザー向けエラーメッセージ)
// - status: number (HTTP ステータスコード)
// - statusText: string (HTTP ステータステキスト)
// - code?: string (翻訳用エラーコード)エラーコードの使用
const authClient = createAuthClient();
const errorMessages = {
USER_ALREADY_EXISTS: {
en: "user already registered",
es: "usuario ya registrado",
},
};
const { error } = await authClient.signUp.email({
email: "user@email.com",
password: "password",
name: "User",
});
if (error?.code && error.code in errorMessages) {
alert(errorMessages[error.code].en);
}Hook でのエラーハンドリング
const { data, error, isPending } = useSession();
if (error) {
// セッションフェッチエラーの処理
}プラグイン
マジックリンクプラグイン例
import { createAuthClient } from "better-auth/client";
import { magicLinkClient } from "better-auth/client/plugins";
const authClient = createAuthClient({
plugins: [magicLinkClient()],
});
// 新しいプラグインメソッドの使用
await authClient.signIn.magicLink({ email: "test@email.com" });注意点
- 非ブラウザ環境: React Native/Expo では
disableDefaultFetchPlugins: trueを設定してデフォルト fetch プラグインを無効化 - ベース URL 設定: カスタムパスを含む完全な URL を明示的に提供(例:
http://localhost:3000/custom-path/auth) - Fetch ライブラリ: Better Auth は「better-fetch」を使用(ネイティブ Fetch API のラッパー)
- シグナル管理: 特定のエンドポイントは atom シグナルをトリガーし、認証状態との UI 同期を維持するために Hook のリレンダーを引き起こす
- フレームワーク非依存のコアとフレームワーク固有のラッパー
- 全フレームワークで一貫したメソッドシグネチャ
- フレームワークごとの組み込みリアクティブデータ管理
Cookies
Better Auth は Cookie を使用してセッショントークン、セッションデータ、OAuth 状態、その他の認証関連情報を保存する。すべての Cookie は auth オプションの secret キーまたは BETTER_AUTH_SECRET 環境変数を使用して暗号的に署名される。バージョン管理されたシークレットでのローテーション時、暗号化された Cookie データは現在のキーを自動的に使用し、以前のキーでも復号可能。
設定オプション
| Option | Purpose | Default | Type |
|---|---|---|---|
cookiePrefix | 全 Cookie 名のプレフィックス | "better-auth" | string |
cookies | カスタム Cookie 名と属性 | 下記デフォルト参照 | object |
crossSubDomainCookies.enabled | サブドメイン間共有の有効化 | false | boolean |
crossSubDomainCookies.domain | Cookie 共有のルートドメイン | — | string |
useSecureCookies | 非本番環境でも Secure フラグを強制 | false | boolean |
デフォルト Cookie
- `session_token`: セッショントークンを保存
- `session_data`: Cookie キャッシュ有効時にセッションデータを保存
- `dont_remember`:
rememberMe無効時のフラグを保存 - `two_factor`: 二要素認証プラグイン使用時(プラグイン依存)
コード例
カスタム Cookie プレフィックスの設定
import { betterAuth } from "better-auth";
export const auth = betterAuth({
advanced: {
cookiePrefix: "my-app",
},
});カスタム Cookie 名と属性
import { betterAuth } from "better-auth";
export const auth = betterAuth({
advanced: {
cookies: {
session_token: {
name: "custom_session_token",
attributes: {
// カスタム Cookie 属性を設定
},
},
},
},
});クロスサブドメイン設定
import { betterAuth } from "better-auth";
export const auth = betterAuth({
advanced: {
crossSubDomainCookies: {
enabled: true,
domain: "app.example.com",
},
},
trustedOrigins: [
"https://example.com",
"https://app1.example.com",
"https://app2.example.com",
],
});Secure Cookie の強制
import { betterAuth } from "better-auth";
export const auth = betterAuth({
advanced: {
useSecureCookies: true,
},
});Safari ITP とクロスドメインソリューション
Safari の Intelligent Tracking Prevention (ITP) はサードパーティ Cookie をブロックする。フロントエンドと API が異なるドメインにある場合、Safari で認証が失敗する可能性がある。
問題シナリオ
Frontend: https://app.domainB.com
API: https://domainA.comcredentials: "include" 付きリクエストで、Safari は domainA.com をサードパーティとして扱い、Set-Cookie ヘッダーが無視され、セッションが失敗する。
ソリューション 1: リバースプロキシ
API 呼び出しをフロントエンドのドメインを通じてルーティング:
Netlify 設定:
[[redirects]]
from = "/api/*"
to = "https://domainA.com/api/:splat"
status = 200
force = trueVercel 設定:
{
"rewrites": [
{
"source": "/api/:path*",
"destination": "https://domainA.com/api/:path*"
}
]
}ソリューション 2: 共有親ドメイン
共通の親ドメイン構造を使用:
https://app.example.com
https://api.example.comクロスサブドメイン Cookie を有効化:
export const auth = betterAuth({
advanced: {
crossSubDomainCookies: {
enabled: true,
domain: "example.com",
},
},
});セキュリティ考慮事項
- HTTP-Only: 本番環境ではすべての Cookie がデフォルトで
httpOnly(JavaScript アクセスを防止) - Secure フラグ: 本番環境では Cookie は自動的に Secure フラグを使用
- ドメイン制限: クロスサブドメイン Cookie は必要な場合のみ有効にし、ドメインは必要最小限のスコープに設定
- 信頼されないサブドメイン: 侵害される可能性のあるサブドメインには注意。信頼されないサービスには別ドメインを検討
- 署名: Cookie は改ざん防止のため暗号的に署名される
- 本番モード: 非本番環境ではセキュリティを強制するために明示的に
useSecureCookies: trueが必要
注意点
- Cookie の命名パターン:
${prefix}.${cookie_name}(例:better-auth.session_token) - プラグインは追加の Cookie を導入する可能性がある(プラグインドキュメントを参照)
- Cookie によるセッションデータキャッシュは明示的な有効化が必要
- バージョン管理されたシークレットはキーローテーション時の Cookie 復号を自動的に管理
Database
Better Auth はユーザー、セッション、アカウント、検証レコードを保存するためにデータベースに接続する。複数のデータベースアダプターをサポートし、ステートレスセッション管理ではデータベースなしでも動作可能。
コアスキーマテーブル
User テーブル
| Field | Type | Description |
|---|---|---|
id | string (pk) | 一意の識別子 |
name | string | ユーザー名 |
email | string | メールアドレス |
emailVerified | boolean | メール検証状態 |
image | string (optional) | プロフィール画像 URL |
createdAt | timestamp | 作成日時 |
updatedAt | timestamp | 更新日時 |
Session テーブル
| Field | Type | Description |
|---|---|---|
id | string (pk) | 一意の識別子 |
userId | string (fk) | 関連ユーザー ID |
token | string | セッショントークン |
expiresAt | timestamp | 有効期限 |
ipAddress | string (optional) | クライアント IP アドレス |
userAgent | string (optional) | ブラウザ/クライアント情報 |
createdAt | timestamp | 作成日時 |
updatedAt | timestamp | 更新日時 |
Account テーブル
| Field | Type | Description |
|---|---|---|
id | string (pk) | 一意の識別子 |
userId | string (fk) | 関連ユーザー ID |
accountId | string | プロバイダー内アカウント ID |
providerId | string | 認証プロバイダー ID |
accessToken | string (optional) | アクセストークン |
refreshToken | string (optional) | リフレッシュトークン |
scope | string | トークンスコープ |
idToken | string (optional) | ID トークン |
password | string (optional) | ハッシュ化パスワード |
createdAt | timestamp | 作成日時 |
updatedAt | timestamp | 更新日時 |
Verification テーブル
| Field | Type | Description |
|---|---|---|
id | string (pk) | 一意の識別子 |
identifier | string | 検証識別子 |
value | string | 検証値 |
expiresAt | timestamp | 有効期限 |
createdAt | timestamp | 作成日時 |
updatedAt | timestamp | 更新日時 |
サポートデータベース/アダプター
- SQLite / D1
- PostgreSQL
- MySQL
- MSSQL
- MongoDB
- Prisma ORM
- Drizzle ORM
- Kysely(ビルトイン)
主要機能
CLI ツール
npx auth@latest migrate # マイグレーション適用
npx auth@latest generate # スキーマ生成セカンダリストレージ
Redis などのキーバリューストアをセッションデータや短命レコードに実装し、プライマリデータベースの負荷を軽減。
カスタムスキーマ
テーブル名、カラム名のカスタマイズ、additionalFields 設定によるユーザー/セッションスキーマの拡張が可能。
ID 生成オプション
3つのアプローチ: データベース管理、カスタム関数、一貫した生成器(UUID または数値シリアル)。
データベースフック
ユーザー、セッション、アカウント操作の before/after ライフサイクルフックで検証やカスタムロジックを実装。
実験的 Joins
パフォーマンス最適化: 単一リクエストで複数クエリを実行(50 以上のエンドポイントで対応)。
コード例
カスタムフィールド
import { betterAuth } from "better-auth";
export const auth = betterAuth({
database: db,
user: {
additionalFields: {
role: {
type: ["user", "admin"],
required: false,
defaultValue: "user",
},
},
},
});注意点
- プログラマティックマイグレーションは Kysely アダプターのみ対応(Prisma/Drizzle は非対応)
- PostgreSQL はスキーマパスを自動検出
- データベースフックで
APIErrorをスローすることで操作を中止可能 - クライアント側でカスタムフィールドの型推論を行うには追加設定が必要
Dynamic Base URL
許可リストベースのアプローチによる動的ベース URL 解決をサポートし、アプリケーションが複数ドメインやプレビューデプロイメント(カスタムドメイン、Vercel プレビュー、ブランチデプロイメントなど)で同時に動作できるようにする。
セットアップ
export const auth = betterAuth({
baseURL: {
allowedHosts: [
"myapp.com",
"www.myapp.com",
"*.vercel.app",
],
},
});リクエスト受信時、Better Auth は x-forwarded-host または host ヘッダーからホストを抽出し、許可リストに対して検証する。
設定オプション
| Property | Type | Default | Description |
|---|---|---|---|
allowedHosts | string[] | Required | 許可するホストパターンのリスト(ワイルドカード対応) |
fallback | string | — | マッチしないホスト用の URL(エラーはスローされない) |
protocol | `"http" \ | "https" \ | "auto"` |
ワイルドカードパターン
| Pattern | Matches |
|---|---|
myapp.com | 完全一致ドメインのみ |
*.vercel.app | 任意の Vercel サブドメイン |
preview-*.myapp.com | preview- で始まるサブドメイン |
localhost:* | 任意のポートの localhost |
プロトコルハンドリング
| Value | Behavior |
|---|---|
"https" | 常に HTTPS |
"http" | 常に HTTP |
"auto" | x-forwarded-proto から導出。利用不可の場合は HTTPS がデフォルト |
Cookie Secure フラグ: https → secure、http → insecure、auto/未設定 → NODE_ENV === "production" に依存(advanced.useSecureCookies でオーバーライド可能)。
コード例
フォールバック URL
export const auth = betterAuth({
baseURL: {
allowedHosts: ["myapp.com", "*.vercel.app"],
fallback: "https://myapp.com",
},
});環境ベースのプロトコル
export const auth = betterAuth({
baseURL: {
allowedHosts: ["localhost:3000", "myapp.com", "*.vercel.app"],
protocol: process.env.NODE_ENV === "development" ? "http" : "https",
},
});動的ホストでのクロスサブドメイン Cookie
export const auth = betterAuth({
baseURL: {
allowedHosts: ["auth.example1.com", "auth.example2.com"],
protocol: "https",
},
advanced: {
crossSubDomainCookies: {
enabled: true,
// domain: ".example.com", // オプション: 静的ドメインを強制
},
},
});後方互換性(静的文字列)
export const auth = betterAuth({
baseURL: "https://myapp.com",
});一般的な実装パターン
Vercel デプロイメント:
allowedHosts: ["myapp.com", "www.myapp.com", "*.vercel.app"]開発 + 本番:
allowedHosts: ["localhost:3000", "localhost:5173", "myapp.com", "*.vercel.app"],
protocol: process.env.NODE_ENV === "development" ? "http" : "https"複数本番ドメイン:
allowedHosts: ["myapp.com", "myapp.co.uk", "myapp.eu"]注意点
allowedHostsは自動的にパターンをtrustedOriginsに追加し、重複を排除allowedHostsの設定は必須 — Better Auth は自動プラットフォーム検出を行わないfallbackは未認識ホストを静かにマスクし、設定ミスを隠蔽する可能性があるため慎重に使用
セキュリティ考慮事項
- 必須の許可リスト: すべてのホストパターンは明示的に宣言する必要がある(自動検出なし)
- ヘッダーのサニタイゼーション:
x-forwarded-hostとhostヘッダーは処理前にサニタイズされる - 明示的ワイルドカードのみ: ワイルドカードはサポートされるが、意図的な設定が必要
- 未知ホストでのエラー:
fallbackが設定されていない限り、未知のホストはエラーをスローする(可視性のためにこちらが推奨)
Email は認証方法に関係なく Better Auth のすべてのユーザーに必須のフィールド。フレームワークはメール検証、パスワードリセット、トークンベースのワークフローを提供する。
メール検証
トークンベースのメール検証(OTP ベースは Email OTP プラグインで利用可能)。sendVerificationEmail 関数の実装が必要。
設定オプション
| Option | Type | Default | Description |
|---|---|---|---|
sendVerificationEmail | function | — | 検証メール送信ハンドラー(必須) |
sendOnSignUp | boolean | false | 登録時に自動的に検証メールを送信 |
sendOnSignIn | boolean | false | 未検証の場合、サインイン時に検証メールを再送 |
autoSignInAfterVerification | boolean | false | メール確認直後にセッションを作成 |
afterEmailVerification | async function | — | 検証成功後に実行されるコールバック |
emailAndPassword.requireEmailVerification を true に設定すると、未検証ログインをブロック(HTTP 403 を返す)。
コード例
サーバー — 基本検証セットアップ
export const auth = betterAuth({
emailVerification: {
sendVerificationEmail: async ({ user, url, token }, request) => {
// `url` は構築済みの検証リンク
// `token` はカスタム検証 URL 構築用
void sendEmail({
to: user.email,
subject: "Verify your email",
text: `Click to verify: ${url}`,
});
// await しないこと — タイミング攻撃を回避
},
sendOnSignUp: true,
},
});サーバー — ログインに検証を必須化
export const auth = betterAuth({
emailAndPassword: {
enabled: true,
requireEmailVerification: true,
},
emailVerification: {
sendVerificationEmail: async ({ user, url }) => {
void sendEmail({ to: user.email, text: url });
},
sendOnSignIn: true,
},
});サーバー — 自動サインイン + 検証後コールバック
export const auth = betterAuth({
emailVerification: {
sendVerificationEmail: async ({ user, url }) => {
void sendEmail({ to: user.email, text: url });
},
autoSignInAfterVerification: true,
async afterEmailVerification(user, request) {
console.log(`${user.email} verified successfully`);
},
},
});クライアント — 未検証ログインの 403 処理
authClient.signIn.email(
{ email: "user@example.com", password: "password" },
{
onError: (ctx) => {
if (ctx.error.status === 403) {
alert("Please verify your email address");
}
},
}
);クライアント — 手動検証トリガー
await authClient.sendVerificationEmail({
email: "user@email.com",
callbackURL: "/",
});クライアント — カスタムトークン検証
await authClient.verifyEmail({
query: { token: "verification_token_value" },
});パスワードリセットメール
// サーバー設定
export const auth = betterAuth({
emailAndPassword: {
enabled: true,
sendResetPassword: async ({ user, url, token }, request) => {
void sendEmail({
to: user.email,
subject: "Reset your password",
text: `Reset link: ${url}`,
});
},
},
});機能サマリー
| Feature | Trigger | Use Case |
|---|---|---|
| 自動検証 | sendOnSignUp: true | 新規ユーザー登録フロー |
| 必須検証 | requireEmailVerification: true | 高セキュリティアプリケーション |
| 手動トリガー | sendVerificationEmail() | ユーザー主導の再検証 |
| 自動サインイン | autoSignInAfterVerification: true | シームレスなオンボーディング |
| カスタムコールバック | afterEmailVerification | 検証後ワークフロー |
注意点
- タイミング攻撃防止: 検証/リセットハンドラー内でメール送信を await しないこと
- サーバーレスプラットフォーム: Vercel (
waitUntil)、Firebase (onFinish) 等のプラットフォーム固有メカニズムを使用し、レスポンスをブロックせずに配信を確保 requireEmailVerificationが有効で SSO ユーザーのメールが未検証の場合、検証メールは送信されるが SSO ログインはブロックされない
セキュリティ考慮事項
- タイミング攻撃: 検証/リセットハンドラーでメール送信を await しない
- トークンセキュリティ: トークンは暗号的に生成される。ログに生トークンを露出させないこと
- メール所有確認: 検証はフォーマットの妥当性ではなく、アドレスの実際の管理権を確認する
Hooks
Hooks は認証ライフサイクルの特定のポイント(エンドポイント実行の前後)でインターセプトし、カスタムロジックを実行する。別のエンドポイントを構築する必要がない。
Hook タイプ
| Type | Timing | Use Cases |
|---|---|---|
before | エンドポイント処理の前 | リクエストの変更、事前検証、カスタムレスポンスでの早期リターン |
after | エンドポイント完了後 | レスポンスの変更、副作用のトリガー(通知、分析) |
セットアップ
export const auth = betterAuth({
hooks: {
before: createAuthMiddleware(async (ctx) => { /* ... */ }),
after: createAuthMiddleware(async (ctx) => { /* ... */ }),
},
});Context (ctx) オブジェクト
| Property | Description |
|---|---|
ctx.path | 現在のエンドポイントパス(例: /sign-up/email) |
ctx.body | パース済み POST リクエストボディ |
ctx.headers | リクエストヘッダー |
ctx.request | Request オブジェクト(サーバーのみモードでは存在しない場合あり) |
ctx.query | クエリパラメーター |
ctx.context | 認証関連コンテキスト(下記テーブル参照) |
ctx.context プロパティ
| Property | Description |
|---|---|
newSession | 新しく作成されたセッション — after hooks でのみ利用可能 |
returned | 前の Hook の戻り値 |
responseHeaders | 前の Hook からのヘッダー |
authCookies | BetterAuth Cookie 設定 |
secret | Auth インスタンスのシークレットキー |
password | パスワードユーティリティ: hash, verify |
adapter | ORM ライクなデータベースアダプターメソッド |
internalAdapter | 内部 DB 操作メソッド(例: createSession()) |
generateId | ID 生成ユーティリティ |
コード例
Before Hook — メールドメイン制限
export const auth = betterAuth({
hooks: {
before: createAuthMiddleware(async (ctx) => {
if (ctx.path !== "/sign-up/email") return;
if (!ctx.body?.email.endsWith("@example.com")) {
throw new APIError("BAD_REQUEST", {
message: "Email must end with @example.com",
});
}
}),
},
});After Hook — 登録時の通知
export const auth = betterAuth({
hooks: {
after: createAuthMiddleware(async (ctx) => {
if (ctx.path.startsWith("/sign-up")) {
const newSession = ctx.context.newSession;
if (newSession) {
sendMessage({
type: "user-register",
name: newSession.user.name,
});
}
}
}),
},
});レスポンスユーティリティ
JSON レスポンス
return ctx.json({ message: "Hello World" });リダイレクト
throw ctx.redirect("/sign-up/name");Cookie
// プレーン Cookie
ctx.setCookies("my-cookie", "value");
const cookie = ctx.getCookies("my-cookie");
// 署名付き Cookie
await ctx.setSignedCookie("my-signed-cookie", "value", ctx.context.secret, {
maxAge: 1000,
});
const signedCookie = await ctx.getSignedCookie("my-signed-cookie");エラースロー
throw new APIError("BAD_REQUEST", { message: "Invalid request" });バックグラウンドタスク
// Fire-and-forget
ctx.context.runInBackground(sendAnalyticsEvent(newSession.user.id));
// レスポンス前に完了が必要
await ctx.context.runInBackgroundOrAwait(sendWelcomeEmail(newSession.user));advanced.backgroundTasks でハンドラーを設定する。
注意点
- 認証動作のカスタマイズには、別のエンドポイントを構築するよりも Hooks を使用することを推奨
- 複数エンドポイントで再利用されるロジックには、プラグインの作成を検討
ctx.requestはサーバーのみ(非 HTTP)呼び出しでは存在しない場合があるctx.context.newSessionはafterhooks でのみ設定される
Getting Started
Better Auth のインストールと基本セットアップ。
| トピック | 説明 | パス |
|---|---|---|
| Installation | インストール・環境変数・マイグレーション | installation.md |
| Basic Usage | auth インスタンス作成・ルートハンドラ・クライアント基本 | basic-usage.md |
Guides
Better Auth の実装ガイド。
| ガイド | 説明 | パス |
|---|---|---|
| Your First Plugin | プラグイン作成チュートリアル | your-first-plugin.md |
| Optimizing for Performance | パフォーマンス最適化 | optimizing-for-performance.md |
| Create a DB Adapter | カスタム DB アダプター作成 | create-a-db-adapter.md |
| Browser Extension | ブラウザ拡張機能での使用 | browser-extension.md |