Contents
Auth0 カスタムデータベース接続の概要と利用シーン
Auth0 の カスタムデータベース接続 は、既存のユーザーテーブルをそのまま認証フローに組み込むための仕組みです。標準的な Auth0 データベース接続では対応できない独自スキーマやハッシュ方式を持つシステムでも、最小限の変更でシングルサインオンを実現できます。本節では概念と代表的な活用例を整理し、導入判断に必要なポイントを示します。
基本概念
カスタムデータベース接続は「Auth0 が提供する標準 DB」ではなく、利用者が所有する任意のリレーショナル DB(PostgreSQL・MySQL など)や NoSQL に対して認証ロジックを書き込む仕組みです。実装は Node.js(TypeScript 推奨)のスクリプトとして保存され、Auth0 のサーバーレスコンテナ上で実行されます。
- 関数シグネチャが固定:
login,create,verifyなどのエントリポイントは決まった引数とコールバック形式を取ります。 - サイズ制限:圧縮後最大 2 MB(テキストベース)まで保存可能です。
- 実行環境:Node.js 18 LTS が標準で提供され、
pg,mysql2,bcryptといった一般的な npm パッケージを利用できます。
典型的な活用例
| ユースケース | メリット | 注意点 |
|---|---|---|
| 既存顧客管理システムとの統合 | DB を二重化しないので運用コストが削減でき、データの整合性も保てる | スキーマ変更時はスクリプトのリビジョン管理と再デプロイが必須 |
| マルチテナント SaaS のテナント別認証 | テナント ID に応じて接続先 DB を動的に切り替えられ、テナントごとのカスタムロジックを実装可能 | 同時接続数の上限(Auth0 のレートリミット)に注意し、プール設定で対策する |
| 独自ハッシュ方式のパスワード | bcrypt 以外でも PBKDF2, Argon2, カスタム暗号化ロジックを自由に組み込める | アルゴリズム選定は最新のセキュリティ勧告に従い、脆弱性がないことを検証する |
結論:既存 DB をそのまま活用したい場合や、認証ロジックに高度なカスタマイズが必要なケースで最も有効です。次の章では実際に環境を構築し、スクリプトを書き始める手順を解説します。
事前準備:Auth0 テナント作成とローカル開発環境の構築
本セクションは「実装前に必ずやっておくべきこと」を段階的に示します。無料プランでも必要な権限が取得でき、ローカルで TypeScript 開発をすぐ始められるように設計しています。
Auth0 アカウントとテナントのセットアップ
まずは Auth0 の公式サイトからアカウントを作成し、利用するテナントを用意します。テナントはプロジェクト単位で分離できるため、開発・本番を別々に管理すると安全です。
- https://auth0.com にアクセスし Sign Up → メール認証
- ダッシュボード左上の Create Tenant をクリック
- テナント名(例:
my-company) とリージョン(US, EU など)を選択して作成
テナントができたら、Applications > Applications に新規アプリケーション(シングルページアプリ・ネイティブアプリ等)を登録し、Client ID と Client Secret を控えておきます。これらは後の API 呼び出しやフロントエンド設定で必要になります。
Node.js / TypeScript 開発環境のインストール
カスタムスクリプトは Node.js 上で動作するため、ローカルに同等バージョンを用意します。以下の手順は nvm を利用したインストール例です。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# nvm のインストール(最新版推奨) curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash source ~/.bashrc # Node.js 18.x 系のインストールと利用設定 nvm install 18 nvm use 18 # プロジェクトディレクトリ作成 mkdir auth0-custom-db && cd auth0-custom-db # npm 初期化 & TypeScript 開発環境構築 npm init -y npm i typescript @types/node ts-node pg bcrypt dotenv --save-dev npx tsc --init |
tsconfig.json の主要設定は次の通りです。
|
1 2 3 4 5 6 7 8 9 |
{ "target": "ES2022", "moduleResolution": "node", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true } |
これにより、Auth0 が期待する ECMAScript 構文と型安全性が確保されます。
必要な権限(ロール)と Machine‑to‑Machine アプリの作成
スクリプトから Auth0 Management API を呼び出す場合は Machine‑to‑Machine (M2M) アプリ が必要です。以下の手順で最小限のスコープを付与します。
- ダッシュボード → APIs →
Auth Management APIを選択 - 「Permissions」タブで
read:connections,update:connections,create:client-grantsにチェック - Applications > Machine‑to‑Machine Applications から新規 M2M アプリを作成し、上記スコープを割り当てる
取得した Client ID / Client Secret を .env ファイルに保存しておくと、CI/CD パイプラインでも安全に利用できます。
カスタムデータベーススクリプトの実装パターン
この章では Auth0 が要求する 3 つの主要関数 Login, Create, Verify の実装手順を詳しく解説します。各関数は async (connection, payload, callback) => {} というシグネチャで書き、callback(error, result) に結果またはエラーを渡す必要があります。
Login スクリプトの構成とポイント
Login 関数は 「メール + パスワード」 → DB 検証 → 成功時にユーザー情報返却 のフローを実装します。以下のコードは PostgreSQL を例にしていますが、pg 以外のクライアントでも同様です。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
// src/scripts/login.ts import { Client } from 'pg'; import bcrypt from 'bcrypt'; import dotenv from 'dotenv'; dotenv.config(); export async function login(connection: any, payload: any, callback: Function) { const { username, password } = payload; // Auth0 では `username` がメールアドレスになることが多い const client = new Client({ connectionString: process.env.DATABASE_URL }); try { await client.connect(); // パラメータ化クエリでインジェクション対策 const res = await client.query( `SELECT id, email, password_hash FROM users WHERE email = $1`, [username] ); if (res.rowCount === 0) { return callback(new Error('Invalid credentials')); } const user = res.rows[0]; const match = await bcrypt.compare(password, user.password_hash); if (!match) { return callback(new Error('Invalid credentials')); } // Auth0 が期待するフォーマットで返す callback(null, { user_id: `db|${user.id}`, email: user.email, }); } catch (err) { console.error('Login script error:', err); callback(err); } finally { await client.end(); } } |
実装上の留意点
- エラーメッセージは統一:
Invalid credentialsのみ返すことで情報漏洩を防止。 - 接続プールは使用しない(スクリプトは短時間で完了するため)ので
Clientを直接インスタンス化しています。大規模トラフィックの場合はプール化も検討してください。 - 環境変数のロードは
.envにまとめ、CI 時にはシークレット管理ツールに置き換えると安全です。
Create(ユーザー登録)スクリプトの構成
Create 関数は新規ユーザー情報を受け取り、ハッシュ化したパスワードと共に DB に保存します。重複エラーは Auth0 が認識できる形で返すことが重要です。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
export async function create(connection: any, payload: any, callback: Function) { const { email, password } = payload; const client = new Client({ connectionString: process.env.DATABASE_URL }); try { await client.connect(); // 安全なハッシュ生成(コスト因子 12 が推奨) const hash = await bcrypt.hash(password, 12); const res = await client.query( `INSERT INTO users (email, password_hash) VALUES ($1, $2) RETURNING id`, [email, hash] ); callback(null, { user_id: `db|${res.rows[0].id}`, email, }); } catch (err) { console.error('Create script error:', err); // PostgreSQL のユニーク制約違反コードは 23505 if ((err as any).code === '23505') { return callback(new Error('User already exists')); } callback(err); } finally { await client.end(); } } |
実装上の留意点
- トランザクションは不要:単一 INSERT のみなので自動コミットで問題ありません。
- エラーハンドリングを細分化し、重複時だけ特定メッセージを返すことで UI 側に適切なフィードバックが届きます。
Verify(パスワードリセット)スクリプトの構成
Verify 関数は「新しいパスワード + リセットトークン」の組み合わせを受け取り、ハッシュ化した後に DB を更新します。トークン検証ロジックはアプリ側で別途実装する前提です。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
export async function verify(connection: any, payload: any, callback: Function) { const { password, verification_token } = payload; const client = new Client({ connectionString: process.env.DATABASE_URL }); try { await client.connect(); // トークン検証は別途実装(例:JWT、ワンタイムコードなど) // ここではハッシュ化のみを行う const hash = await bcrypt.hash(password, 12); await client.query( `UPDATE users SET password_hash = $1 WHERE verification_token = $2`, [hash, verification_token] ); callback(null, true); } catch (err) { console.error('Verify script error:', err); callback(err); } finally { await client.end(); } } |
実装上の留意点
- トークン有効期限は DB カラムで管理し、使用済みトークンは即座に削除または無効化します。
- 成功時は
trueを返すだけで構いませんが、必要に応じてユーザー情報を再度返却しても問題ありません。
実装で頻出するテクニック:bcrypt・パラメータ化クエリ・非同期処理
本章では実装時に必ず押さえておきたい 3 つのベストプラクティス をサンプルコードとともに解説します。すべて TypeScript で記述し、Node.js 18 の標準機能を活用しています。
bcrypt による安全なパスワードハッシュ化
bcrypt は計算コストが調整可能なハッシュ関数です。以下のユーティリティはプロジェクト全体で使い回すことを想定しています。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import bcrypt from 'bcrypt'; /** * 平文パスワードからハッシュを生成(cost=12 が推奨) */ export async function hashPassword(plain: string): Promise<string> { return await bcrypt.hash(plain, 12); } /** * ハッシュと平文の照合 */ export async function verifyPassword( plain: string, hashed: string ): Promise<boolean> { return await bcrypt.compare(plain, hashed); } |
ポイント
- コスト因子は 10〜14 が実務的に安全かつパフォーマンスが許容範囲です。
awaitを使うことで例外処理がシンプルになり、try/catchで一元管理できます。
パラメータ化クエリによる SQL インジェクション防止
pg のプレースホルダー $1, $2 … は文字列結合を排除し、データ型変換も自動で行います。
|
1 2 3 4 5 6 7 |
const query = ` SELECT id, email FROM users WHERE email = $1 AND status = $2 `; const values = [email, 'active']; await client.query(query, values); |
ポイント
- 動的テーブル名が必要なケースは ホワイトリストで検証した上でテンプレート文字列に埋め込む ことを徹底してください。
- 可能なら ORM(Prisma, TypeORM) の利用も検討できますが、カスタムスクリプトでは軽量な
pgが推奨されます。
async/await を用いた非同期処理のベストパターン
複数の DB 呼び出しや外部 API コールを順序通りに実行したい場合は、以下のように async 関数内で await します。エラーは一括で捕捉できるため、コードが読みやすくなります。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
export async function getUserWithRoles(userId: string) { const userRes = await client.query( 'SELECT * FROM users WHERE id=$1', [userId] ); if (userRes.rowCount === 0) throw new Error('User not found'); const rolesRes = await client.query( `SELECT r.name FROM roles r JOIN user_roles ur ON r.id=ur.role_id WHERE ur.user_id=$1`, [userId] ); return { ...userRes.rows[0], roles: rolesRes.rows.map(r => r.name), }; } |
ポイント
try/catchで囲むと、DB エラーやネットワーク障害を統一的にハンドリングできます。- 大規模トラフィックでは Promise.all を使って並列化できるケースもありますが、認証フローは順序依存が多いため慎重に選択してください。
Auth0 Dashboard と Management API/CLI での設定・デプロイ手順
この章では GUI(Dashboard)と CLI/Automation の二通りから カスタム DB 接続を作成し、スクリプトをデプロイする方法 を詳述します。両方のフローを併用すれば、開発・検証は手動で行い、本番リリースは CI/CD で自動化できます。
Dashboard からカスタムデータベース接続を作成
Dashboard は 最初の検証や小規模テスト に適しています。以下の手順で接続とスクリプトを登録します。
- 左メニュー → Authentication > Database → 「Create DB Connection」
- 名前(例:
my-custom-db) と「Custom Database」を選択し、Createをクリック - タブ切り替えで Scripts に移動し、
Login,Create User,Verifyの 3 つのスクリプトを貼り付ける(ローカルで作成した TypeScript ファイルは事前に JavaScript にコンパイルしておく) - 「Test Login」ボタンでサンプルユーザー情報を入力し、正しく認証できるか確認
ポイント
- Dashboard はスクリプトサイズ(2 MB)と構文エラーを自動チェックします。
- テスト実行時のログは Logs > System Logs に出力され、
console.errorで記述した内容が確認できます。
auth0-cli を使ったスクリプトのアップロード
CLI は コードベースと連携させて CI/CD パイプラインに組み込む のに最適です。以下は auth0 CLI の基本的な使用例です。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# グローバルインストール(npm 推奨) npm i -g auth0 # ブラウザで認証し、CLI にログイン auth0 login # JSON 定義ファイルを作成(my-custom-db.json 参照) cat > my-custom-db.json <<'EOF' { "name": "my-custom-db", "strategy": "auth0-database-connection", "options": { "customScripts": { "login": "./scripts/login.js", "create": "./scripts/create.js", "verify": "./scripts/verify.js" }, "enabled_clients": ["<YOUR_CLIENT_ID>"] } } EOF # 接続作成コマンド auth0 connections create --json @my-custom-db.json |
ポイント
--jsonオプションで外部ファイルを直接渡すと、スクリプトのパス解決が自動的に行われます。- 既存接続を更新する場合は
auth0 connections update --id <CONN_ID> --json @my-custom-db.jsonを使用します。
Management API を利用した自動デプロイ(CI/CD 向け)
Management API は GitHub Actions、Azure Pipelines、GitLab CI から直接呼び出すことができます。以下はトークン取得からスクリプト更新までのフローです。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# 1. M2M アプリでアクセストークンを取得(クレデンシャルフロー) TOKEN=$(curl -s -X POST https://YOUR_DOMAIN/oauth/token \ -H "Content-Type: application/json" \ -d '{ "client_id": "<M2M_CLIENT_ID>", "client_secret": "<M2M_CLIENT_SECRET>", "audience": "https://YOUR_DOMAIN/api/v2/", "grant_type":"client_credentials" }' | jq -r .access_token) # 2. 接続 ID を取得(リストから対象を検索) CONN_ID=$(curl -s -H "Authorization: Bearer $TOKEN" \ https://YOUR_DOMAIN/api/v2/connections?strategy=auth0-database-connection \ | jq -r '.[] | select(.name=="my-custom-db") | .id') # 3. スクリプトを文字列化して PATCH リクエスト curl -X PATCH "https://YOUR_DOMAIN/api/v2/connections/$CONN_ID" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d "$(jq -n --arg login "$(cat ./scripts/login.js)" \ --arg create "$(cat ./scripts/create.js)" \ --arg verify "$(cat ./scripts/verify.js)" \ '{options:{customScripts:{login:$login,create:$create,verify:$verify}}}')" |
ポイント
jqを使ってスクリプトファイルを JSON 文字列にエスケープしながら送信します。- CI のシークレットストアで M2M クライアント ID/Secret と Auth0 ドメイン を管理すれば、コードベースの変更が自動的に反映されます。
セキュリティベストプラクティスとトラブルシューティング
本節では実運用で必ず守るべき セキュリティ対策 と、開発中・本番環境でよく遭遇するエラーの原因と解決策をまとめます。さらに 2026 年版の Actions/Triggers 移行ガイドも提供します。
SQL インジェクション防止の徹底
- 必ずパラメータ化クエリ を使用し、文字列結合は排除する
- 動的テーブル名やカラム名が必要な場合は ホワイトリストで検証 した上でテンプレート文字列に組み込む
- 入力長・文字種のサーバー側バリデーション(例:メールは
^[^@\\s]+@[^@\\s]+\\.[^@\\s]{2,}$)を実装
ベストプラクティス:SQL インジェクション対策は「コードレベル + データベース側の権限最小化」の二段階防御が最も効果的です。
レートリミットとエラーメッセージの抑制
- 認証失敗時は同一メッセージ(
Invalid credentials)を返し、ユーザーがメールアドレスの有無を推測できないようにします。 - Auth0 の「Failed Logins」アラートと IP ベースのレートリミットを有効化し、ブルートフォース攻撃を防止します。
- スクリプト側で 意図的な遅延(例:
await new Promise(r => setTimeout(r, 300));)を入れると、攻撃者の試行回数が減少します。
よくあるエラーと対処法
| エラーコード / メッセージ | 主な原因 | 推奨対策 |
|---|---|---|
Invalid credentials |
パスワードハッシュ不一致、メール未登録 | すべての認証失敗で同一エラーメッセージを返す |
TimeoutError: Query read timeout |
DB 接続プール枯渇、ネットワーク遅延 | max パラメータ増加、クエリ最適化、接続ヘルスチェック実装 |
Script size limit exceeded (2 MB) |
スクリプトが大きくなりすぎた | ロジックを外部 API(AWS Lambda, Cloud Run 等)へ委譲し、スクリプトは薄く保つ |
403 Forbidden – insufficient_scope |
M2M アプリに必要権限が付与されていない | Management API の read:connections, update:connections スコープを再確認 |
デバッグのヒント:console.error で出力した情報は Auth0 ダッシュボードの Logs > System Logs にリアルタイムで流れます。検索クエリに type:"s"(スクリプト)や client_id:"<YOUR_CLIENT_ID>" を付与すると絞り込みが容易です。
2026 年版 Actions/Triggers への移行ガイド
Auth0 は カスタムデータベース接続の段階的廃止 を発表しており、代替手段として Post‑Login Actions と Credentials Exchange Triggers が推奨されています。以下は既存スクリプトを移行するためのステップです。
- Action プロジェクト作成
bash
auth0 actions create --name my-login-action --runtime node18 - 認証ロジック(login.ts)を npm パッケージ化し、
node_modulesにインストール。例:npm i @my-org/auth-db-login - Action 本体に組み込み
js
// src/index.js (Action entry)
const { verifyUser } = require('@my-org/auth-db-login');
exports.onExecutePostLogin = async (event, api) => {
const { email, password } = event.request.body;
const user = await verifyUser(email, password);
if (!user) {
api.access.deny('Invalid credentials');
return;
}
// 必要に応じてカスタムクレームを付与
api.idToken.setCustomClaim('role', user.role);
};
4. **デプロイ**bash
auth0 actions deploy --name my-login-action --environment production
5. フローの切り替え:Dashboard の Authentication > Flows で Post‑Login Action を有効化し、既存カスタム DB 接続は「非推奨」ステータスになるまで無効化する。
移行のメリット
- サイズ制限が緩和(Actions は最大 5 MB)
- デプロイが Git と連動し、ロールバックや CI/CD が容易
- 環境変数とシークレット管理が統一され、セキュリティポリシーの適用が簡単
既存スクリプトは 外部モジュール化 して Action にインポートすれば、ロジック自体をほぼそのまま再利用できます。段階的に移行し、2026 年以降も安全かつ拡張性の高い認証基盤を維持しましょう。
本稿では Auth0 カスタムデータベース接続の全体像から実装・テスト・デプロイ、セキュリティ対策、そして最新の Actions への移行手順まで網羅的に解説しました。この記事を手引きに、まずは開発環境でスクリプトを作成し、段階的に本番テナントへ導入してください。