authentication について
authentication は、さまざまな Web スタックで認証を計画・実装するスキルです。既存実装の調査からフレームワーク別の設定、セキュリティ確認、テストまでを案内し、特定のライブラリに固定せずプロジェクトに合わせます。
主な機能
- 01既存の認証パターンとスタックの検出
- 02フレームワーク別の認証ファイル計画
- 03セッション、OAuth、ルート保護の案内
- 04シークレットとセキュリティ設定の確認
- 05テストケース生成とチェックリスト実行
活用シーン
- 01ログインと登録フローの追加
- 02ルートと API エンドポイントの保護
- 03OAuth またはセッション管理の導入
- 04認証実装の監査
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ mgd34msu/ goodvibes-plugin --skill authenticationインストール後、Claude Code で/authenticationを入力して読み込まれたか確認します。
同梱ファイルreferences/decision-tree.mdscripts/auth-checklist.sh
詳細
リソース
scripts/
auth-checklist.sh
references/
decision-tree.md
認証実装ワークフロー
このスキルは、調査からテストまで、認証の実装を一貫して進めます。ライブラリ固有のスキル(clerk、nextauth、lucia、auth0、firebase-auth、supabase-auth、passport)を、スタックに合わせて適応する統一ワークフローに置き換えます。
このスキルを使用するタイミング
以下を実装する場合に使用します。
- ユーザーのログインおよびサインアップのフロー
- セッション管理(Cookie、JWT、サーバーサイドセッション)
- OAuth 統合(Google、GitHub など)
- 保護されたルートと API エンドポイント
- ミドルウェアベースの認証
- ロールベースのアクセス制御(RBAC)
- トークンの更新メカニズム
- パスワードリセットのフロー
前提条件
開始する前に、以下を確認してください。
- プロジェクトのフレームワークを把握していること(Next.js、Remix、Express など)
- データベースを使用する認証の場合、データベーススキーマを計画していること
- 認証方式を把握していること(マネージドサービスかセルフホストか)
- 環境変数の設定にアクセスできること
ワークフローの手順
ステップ 1:調査
discover を使用して、既存の認証パターンを把握します。
discover:
queries:
- id: existing-auth
type: grep
pattern: "(useAuth|getSession|withAuth|requireAuth|protect|authenticate)"
glob: "**/*.{ts,tsx,js,jsx}"
- id: middleware-files
type: glob
patterns:
- "**/middleware.{ts,js}"
- "**/auth/**/*.{ts,js}"
- "**/_middleware.{ts,js}"
- id: session-handling
type: grep
pattern: "(session|jwt|token|cookie)"
glob: "**/*.{ts,tsx,js,jsx}"
- id: protected-routes
type: grep
pattern: "(protected|private|requireAuth|withAuth)"
glob: "**/*.{ts,tsx,js,jsx}"
verbosity: files_only
確認する項目:
- 既存の認証フック、ユーティリティ、またはミドルウェア
- セッションの保存方法(Cookie、localStorage、サーバーサイド)
- 保護されたルートのパターン
- 認証プロバイダーの設定(サードパーティを使用している場合)
判断ポイント: 認証がすでに部分的に実装されている場合は、拡張する前に既存のファイルを読んでパターンを把握してください。
ステップ 2:スタックの検出
detect_stack を使用してフレームワークを特定し、認証方式を判断します。
detect_stack:
path: "."
フレームワークの検出:
- Next.js(App Router):middleware.ts + Server Actions + cookies を使用します
- Next.js(Pages Router):getServerSideProps + API routes + NextAuth を使用します
- Remix:loader/action 認証 + セッション Cookie を使用します
- Express/Fastify:ミドルウェア + セッションストアを使用します
- tRPC:コンテキスト + ミドルウェアを使用します
- GraphQL:コンテキスト + ディレクティブを使用します
判断ツリーを参照: references/decision-tree.md を読んで、フレームワークと要件に基づき、マネージド(Clerk、Auth0)、セルフホスト(NextAuth、Lucia)、サーバーレス(Supabase Auth)のいずれかを選択します。
ステップ 3:実装計画
フレームワークと判断ツリーに基づき、作成または変更するファイルを計画します。
必要となる一般的なファイル:
-
ミドルウェア(
middleware.ts、auth.middleware.ts)- リクエストをインターセプトします
- 認証状態を確認します
- 未認証ユーザーをリダイレクトします
-
認証ユーティリティ(
lib/auth.ts、utils/auth.ts)- セッションの作成/検証
- トークンの生成/検証
- パスワードのハッシュ化(bcrypt、argon2)
-
認証 API ルート(
/api/auth/login、/api/auth/signup、/api/auth/logout)- 認証リクエストを処理します
- セッション Cookie を設定します
- 認証状態を返します
-
保護されたルートのラッパー(
withAuth、requireAuth)- HOC またはサーバーユーティリティ
- レンダリング前に認証を確認します
- リダイレクトするか 401 を返します
-
クライアントフック(
useAuth、useSession)- 現在のユーザーにアクセスします
- クライアント側の認証状態を管理します
- ログイン/ログアウトを実行します
フレームワーク固有のパターン:
Next.js App Router:
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const token = request.cookies.get('session')?.value;
if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ['/dashboard/:path*', '/api/:path*']
};
Remix:
// app/utils/session.server.ts
import { createCookieSessionStorage } from '@remix-run/node';
const { getSession, commitSession, destroySession } =
createCookieSessionStorage({
cookie: {
name: '__session',
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
secrets: [process.env.SESSION_SECRET],
sameSite: 'lax'
}
});
export { getSession, commitSession, destroySession };
Express:
// middleware/auth.ts
import jwt from 'jsonwebtoken';
import type { Request, Response, NextFunction } from 'express';
export function requireAuth(req: Request, res: Response, next: NextFunction) {
const token = req.headers.authorization?.replace(/^bearer\s+/i, '');
if (!token) {
return res.status(401).json({ error: 'Unauthorized' });
}
try {
const payload = jwt.verify(token, process.env.JWT_SECRET!);
req.user = payload;
next();
} catch (err) {
return res.status(401).json({ error: 'Invalid token' });
}
}
ステップ 4:設定とコアファイルの作成
precision_write をバッチモードで使用し、認証インフラストラクチャを作成します。
precision_write:
files:
- path: "middleware.ts"
content: |
# Framework-specific middleware (see patterns above)
- path: "lib/auth.ts"
content: |
# Session validation, token generation
- path: "app/api/auth/login/route.ts"
content: |
# Login endpoint implementation
- path: "app/api/auth/signup/route.ts"
content: |
# Sign-up endpoint with validation
- path: "app/api/auth/logout/route.ts"
content: |
# Logout and session cleanup
verbosity: minimal
重要:プレースホルダーを使用しない
- 検証を完全に実装します(zod、yup など)
- 適切なエラーハンドリングを含めます
- bcrypt/argon2 でパスワードをハッシュ化します
- セキュアなセッション設定を使用します
- 該当する場合は CSRF 保護を追加します
ステップ 5:依存関係のインストール
precision_exec を使用して、必要なパッケージをインストールします。
precision_exec:
commands:
# For JWT-based auth
- cmd: "npm install jsonwebtoken bcryptjs"
- cmd: "npm install -D @types/jsonwebtoken @types/bcryptjs"
# For session-based auth
- cmd: "npm install express-session connect-redis"
- cmd: "npm install -D @types/express-session"
# For managed services
- cmd: "npm install @clerk/nextjs" # Clerk
- cmd: "npm install next-auth" # NextAuth
- cmd: "npm install better-auth" # Better Auth (replaces deprecated Lucia)
verbosity: minimal
データベースのマイグレーションを実行する(必要な場合):
precision_exec:
commands:
- cmd: "npx prisma migrate dev --name add_user_auth"
timeout_ms: 60000
- cmd: "npx prisma generate"
verbosity: standard
ステップ 6:セキュリティ検証
analysis-engine のツールを使用して、セキュリティを検証します。
1. ハードコードされたシークレットをスキャンする:
scan_for_secrets:
paths:
- "lib/auth.ts"
- "app/api/auth/**/*.ts"
- "middleware.ts"
**期待される結果:**シークレットが 0 件検出されます。すべての API キー、JWT シークレット、認証情報は .env ファイルに保存する必要があります。
シークレットが検出された場合:
.envまたは.env.localに移動します- アクセスには
process.env.VAR_NAMEを使用します - まだ追加されていない場合は、
.gitignoreに追加します
2. 環境変数を監査する:
env_audit:
check_documented: true
**期待される結果:**認証関連の環境変数がすべて .env.example または README に記載されています。
必要な変数(一般的な例):
JWT_SECRETまたはSESSION_SECRETDATABASE_URL(データベースを使用する場合)- OAuth 認証情報(
GOOGLE_CLIENT_ID、GITHUB_CLIENT_SECRETなど) NEXTAUTH_URLとNEXTAUTH_SECRET(NextAuth 用)
3. 実装を検証する:
# Use precision_grep to validate critical security patterns
precision_grep:
queries:
- id: password_hashing
pattern: "bcrypt|argon2|hashPassword"
path: "lib"
glob: "**/*.ts"
- id: httpOnly_cookies
pattern: "httpOnly.*true|httpOnly:\s*true"
path: "."
glob: "**/*.ts"
- id: csrf_protection
pattern: "csrf|CsrfToken|verifyCsrfToken"
path: "."
glob: "**/*.ts"
output:
format: "count_only"
ステップ 7:保護されたルートの実装
ルート保護ユーティリティを作成します。
サーバーサイド保護(Next.js App Router):
// lib/auth.ts
import { cookies } from 'next/headers';
import { redirect } from 'next/navigation';
export async function requireAuth() {
const cookieStore = await cookies();
const session = cookieStore.get('session')?.value;
if (!session) {
redirect('/login');
}
const user = await validateSession(session);
if (!user) {
redirect('/login');
}
return user;
}
クライアントサイドのフック(React):
// hooks/useAuth.ts
import { useEffect, useState } from 'react';
import { useRouter } from 'next/navigation';
export function useAuth(options?: { redirectTo?: string }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const router = useRouter();
useEffect(() => {
fetch('/api/auth/me')
.then(res => res.ok ? res.json() : null)
.then(data => {
if (!data && options?.redirectTo) {
router.push(options.redirectTo);
} else {
setUser(data);
}
})
.finally(() => setLoading(false));
}, [options?.redirectTo, router]);
return { user, loading };
}
ルートに適用する:
precision_edit を使用して、既存のルートに認証チェックを追加します。
precision_edit:
files:
- path: "app/dashboard/page.tsx"
edits:
- find: "export default function DashboardPage()"
replace: |
export default async function DashboardPage() {
const user = await requireAuth();
hints:
near_line: 1
ステップ 8:実装をテストする
suggest_test_cases を使用して、認証に特化したテストシナリオを生成します。
suggest_test_cases:
file: "lib/auth.ts"
category: "authentication"
期待されるテストケース:
- 正しい認証情報でログインできる
- 誤ったパスワードでログインに失敗する
- 有効なデータでサインアップできる
- 重複したメールアドレスでサインアップに失敗する
- 未認証ユーザーが保護されたルートにアクセスするとリダイレクトされる
- セッショントークンが更新される
- ログアウトするとセッションが消去される
- CSRF トークンが検証される
- パスワードリセットのフローが動作する
検証スクリプトを実行する:
bash scripts/auth-checklist.sh .
**期待される終了コード:**0(すべてのチェックに合格)
手動テストのチェックリスト:
- 新しいユーザーをサインアップする
- データベース内でパスワードがハッシュ化されていることを確認する
- 認証情報を使ってログインする
- 保護されたルートにアクセスする(成功するはずです)
- ログアウトする
- 保護されたルートにアクセスする(ログイン画面にリダイレクトされるはずです)
- OAuth フローをテストする(実装されている場合)
- ページの再読み込み後もセッションが維持されることを確認する
- トークンの有効期限切れと更新をテストする
フレームワーク別の一般的なパターン
Next.js(App Router)
セッション管理:
- 'next/headers' の cookies() を使用します
- httpOnly、secure、sameSite を設定した Cookie を使用します
- middleware.ts とサーバーコンポーネントで検証します
保護されたルート:
- サーバーサイド:page/layout で await requireAuth() を呼び出します
- クライアントサイド:redirectTo を指定して useAuth() フックを使用します
- API ルート:ルートハンドラーで Cookie を確認します
OAuth:
- 手軽さを重視する場合は NextAuth を使用します
- または、リダイレクト URI を使った OAuth フローを手動で実装します
Remix
セッション管理:
- createCookieSessionStorage を使用します
- loaders で検証します
- actions でセッションをコミットします
保護されたルート:
- loader でセッションを確認し、未認証の場合は redirect() をスローします
- getSession() ユーティリティを一貫して使用します
OAuth:
- remix-auth のストラテジーを使用します
- 専用ルートでコールバックを処理します
Express/Fastify
セッション管理:
- express-session と Redis ストアを使用します
- または Authorization ヘッダー内の JWT トークンを使用します
保護されたルート:
- requireAuth、optionalAuth などのミドルウェア関数を使用します
- 特定のルートまたは全体に適用します
OAuth:
- passport.js のストラテジーを使用します
- ユーザーのシリアライズ/デシリアライズを設定します
エラー処理
認証の失敗には、エラーリカバリープロトコルに従ってください。
ログインの失敗
try {
const user = await validateCredentials(email, password);
await createSession(user.id);
return { success: true };
} catch (err) {
if (err instanceof InvalidCredentialsError) {
// Don't leak whether email exists
return { error: 'Invalid email or password' };
}
if (err instanceof UserLockedError) {
return { error: 'Account locked. Contact support.' };
}
throw err; // Unexpected error
}
セッション検証の失敗
export async function validateSession(token: string) {
try {
const payload = jwt.verify(token, process.env.JWT_SECRET!);
return await getUserById(payload.userId);
} catch (err) {
if (err instanceof jwt.TokenExpiredError) {
return null; // Let caller handle (refresh or re-login)
}
if (err instanceof jwt.JsonWebTokenError) {
return null; // Invalid token
}
throw err; // Unexpected error
}
}
データベースエラー
try {
const user = await db.user.create({
data: { email, passwordHash }
});
} catch (err) {
if (err.code === 'P2002') {
// Prisma unique constraint violation
return { error: 'Email already registered' };
}
throw err;
}
セキュリティチェックリスト
実装を完了とする前に、以下を確認してください。
- パスワードが bcrypt(コスト 10 以上)または argon2 でハッシュ化されている
- セッショントークンがランダム(crypto.randomBytes)または署名済み JWT である
- Cookie が httpOnly、secure(本番環境)、sameSite: 'lax' または 'strict' に設定されている
- CSRF 保護が有効になっている(Cookie ベースの認証の場合)
- ログイン/サインアップのエンドポイントにレート制限が設定されている
- すべての認証エンドポイントで入力が検証されている
- エラーメッセージに機密データが含まれていない
- 環境変数が .env.example に記載されている
- ソースコードにシークレットがハードコードされていない
- セッションの有効期限が設定されている(一般的には 1~7 日)
- ログアウト時にすべての認証トークン/セッションが消去される
- OAuth のリダイレクト URI が許可リストに登録されている
参考資料
references/decision-tree.md- マネージド、セルフホスト可能、サーバーレスの比較.goodvibes/memory/patterns.json- プロジェクト固有の認証パターンscripts/auth-checklist.sh- 自動検証スクリプト
トラブルシューティング
「リクエスト間でセッションが保持されない」
考えられる原因:
- Cookie が設定されていない(レスポンスヘッダーを確認してください)
- httpOnly によりクライアント側からアクセスできない(想定どおりです)
- sameSite: 'strict' によりクロスオリジン通信がブロックされている('lax' を使用してください)
- Cookie のドメインが一致していない
修正方法:
- 認証コードの Cookie 設定を確認します
- レスポンスに Cookie が含まれていることを確認します:
precision_exec: { cmd: "curl -v localhost:3000/api/auth/login" } - ミドルウェアが Cookie を正しく読み取っていることを確認します
「サーバー再起動後に JWT トークンが無効になる」
考えられる原因:
- 再起動のたびに JWT_SECRET が変わる
- .env ファイルにシークレットがない
- 環境によってシークレットが異なる
修正方法:
- JWT_SECRET を .env に移します:
JWT_SECRET=<random-256-bit-hex> - 次のコマンドで生成します:
openssl rand -hex 32 - .env を git にコミットしないでください
「OAuth コールバックが redirect_uri_mismatch で失敗する」
考えられる原因:
- OAuth プロバイダーのダッシュボードでコールバック URL が許可リストに登録されていない
- http と https が一致していない
- ポート番号が指定されていない
修正方法:
- OAuth プロバイダーの設定を確認します
- エラーに表示されているとおりのコールバック URL を追加します
- ローカル開発では、http://localhost:3000/api/auth/callback/google を使用します(例)
「保護されたルートでリダイレクトされない」
考えられる原因:
- ミドルウェアが正しく設定されていない
- Matcher パターンがルートと一致しない
- セッションの検証が失敗しているが、エラーが表示されない
修正方法:
- middleware.ts の config.matcher を確認します
- ミドルウェアにログを追加し、実行されていることを確認します
- セッションの検証を個別にテストします
実装後の次のステップ
-
ロールベースのアクセス制御(RBAC)を追加する:
- User モデルに
roleフィールドを追加する - 権限チェック用のユーティリティを作成する
- 管理者用ルートを保護する
- User モデルに
-
パスワードリセットを実装する:
- リセットトークンを生成する
- リセットリンクを含むメールを送信する
- トークンを検証し、パスワードを更新する
-
メール認証を追加する:
- サインアップ時に認証トークンを生成する
- 認証メールを送信する
- トークンを検証し、ユーザーを認証済みとしてマークする
-
リフレッシュトークンを設定する:
- 有効期間の短いアクセス トークンを発行する(15 min)
- 有効期間の長いリフレッシュトークンを発行する(7 days)
- リフレッシュトークンの使用時にローテーションする
-
監査ログを追加する:
- すべてのログイン試行(成功と失敗)を記録する
- IP アドレスとユーザーエージェントを追跡する
- 不審なアクティビティを監視する
まとめ
このワークフローでは、次のことが可能です。
- フレームワークに依存しない認証の実装
- 検証を重視したセキュリティファーストのアプローチ
- GoodVibes の精密ツールとの統合
- スクリプトによる自動検証
- エラー処理とトラブルシューティング
各ステップを順番に進め、すべてのファイル操作と検証に精密ツールを使用してください。