clerk-webhooks-events
Clerk webhookを設定して認証イベントを処理します。ユーザー同期の設定、認証イベントの処理、またはClerkを外部システムと連携させる際に使用します。「clerk webhooks」「clerk events」「clerk user sync」「clerk notifications」「clerk event handling」といったフレーズで呼び出すことができます。
description の原文を見る
Configure Clerk webhooks and handle authentication events. Use when setting up user sync, handling auth events, or integrating Clerk with external systems. Trigger with phrases like "clerk webhooks", "clerk events", "clerk user sync", "clerk notifications", "clerk event handling".
SKILL.md 本文
Clerkウェブフック & イベント
概要
ユーザーライフサイクルイベントとデータ同期のためのClerkウェブフックを設定・処理します。
前提条件
- ウェブフックアクセス可能なClerkアカウント
- ウェブフック用のHTTPSエンドポイント
- 検証用のsvixパッケージ
手順
ステップ1: 依存関係をインストール
npm install svix
ステップ2: ウェブフックエンドポイントを作成
// app/api/webhooks/clerk/route.ts
import { Webhook } from 'svix'
import { headers } from 'next/headers'
import { WebhookEvent } from '@clerk/nextjs/server'
export async function POST(req: Request) {
const WEBHOOK_SECRET = process.env.CLERK_WEBHOOK_SECRET
if (!WEBHOOK_SECRET) {
throw new Error('CLERK_WEBHOOK_SECRET not set')
}
// Svixヘッダーを取得
const headerPayload = await headers()
const svix_id = headerPayload.get('svix-id')
const svix_timestamp = headerPayload.get('svix-timestamp')
const svix_signature = headerPayload.get('svix-signature')
if (!svix_id || !svix_timestamp || !svix_signature) {
return Response.json({ error: 'Missing headers' }, { status: 400 })
}
// ボディを取得
const payload = await req.json()
const body = JSON.stringify(payload)
// ウェブフックを検証
const wh = new Webhook(WEBHOOK_SECRET)
let evt: WebhookEvent
try {
evt = wh.verify(body, {
'svix-id': svix_id,
'svix-timestamp': svix_timestamp,
'svix-signature': svix_signature,
}) as WebhookEvent
} catch (err) {
console.error('Webhook verification failed:', err)
return Response.json({ error: 'Invalid signature' }, { status: 400 })
}
// イベントを処理
const eventType = evt.type
console.log(`Received webhook: ${eventType}`)
switch (eventType) {
case 'user.created':
await handleUserCreated(evt.data)
break
case 'user.updated':
await handleUserUpdated(evt.data)
break
case 'user.deleted':
await handleUserDeleted(evt.data)
break
case 'session.created':
await handleSessionCreated(evt.data)
break
case 'organization.created':
await handleOrgCreated(evt.data)
break
default:
console.log(`Unhandled event type: ${eventType}`)
}
return Response.json({ success: true })
}
ステップ3: イベントハンドラーを実装
// lib/webhook-handlers.ts
import { db } from './db'
interface ClerkUserData {
id: string
email_addresses: Array<{ email_address: string; id: string }>
first_name: string | null
last_name: string | null
image_url: string
created_at: number
updated_at: number
}
export async function handleUserCreated(data: ClerkUserData) {
const primaryEmail = data.email_addresses.find(
e => e.id === data.primary_email_address_id
)?.email_address
await db.user.create({
data: {
clerkId: data.id,
email: primaryEmail,
firstName: data.first_name,
lastName: data.last_name,
imageUrl: data.image_url,
createdAt: new Date(data.created_at)
}
})
// ウェルカムメールを送信
await sendWelcomeEmail(primaryEmail)
console.log(`User created: ${data.id}`)
}
export async function handleUserUpdated(data: ClerkUserData) {
const primaryEmail = data.email_addresses.find(
e => e.id === data.primary_email_address_id
)?.email_address
await db.user.update({
where: { clerkId: data.id },
data: {
email: primaryEmail,
firstName: data.first_name,
lastName: data.last_name,
imageUrl: data.image_url,
updatedAt: new Date(data.updated_at)
}
})
console.log(`User updated: ${data.id}`)
}
export async function handleUserDeleted(data: { id: string }) {
await db.user.delete({
where: { clerkId: data.id }
})
// ユーザーデータをクリーンアップ
await cleanupUserData(data.id)
console.log(`User deleted: ${data.id}`)
}
export async function handleSessionCreated(data: any) {
// セッションをアナリティクス用にログ
await db.sessionLog.create({
data: {
userId: data.user_id,
sessionId: data.id,
createdAt: new Date(data.created_at),
userAgent: data.user_agent
}
})
console.log(`Session created: ${data.id}`)
}
export async function handleOrgCreated(data: any) {
await db.organization.create({
data: {
clerkOrgId: data.id,
name: data.name,
slug: data.slug,
createdAt: new Date(data.created_at)
}
})
console.log(`Organization created: ${data.id}`)
}
ステップ4: べき等性とエラーハンドリング
// lib/webhook-idempotency.ts
import { Redis } from '@upstash/redis'
const redis = Redis.fromEnv()
export async function processWithIdempotency(
eventId: string,
handler: () => Promise<void>
) {
const key = `webhook:${eventId}`
// 既に処理されているかチェック
const processed = await redis.get(key)
if (processed) {
console.log(`Event ${eventId} already processed`)
return { skipped: true }
}
try {
await handler()
// 処理済みとしてマーク (24時間後に失効)
await redis.set(key, 'processed', { ex: 86400 })
return { success: true }
} catch (error) {
// エラーをログするが、処理済みとしてはマークしない
console.error(`Failed to process ${eventId}:`, error)
throw error
}
}
// ウェブフックハンドラーでの使用法
export async function POST(req: Request) {
// ... 検証コード ...
const svix_id = headerPayload.get('svix-id')!
const result = await processWithIdempotency(svix_id, async () => {
switch (evt.type) {
case 'user.created':
await handleUserCreated(evt.data)
break
// ... その他のハンドラー
}
})
return Response.json(result)
}
ステップ5: Clerkダッシュボードでウェブフックを設定
- Clerkダッシュボード > ウェブフック に移動
- エンドポイントURLを追加:
https://yourdomain.com/api/webhooks/clerk - イベントを選択:
user.createduser.updateduser.deletedsession.createdsession.endedorganization.*(組織を使用する場合)
- ウェブフックシークレットを環境変数にコピー
利用可能なイベント
| イベント | 説明 |
|---|---|
user.created | 新規ユーザーがサインアップ |
user.updated | ユーザープロフィールが変更 |
user.deleted | ユーザーアカウントが削除 |
session.created | 新規セッション開始 |
session.ended | セッション終了 |
session.revoked | セッション手動取消 |
organization.created | 組織を作成 |
organization.updated | 組織設定が変更 |
organization.deleted | 組織を削除 |
organizationMembership.* | メンバーを追加・削除 |
email.created | メール確認を送信 |
出力
- ウェブフックエンドポイントが設定完了
- イベントハンドラーを実装
- べき等性の保護
- ユーザーデータ同期が動作
ウェブフックのローカルテスト
# ローカルテスト用にngrokを使用
npx ngrok http 3000
# またはClerk CLIを使用
npx @clerk/cli dev
# curlでテスト
curl -X POST http://localhost:3000/api/webhooks/clerk \
-H "Content-Type: application/json" \
-H "svix-id: test" \
-H "svix-timestamp: $(date +%s)" \
-H "svix-signature: v1,..." \
-d '{"type":"user.created","data":{}}'
エラーハンドリング
| エラー | 原因 | 解決策 |
|---|---|---|
| Invalid signature | 間違ったシークレット | CLERK_WEBHOOK_SECRETを確認 |
| Missing headers | Clerkからのリクエストではない | 送信元がClerkであることを確認 |
| 重複処理 | イベントが2回送信 | べき等性を実装 |
| タイムアウト | ハンドラーが遅い | バックグラウンドジョブを使用 |
リソース
次のステップ
最適化戦略については clerk-performance-tuning に進んでください。
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- Brmbobo
- リポジトリ
- Brmbobo/Web2podcast
- ライセンス
- MIT
- 最終更新
- 2026/1/26
Source: https://github.com/Brmbobo/Web2podcast / ライセンス: MIT
関連スキル
secure-code-guardian
認証・認可の実装、ユーザー入力の保護、OWASP Top 10の脆弱性対策が必要な場合に使用します。bcrypt/argon2によるパスワードハッシング、パラメータ化ステートメントによるSQLインジェクション対策、CORS/CSPヘッダーの設定、Zodによる入力検証、JWTトークンの構築などのカスタムセキュリティ実装に対応します。認証、認可、入力検証、暗号化、OWASP Top 10対策、セッション管理、セキュリティ強化全般で活用できます。ただし、構築済みのOAuth/SSO統合や単独のセキュリティ監査が必要な場合は、より特化したスキルの検討をお勧めします。
claude-authenticity
APIエンドポイントが本物のClaudeによって支えられているか(ラッパーやプロキシ、偽装ではないか)を、claude-verifyプロジェクトを模した9つの重み付きルールベースチェックで検証できます。また、Claudeの正体を上書きしているプロバイダーから注入されたシステムプロンプトも抽出します。完全に自己完結しており、httpx以外の追加パッケージは不要です。Claude APIキーまたはエンドポイントを検証したい場合、サードパーティのClaudeサービスが本物か確認したい場合、APIプロバイダーのClaude正当性を監査したい場合、複数モデルを並行してテストしたい場合、またはプロバイダーが注入したシステムプロンプトを特定したい場合に使用できます。
anth-security-basics
Anthropic Claude APIのセキュリティベストプラクティスを適用し、キー管理、入力値の検証、プロンプトインジェクション対策を実施します。APIキーの保護、Claudeに送信する前のユーザー入力検証、コンテンツセーフティガードレールの実装が必要な場合に活用できます。「anthropic security」「claude api key security」「secure anthropic」「prompt injection defense」といったフレーズでトリガーされます。
x-ray
x-ray.mdプレ監査レポートを生成します。概要、強化された脅威モデル(プロトコルタイプのプロファイリング、Gitの重み付け攻撃面分析、時間軸リスク分析、コンポーザビリティ依存関係マッピング)、不変条件、統合、ドキュメント品質、テスト分析、開発者・Gitの履歴をカバーしています。「x-ray」「audit readiness」「readiness report」「pre-audit report」「prep this protocol」「protocol prep」「summarize this protocol」のキーワードで実行されます。
semgrep
Semgrepスタティック分析スキャンを実行し、カスタム検出ルールを作成します。Semgrepでのコードスキャン、セキュリティ脆弱性の検出、カスタムYAMLルールの作成、または特定のバグパターンの検出が必要な場合に使用します。重要:ユーザーが「バグをスキャンしたい」「コード品質を確認したい」「脆弱性を見つけたい」「スタティック分析」「セキュリティlint」「コード監査」または「コーディング標準を適用したい」と尋ねた場合も、Semgrepという名称を明記していなくても、このスキルを使用してください。Semgrepは30以上の言語に対応したパターンベースのコードスキャンに最適なツールです。
ghost-bits-cast-attack
Java「ゴーストビッツ」/キャストアタック プレイブック(Black Hat Asia 2026)。16ビット文字が8ビットバイトに暗黙的に縮小されるJavaサービスへの攻撃時に使用します。WAF/IDSを回避して、SQLインジェクション、デシリアライゼーション型RCE、ファイルアップロード(Webシェル)、パストトラバーサル、CRLF インジェクション、リクエストスマグリング、SMTPインジェクションを実行できます。Tomcat、Spring、Jetty、Undertow、Vert.x、Jackson、Fastjson、Apache Commons BCEL、Apache HttpClient、Angus Mail、JDK HttpServer、Lettuce、Jodd、XMLWriterに影響し、WAFバイパスにより多くの「パッチ済み」CVEを再度有効化します。