name: clerk-auth
Clerk を使った認証・マルチユーザー対応を craftgarden プロダクトに導入するスキル。 AI PM Service での実績をベースに、横展開を効率化する。
jaJP を適用(Dashboard設定は不要)colorPrimary: '#6B8F71')NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_...
CLERK_SECRET_KEY=sk_...
CLERK_WEBHOOK_SECRET=whsec_...
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
# 旧認証パッケージ削除(存在する場合)
pnpm remove next-auth @auth/drizzle-adapter drizzle-orm
# Clerk パッケージ追加
pnpm add @clerk/nextjs @clerk/localizations svix
SQLスキーマは references/schema-users.sql を参照。
対象テーブル: tasks, projects, その他ユーザースコープが必要なテーブル
// db.ts に追加
export async function upsertUser(data: { id: string; email: string; name?: string | null; image_url?: string | null }): Promise<User> {
// INSERT ON CONFLICT UPDATE パターン
}
export async function getUser(id: string): Promise<User | null>
export async function deleteUser(id: string): Promise<boolean>
export async function assignOrphanedDataToUser(userId: string): Promise<void> {
// user_id IS NULL のレコードを一括割当(初回ユーザー作成時)
}
テンプレート: references/template-auth-helper.ts
src/lib/auth.ts に requireUserId() と getUserId() を実装。
テンプレート: references/template-middleware.ts
注意: matcher から静的アセット(favicon, icons, .png, .svg)を必ず除外すること。 未ログイン時に 404 エラーが出る原因になる。
テンプレート: references/template-clerk-provider.tsx
注意: ClerkProvider はビルド時に publishableKey を要求する。
layout.tsx に export const dynamic = "force-dynamic" を追加すること。
テンプレート: references/template-sign-in-page.tsx
ファイルパス:
src/app/sign-in/[[...sign-in]]/page.tsx
src/app/sign-up/[[...sign-up]]/page.tsx
src/app/api/auth/[...nextauth]/route.tssrc/app/login/page.tsxsrc/types/next-auth.d.ts// src/app/api/webhooks/clerk/route.ts
import { Webhook } from 'svix';
import { headers } from 'next/headers';
// user.created → upsertUser + assignOrphanedDataToUser(初回のみ)
// user.updated → upsertUser
// user.deleted → deleteUser
// ハンドラ先頭で initializeSchema() を呼ぶ(API route は layout を通らない)
Clerk Dashboard Webhook 設定:
https://{domain}/api/webhooks/clerkuser.created, user.updated, user.deletedCLERK_WEBHOOK_SECRET に設定テンプレート: references/template-server-actions.ts
要点:
requireUserId() を呼ぶuserId を含める(マルチユーザーキャッシュ分離)全クエリ関数に userId?: string パラメータを追加し、WHERE user_id = ? で絞り込む。
JOIN クエリの注意: 複数テーブルに user_id がある場合、必ずテーブルプレフィックスを付ける。
// NG: ambiguous column name エラー
const userFilter = ' AND user_id = ?';
// OK: JOIN クエリ用
const userFilter = ' AND t.user_id = ?'; // JOIN あり
const userFilterNoAlias = ' AND user_id = ?'; // 単一テーブル
テンプレート: references/template-ui-components.tsx
import { auth } from '@clerk/nextjs/server';
const { userId } = await auth();
if (!userId) {
return NextResponse.json({ error: '認証が必要です' }, { status: 401 });
}
初回デプロイ後、既存データを Clerk ユーザーに紐づける:
# 1. Clerk API でユーザー ID を取得
curl -H "Authorization: Bearer $CLERK_SECRET_KEY" \
"https://api.clerk.com/v1/users?email_address=user@example.com"
# 2. DB で孤立データを割当
UPDATE tasks SET user_id = 'user_xxxxx' WHERE user_id IS NULL;
UPDATE projects SET user_id = 'user_xxxxx' WHERE user_id IS NULL;
INSERT INTO users (id, email, name) VALUES ('user_xxxxx', 'user@example.com', 'Name');
スクリプト: scripts/setup-vercel-env.sh
Clerk用の環境変数追加と旧認証変数の削除を一括で行う。