หากต้องการทดสอบ Clerk Webhook บน localhost ด้วย Next.js ให้สร้าง route POST ของ App Router เปิดพอร์ต 3000 ผ่านอุโมงค์ HTTPS เพิ่ม URL สาธารณะเป็น endpoint ของ Clerk Webhook และตรวจสอบทุกคำขอด้วย verifyWebhook() ก่อนซิงค์ข้อมูลผู้ใช้ กำหนดให้ route นี้เป็นสาธารณะใน Clerk middleware ต่อไป เพราะ signing secret ใช้ยืนยันคำขอระหว่างเครื่อง ไม่ใช่ session ของเบราว์เซอร์
เมื่อ Webhooks ของClerkเป็นเครื่องมือซิงค์ที่เหมาะสม
Clerkยังคงเป็นแหล่งที่มาของความจริง Webhook มีประโยชน์เมื่อแอปพลิเคชันของคุณต้องการการฉายภาพภายในเครื่องสำหรับการเข้าร่วม การค้นหา การรายงาน เมตาดาต้าการอนุญาต หรือการบูรณาการที่ไม่สามารถสืบค้นพนักงานได้ตามความต้องการ เหตุการณ์ทั่วไปได้แก่ user.created, user.updated, และ user.deleted.
Webhook เป็นแบบอะซิงโครนัส ผู้ใช้อาจลงทะเบียนให้เสร็จสิ้นก่อนที่จะมีการฉายภาพฐานข้อมูล คุณสามารถลองส่งใหม่ได้ และการอัปเดตจะมาถึงได้ใกล้เคียงกัน อย่าทำให้การฉายภาพเป็นแหล่งเดียวสำหรับการตรวจสอบข้อมูลระบุตัวตนหลังการลงชื่อสมัครใช้ทันที การออกแบบการอ่านเพื่อให้ทนต่อการหน่วงเวลาสั้นๆ หรือสร้างแถวแอปพลิเคชันอย่างชัดเจนในโฟลว์ผู้ใช้ และปล่อยให้ webhooks ปรับยอด
สร้างปลายทางเราเตอร์แอป Next.js สาธารณะ
เพิ่ม app/api/webhooks/clerk/route.ts. ปัจจุบันClerk คู่มือการซิงค์ การใช้งาน verifyWebhook จาก @clerk/nextjs/webhooks. ตัวช่วยใช้คำขอ ตรวจสอบลายเซ็น Webhooks มาตรฐาน และส่งคืนข้อมูลเหตุการณ์ที่พิมพ์
// app/api/webhooks/clerk/route.ts
import { verifyWebhook } from '@clerk/nextjs/webhooks';
import { NextRequest } from 'next/server';
export const runtime = 'nodejs';
export async function POST(request: NextRequest) {
try {
const event = await verifyWebhook(request);
await processOnce(event, async () => {
switch (event.type) {
case 'user.created':
case 'user.updated':
await upsertClerkUser(event.data);
break;
case 'user.deleted':
if (event.data.id) await archiveClerkUser(event.data.id);
break;
}
});
return new Response('accepted', { status: 200 });
} catch (error) {
console.error('Clerk webhook rejected', safeError(error));
return new Response('invalid webhook', { status: 400 });
}
}
ตามค่าเริ่มต้น ผู้ช่วยเหลือจะอ่าน CLERK_WEBHOOK_SIGNING_SECRET. Clerk ตรวจสอบการอ้างอิง Webhook ยังอนุญาตให้มีความชัดเจน signingSecret ตัวเลือก แต่การกำหนดค่าสภาพแวดล้อมจะหลีกเลี่ยงการฝังข้อมูลลับในแหล่งที่มา
แยกเส้นทาง webhook ออกจากการป้องกันเซสชัน
คำขอ Webhook ไม่มีเซสชันClerkของผู้ใช้ของคุณ หากมิดเดิลแวร์เรียก auth.protect() สำหรับทุกเส้นทาง API การส่งมอบของClerkจะได้รับการเปลี่ยนเส้นทาง 401 หรือ 404 ก่อนดำเนินการตรวจสอบลายเซ็น กำหนดเส้นทางแอปพลิเคชันที่ได้รับการป้องกันอย่างชัดเจนและออก /api/webhooks/clerk สาธารณะ.
// middleware.ts for Next.js 15 and earlier
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';
const isProtectedRoute = createRouteMatcher([
'/dashboard(.*)',
'/api/private(.*)',
]);
export default clerkMiddleware(async (auth, request) => {
if (isProtectedRoute(request)) await auth.protect();
});
export const config = {
matcher: [
'/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico)).*)',
'/(api|trpc)(.*)',
],
};
Clerk คู่มือการแก้ไขจุดบกพร่องของ webhook โทรออกโดยเฉพาะยกเว้นเส้นทาง webhook ในเวอร์ชัน Next.js ที่ใหม่กว่า อาจใช้แบบแผน proxy.ts; ปฏิบัติตามคู่มือเวอร์ชันClerkที่ติดตั้งในโครงการของคุณ สาธารณะไม่ได้หมายความว่าเชื่อถือได้: verifyWebhook() เป็นข้อบังคับก่อนดำเนินการใดๆ
เชื่อมต่อClerkกับ localhost
- วิ่ง
npm run devและตรวจสอบว่าแอป Next.js กำลังฟังบนพอร์ต 3000 - วิ่ง
npx portpreview 3000. - ในแดชบอร์ดพนักงาน ให้สร้างจุดสิ้นสุดเว็บฮุคด้วย
https://your-subdomain.portpreview.dev/api/webhooks/clerk. - เลือกเฉพาะเหตุการณ์ผู้ใช้ เซสชัน องค์กร หรืออีเมลที่ต้องการ
- คัดลอกความลับในการลงนามปลายทางไปที่
CLERK_WEBHOOK_SIGNING_SECRETในสภาพแวดล้อมท้องถิ่นของคุณแล้วรีสตาร์ท Next.js - เปิดแท็บการทดสอบของอุปกรณ์ปลายทาง เลือก
user.createdและเลือกส่งตัวอย่าง - ยืนยันความพยายามว่าสำเร็จ เส้นทางในเครื่องของคุณส่งคืน 200 และแถวฐานข้อมูลที่คาดไว้เปลี่ยนไปหนึ่งครั้ง
URL ช่องสัญญาณจะต้องใช้งานอยู่สำหรับตัวอย่างที่ตามมา หากมีการเปลี่ยนแปลง ให้อัปเดตจุดสิ้นสุด อย่านำความลับการเซ็นชื่อปลายทางการผลิตมาใช้ซ้ำสำหรับการทดสอบในเครื่อง สร้างจุดสิ้นสุดเฉพาะสภาพแวดล้อมเพื่อให้ประวัติการหมุนเวียนและการตรวจสอบมีความชัดเจน
การซิงโครไนซ์ผู้ใช้แบบจำลองอย่างระมัดระวัง
ใช้ ID ผู้ใช้Clerkเป็นคีย์ภายนอก
เก็บ user_... ในแบบที่ไม่เหมือนใคร clerk_user_id คอลัมน์. ใส่คีย์นั้นลงไปเพื่อลองอีกครั้ง user.created มาบรรจบกันแทนที่จะล้มเหลว เก็บคีย์หลักภายในของคุณเองไว้หากมีตารางอื่นอ้างอิงอยู่แล้ว
เลือกอีเมลหลักโดยเจตนา
ข้อมูลผู้ใช้ของพนักงานประกอบด้วยบันทึกที่อยู่อีเมลและรหัสที่อยู่อีเมลหลัก แก้ไขระเบียนหลักด้วย ID แทนที่จะรับองค์ประกอบอาร์เรย์แรก อีเมลสามารถเปลี่ยนแปลงได้ อย่าใช้เป็นคีย์ต่างประเทศที่ไม่เปลี่ยนรูป
ตัดสินใจว่าการลบหมายถึงอะไร
ก user.deleted กิจกรรมอาจมีข้อมูลน้อยกว่าการสร้างหรืออัปเดต ใช้ ID เพื่อทำให้ไม่ระบุชื่อ ลบชั่วคราว หรือเริ่มต้นเวิร์กโฟลว์การเก็บรักษาตามนโยบายของคุณ การลบซ้อนแบบซ่อนสามารถทำลายบันทึกการเรียกเก็บเงินหรือการตรวจสอบที่กฎระเบียบกำหนดให้คุณต้องเก็บรักษาไว้
อย่าสะท้อนทุกสิ่ง
คงอยู่เฉพาะฟิลด์ที่แอปพลิเคชันของคุณต้องการเท่านั้น ทุกช่องโปรไฟล์ที่คัดลอกจะสร้างความเป็นส่วนตัว การเก็บรักษา และความล้าสมัย การดึงข้อมูลพนักงานไม่ค่อยได้ใช้ตามความต้องการ แทนที่จะทำซ้ำเพย์โหลดทั้งหมด
ลองใหม่และสั่งซื้อโดยไม่เป็นอันตราย
พนักงานบันทึกว่าการตอบสนองที่ไม่ใช่ 2xx ทำให้เกิดการลองเหตุการณ์อีกครั้ง บันทึกตัวระบุข้อความ Webhook จากข้อมูลเมตาหรือส่วนหัวของ Webhook ที่ลงนามแล้วเป็นใบรับที่ไม่ซ้ำกันก่อนที่จะใช้เอฟเฟกต์ หาก SDK ของคุณเปิดเผยรหัส Webhooks มาตรฐานในส่วนหัว ให้เก็บไว้ข้างประเภทเหตุการณ์และการประทับเวลา ส่งกลับ 200 สำหรับสำเนาที่เสร็จสมบูรณ์
สำหรับการอัปเดตผู้ใช้ ให้เปรียบเทียบการประทับเวลาของเหตุการณ์หรือใช้กฎการเขียนล่าสุดที่ป้องกันไม่ให้เหตุการณ์เก่าเขียนทับข้อมูลโปรไฟล์ที่ใหม่กว่า สำหรับสถานะที่มีมูลค่าสูง ให้ดึงข้อมูลผู้ใช้ปัจจุบันจากพนักงานหลังจากการตรวจสอบ และถือว่าเว็บฮุคเป็นสัญญาณให้ปรับยอด เก็บใบเสร็จรับเงิน การอัปเดตผู้ใช้ และงานกล่องขาออกไว้ในธุรกรรมฐานข้อมูลเดียว
await db.transaction(async (tx) => {
const inserted = await tx.webhookReceipt.insertIfAbsent(messageId);
if (!inserted) return;
await tx.user.upsert({
clerkUserId: clerk.id,
primaryEmail: findPrimaryEmail(clerk),
sourceUpdatedAt: eventTimestamp,
});
await tx.outbox.enqueue('profile-synced', { clerkUserId: clerk.id });
});
รูปแบบนี้ป้องกันอีเมลต้อนรับที่ซ้ำกันและการเขียนบางส่วน ดู การลอง webhook อีกครั้งและ idempotency สำหรับตัวเลือกสคีมา
แก้ไขปัญหาการจัดส่งClerkในพื้นที่
404, เปลี่ยนเส้นทาง หรือ HTML แทนตัวจัดการของคุณ
ตรวจสอบเส้นทางของไฟล์ การส่งออก POST และ URL ช่องสัญญาณแบบเต็ม ตรวจสอบมิดเดิลแวร์และโลแคลเขียนใหม่ เว็บฮุคไม่ควรผ่านการเปลี่ยนเส้นทางการเข้าสู่ระบบหรือการตรวจสอบแบบฟอร์ม CSRF ทดสอบ URL สาธารณะด้วย POST พื้นฐาน คาดว่าจะมีการปฏิเสธลายเซ็นจากเส้นทางของคุณ ไม่ใช่เฟรมเวิร์ก 404
verifyWebhook() ขว้างเสมอ
รีสตาร์ท Next.js หลังจากตั้งค่าความลับในการลงนาม ยืนยันว่าข้อมูลลับเป็นของอุปกรณ์ปลายทางและสภาพแวดล้อมนี้ อย่าแยกวิเคราะห์ โคลนอย่างไม่ถูกต้อง หรือใช้เนื้อหาคำขอก่อนที่จะส่งต่อให้ผู้ช่วยเหลือ ตรวจสอบว่าช่องสัญญาณเก็บส่วนหัวลายเซ็น Webhooks มาตรฐานไว้
แดชบอร์ดแสดงการลองใหม่
ดูการตอบสนองความพยายามที่แน่นอน ส่งคืน 2xx หลังจากการยอมรับแบบถาวรเท่านั้น แต่ให้ประมวลผลต่ำกว่าระยะหมดเวลาของผู้ให้บริการ การย้ายฐานข้อมูล ข้อผิดพลาดเกี่ยวกับข้อจำกัดเฉพาะ และการเรียกใช้บริการที่ไม่พร้อมใช้งานพร้อมกันเป็นสาเหตุที่พบบ่อย 500 สาเหตุ
แถวซ้ำกันหรือเก่าแล้ว
เพิ่มข้อจำกัดเฉพาะสำหรับรหัสพนักงานและรหัสข้อความ webhook ทำ user.created และ user.updated ทั้งการอัพอย่างปลอดภัย จากนั้นป้องกันการประทับเวลาเหตุการณ์เก่าๆ เล่นซ้ำตัวอย่างหลังการแก้ไขแต่ละครั้งเพื่อพิสูจน์การจัดการที่ซ้ำกัน
รายการตรวจสอบความปลอดภัย
- ตรวจสอบทุกคำขอกับผู้ช่วยของClerkก่อนที่จะบันทึกฟิลด์เพย์โหลดหรือเขียนข้อมูล
- รักษาเส้นทางที่ไม่ผ่านการรับรองความถูกต้องโดยมิดเดิลแวร์เซสชันผู้ใช้ แต่ป้องกันโดยลายเซ็นเว็บฮุค
- ใช้ข้อมูลลับปลายทางที่แยกกันสำหรับสภาพแวดล้อมภายในเครื่อง การแสดงตัวอย่าง ชั่วคราว และการใช้งานจริง
- ขจัด ID ข้อความที่เซ็นชื่อซ้ำและจำกัด ID ผู้ใช้โดยไม่ซ้ำกัน
- ตรวจทานอีเมล โทรศัพท์ โทเค็น ข้อมูลลับ และเพย์โหลดทั้งหมดจากบันทึกปกติ
- สามารถเลือกเพิ่มการควบคุม IP ที่จัดทำเอกสารของ Clerk/Svix เพื่อเป็นการป้องกันในเชิงลึก แต่อย่าแทนที่การตรวจสอบลายเซ็นด้วยการกรอง IP
- จำกัดอัตราการรับส่งข้อมูลที่ไม่ถูกต้อง กำหนดขนาดเนื้อหา และหมุนเวียนข้อมูลลับหากปรากฏในบันทึกหรือการควบคุมแหล่งที่มา
Clerk ภาพรวมของเว็บฮุค อธิบายการตรวจสอบลายเซ็นและข้อจำกัด IP ของ Svix ที่เป็นตัวเลือก สำหรับข้อมูลพื้นฐาน Raw-Body ของ Next.js และพฤติกรรมการกำหนดเส้นทาง ให้ดำเนินการต่อด้วย คู่มือเว็บฮุค Localhost ของ Next.js.
