Чтобы протестировать веб-перехватчики Clerk на локальном хосте в Next.js, создайте POST-маршрут App Router, откройте порт 3000 с помощью туннеля HTTPS, добавьте общедоступный URL-адрес в качестве конечной точки веб-перехватчика Clerk и проверьте каждый запрос с помощью verifyWebhook() перед синхронизацией пользовательских данных. Сохраняйте маршрут общедоступным в промежуточном программном обеспечении Clerk: секрет подписи удостоверяет подлинность межмашинного запроса, а не сеанса браузера.
Когда вебхуки Clerk — правильный инструмент синхронизации
Clerk остается источником истины. Веб-перехватчик полезен, когда вашему приложению требуется локальная проекция для объединений, поиска, отчетов, метаданных авторизации или интеграций, которые не могут запрашивать Clerk по требованию. Типичные события включают в себя user.created, user.updated, и user.deleted.
Вебхук является асинхронным. Пользователь может завершить регистрацию до того, как появится проекция вашей базы данных, доставки могут быть повторены, а обновления могут поступать близко друг к другу. Не делайте проекцию единственным источником для немедленной проверки личности после регистрации. Проектирование читает так, чтобы допускать небольшую задержку или явно создавать строку приложения в потоке пользователя и позволять веб-перехватчикам согласовывать ее.
Создайте общедоступную конечную точку маршрутизатора приложений Next.js.
Добавлять app/api/webhooks/clerk/route.ts. Текущий статус Clerk руководство по синхронизации использует verifyWebhook от @clerk/nextjs/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 Проверить ссылку на вебхук также допускает явное signingSecret вариант, но конфигурация среды позволяет избежать внедрения секрета в исходный код.
Исключить маршрут веб-перехватчика из защиты сеанса
Запросы 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 руководство по отладке вебхука специально вызывает исключение маршрутов веб-перехватчиков. В более новых версиях Next.js соглашение может использовать proxy.ts; следуйте руководству по версии Clerk, установленному в вашем проекте. Публичный не значит доверенный: verifyWebhook() является обязательным перед любым действием.
Подключите Clerk к локальному хосту
- Бегать
npm run devи убедитесь, что приложение Next.js прослушивает порт 3000. - Бегать
npx portpreview 3000. - На панели инструментов Clerk создайте конечную точку веб-перехватчика с помощью
https://your-subdomain.portpreview.dev/api/webhooks/clerk. - Выберите только необходимого пользователя, сеанс, организацию или события электронной почты.
- Скопируйте секрет подписи конечной точки в
CLERK_WEBHOOK_SIGNING_SECRETв вашей локальной среде и перезапустите Next.js. - Откройте вкладку «Тестирование» конечной точки, выберите
user.createdи выберите «Отправить пример». - Подтвердите, что попытка отмечена успехом, ваш локальный маршрут вернул 200, а ожидаемая строка базы данных изменилась один раз.
URL-адрес туннеля должен оставаться активным для последующих примеров. Если оно изменится, обновите конечную точку. Не используйте повторно секрет подписи рабочей конечной точки для локальных тестов; создавайте конечные точки для конкретной среды, чтобы история ротации и аудита оставалась прозрачной.
Тщательно моделируйте синхронизацию пользователей
Используйте идентификатор пользователя Clerk в качестве внешнего ключа.
Магазин user_... в уникальном clerk_user_id столбец. Upsert на этом ключе, поэтому повторная попытка user.created сходится, а не терпит неудачу. Сохраните свой собственный внутренний первичный ключ, если на него уже ссылаются другие таблицы.
Осознанно выбирайте основной адрес электронной почты
Пользовательские данные Clerk содержат записи адресов электронной почты и идентификатор основного адреса электронной почты. Разрешите первичную запись по идентификатору вместо того, чтобы брать первый элемент массива. Электронная почта может измениться; не используйте его в качестве неизменяемого внешнего ключа.
Решите, что означает удаление
А user.deleted событие может содержать меньше данных, чем создание или обновление. Используйте идентификатор для анонимизации, обратимого удаления или запуска рабочего процесса хранения в соответствии с вашей политикой. Слепое каскадное удаление может привести к уничтожению записей о выставлении счетов или аудита, которые требуют сохранения нормативные акты.
Не отражайте все
Сохраняйте только поля, необходимые вашему приложению. Каждое скопированное поле профиля создает обязательства по обеспечению конфиденциальности, хранения и устаревания. Fetch редко использовал данные Clerk по требованию, а не дублировал всю полезную нагрузку.
Сделайте повторные попытки и заказ безвредными
Clerk документирует, что ответ, отличный от 2xx, вызывает повторную попытку события. Перед применением эффектов запишите идентификатор сообщения веб-перехватчика из подписанных метаданных или заголовков веб-перехватчика как уникальное подтверждение. Если ваш SDK предоставляет идентификаторы стандартных веб-перехватчиков в заголовках, сохраните их вместе с типом события и меткой времени. Верните 200 за готовый дубликат.
Для обновлений пользователей сравните временные метки событий или используйте правила последней записи, которые не позволяют более старому событию перезаписывать новые данные профиля. Для состояния с высоким значением извлеките текущего пользователя из Clerk после проверки и рассматривайте веб-перехватчик как сигнал для согласования. Храните вставку квитанции, обновление пользователя и задание исходящей почты в одной транзакции базы данных.
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 });
});
Этот шаблон предотвращает дублирование приветственных писем и частичную запись. Видеть Повторные попытки вебхука и идемпотентность для вариантов схемы.
Устранение неполадок с доставкой местным Clerkом
404, редирект или HTML вместо вашего обработчика
Проверьте путь к файлу, экспорт POST и полный URL-адрес туннеля. Проверьте перезапись промежуточного программного обеспечения и локали. Вебхук не должен проходить через перенаправление входа в систему или проверку формы CSRF. Проверьте общедоступный URL-адрес с помощью базового POST; ожидайте отклонения подписи от вашего маршрута, а не от фреймворка 404.
verifyWebhook() всегда бросает
Перезапустите Next.js после установки секрета подписи. Убедитесь, что секрет принадлежит этой конечной точке и среде. Не анализируйте, не клонируйте неправильно и не используйте тело запроса перед передачей его помощнику. Убедитесь, что в туннеле сохранены заголовки сигнатур Standard Webhooks.
На панели мониторинга отображаются повторные попытки
Посмотрите на точный ответ на попытку. Возвращайте 2xx только после длительного принятия, но продолжайте обработку ниже тайм-аута поставщика. Миграция базы данных, ошибки уникальных ограничений и синхронный вызов недоступной службы — вот распространенные причины 500.
Строки дублируются или устарели.
Добавьте уникальные ограничения для идентификатора Clerk и идентификатора сообщения веб-перехватчика. Делать user.created и user.updated оба безопасных обновления, а затем защищаются от старых временных меток событий. Повторяйте пример после каждого исправления, чтобы доказать дублирующую обработку.
Контрольный список безопасности
- Проверяйте каждый запрос с помощью помощника Clerk, прежде чем регистрировать поля полезной нагрузки или записывать данные.
- Сохраняйте маршрут не аутентифицированным промежуточным программным обеспечением пользовательского сеанса, но защищенным подписью веб-перехватчика.
- Используйте отдельные секреты конечных точек для локальной, предварительной, промежуточной и производственной сред.
- Дедублируйте идентификаторы подписанных сообщений и ограничивайте уникальные идентификаторы пользователей.
- Удалите электронную почту, телефон, токены, секреты и полные полезные данные из обычных журналов.
- При необходимости добавьте документированные средства управления IP-адресами Clerk/Svix в качестве глубокоэшелонированной защиты, но никогда не заменяйте проверку подписи IP-фильтрацией.
- Ограничьте скорость недействительного трафика, ограничьте размер тела и поменяйте секрет, если он появляется в журналах или системе управления версиями.
Clerk обзор вебхуков объясняет проверку подписи и дополнительные ограничения IP Svix. Для изучения основ необработанного тела Next.js и поведения маршрутов продолжайте Руководство по веб-перехватчику Next.js localhost.
