Kit verwendet Clerk für die Authentifizierung – eine gehostete Identity-Plattform, die Registrierung, Anmeldung, Social Logins, Multi-Faktor-Authentifizierung und Benutzerverwaltung übernimmt. Clerk ist tief in den Boilerplate integriert, von Middleware-basiertem Routenschutz bis zur automatischen Datenbanksynchronisierung über Webhooks.
Diese Seite erklärt die Architektur. Wenn du Clerk in deinem Projekt einrichten möchtest, gehe zu Setup & Anbieter.
Kit funktioniert ohne Clerk während der Entwicklung. Führe
pnpm dev:no-clerk aus, um mit einem Mock-Authentifizierungssystem zu starten. Du kannst das gesamte Dashboard erkunden, ohne vorher ein Clerk-Konto anzulegen.Wie Authentifizierung funktioniert
Das Authentifizierungssystem verbindet vier Schichten, die zusammenarbeiten, um deine Anwendung zu schützen:
- Middleware fängt jede Anfrage ab und prüft, ob die Route öffentlich oder geschützt ist. Geschützte Routen erfordern eine gültige Clerk-Sitzung.
- ClerkProvider umschließt deine Anwendung und macht den Auth-Status für alle Komponenten verfügbar – sowohl Server- als auch Client-Komponenten.
- Auth-Hooks und Helpers ermöglichen den Zugriff auf den aktuellen Benutzer in Server Components (
getServerAuth()) und Client Components (useConditionalAuth()). - Webhooks halten deine Datenbank mit Clerk synchron. Wenn sich ein Benutzer registriert, löst Clerk einen Webhook aus, der den entsprechenden Datenbankdatensatz mit einem Free-Tier und anfänglichen Credits anlegt.
Request Flow:
Browser Request
|
v
Middleware (middleware.ts)
|--- Public route? ---> Allow through (auth state still available)
|--- Protected route? --> Clerk auth.protect() --> Redirect to /login if unauthenticated
|
v
ClerkProvider (layout.tsx)
|--- Production: Real Clerk SDK
|--- Test/Demo: Bypassed (no ClerkProvider)
|
v
Route Component
|--- Server Component: getServerAuth() / currentUser()
|--- Client Component: useConditionalAuth() / useConditionalUser()
|
v
Database (via webhook sync)
|--- user.created --> prisma.user.upsert (Free Tier + credits)
|--- user.updated --> prisma.user.update (email, name)
|--- user.deleted --> prisma.user.delete
ClerkProvider-Integration
Der
ClerkProvider umschließt die gesamte Anwendung im Root-Layout. Er wird bedingt gerendert, je nachdem, ob Clerk in der aktuellen Umgebung aktiv sein soll:src/app/layout.tsx
// Production/Development: Wrap with ClerkProvider
// Test: Return content directly without ClerkProvider
return useClerk ? (
<ClerkProvider
dynamic
appearance={{
elements: {
formButtonPrimary: 'bg-primary hover:bg-primary/90',
footerActionLink: 'text-primary hover:text-primary/90',
},
}}
>
{content}
</ClerkProvider>
) : (
content
)
}
Zwei Dinge sind hier zu beachten:
- Das
dynamic-Prop verhindert Hydration-Fehler beim Next.js-Streaming. Ohne es kann der Clerk-Sitzungstoken zwischen Server- und Client-Rendering abweichen und zu einem Mismatch führen. - Im Test- und Demo-Modus wird der
ClerkProvidervollständig weggelassen. Komponenten erhalten stattdessen Mock-Auth-Daten, und das Clerk-SDK wird nie gebündelt.
Hash-Routing
Kit verwendet Clerks hash-basiertes Routing für Authentifizierungsseiten:
| Route | Zweck | Clerk-Routing |
|---|---|---|
/login | Anmeldeseite | routing="hash" rendert <SignIn /> |
/register | Registrierungsseite | routing="hash" rendert <SignUp /> |
Hash-Routing (
/login#/factor-one, /register#/verify-email) hält mehrstufige Auth-Flows auf einer einzigen Next.js-Seite, ohne zusätzliche Route-Handler zu benötigen. Clerk verwaltet die Schrittübergänge intern über URL-Hash-Fragmente.Die Auth-URLs werden über Umgebungsvariablen konfiguriert:
bash
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/login
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/register
NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/dashboard
NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/dashboard
Vorkonfigurierte Auth-Flows
Kit wird mit drei sofort nutzbaren Authentifizierungs-Flows geliefert:
Registrierung — Neue Benutzer registrieren sich unter
/register. Clerk übernimmt E-Mail-Verifizierung, Passwortanforderungen und optionalen Social Login. Nach erfolgreicher Registrierung werden Benutzer zu /dashboard weitergeleitet. Ein Clerk-Webhook löst user.created aus, der den Datenbankdatensatz mit einem Free-Tier initialisiert.Anmeldung — Wiederkehrende Benutzer melden sich unter
/login an. Clerk unterstützt E-Mail/Passwort, Social Login (Google, GitHub) und Multi-Faktor-Authentifizierung. Nach der Anmeldung landen Benutzer auf /dashboard.Passwort zurücksetzen — In Clerks Anmelde-Flow integriert. Benutzer klicken auf „Passwort vergessen?" auf der Login-Seite und erhalten eine Reset-E-Mail. Keine eigene Implementierung notwendig.
Die Registrierungsseite unterstützt Checkout-Integration. Wenn ein Benutzer
/register?tier=pro&billing=monthly aufruft, wird er nach abgeschlossener Registrierung automatisch zum Zahlungs-Checkout weitergeleitet.Umgebungsbewusste Architektur
Das Auth-System von Kit arbeitet in drei Modi, die automatisch anhand von Umgebungsvariablen bestimmt werden. Du musst den Anwendungscode nie ändern, wenn du zwischen Entwicklung, Test und Produktion wechselst.
| Modus | Wann aktiv | Verhalten |
|---|---|---|
| Produktion | VERCEL_ENV=production | Echte Clerk-Authentifizierung. Kann nicht überschrieben werden. |
| Test / CI | NODE_ENV=test oder CLERK_ENABLED=false | Mock-Auth mit Test-Benutzer. Clerk-SDK wird nicht geladen. |
| Demo | NEXT_PUBLIC_DEMO_MODE=true | Demo-Login-Formular mit Tier-Wechsel. Keine echte Auth. |
| Entwicklung | Standard | Verwendet Clerk, wenn API-Keys vorhanden sind, sonst Mock-Auth. |
Die shouldUseClerk()-Funktion
Die gesamte Umgebungserkennung ist in einer einzigen Funktion zentralisiert. Jeder Teil der Anwendung – Middleware, Layouts, Komponenten, API-Routen – ruft
shouldUseClerk() auf, anstatt Umgebungsvariablen direkt zu prüfen:src/lib/auth/config.ts
export function shouldUseClerk(): boolean {
// Priority 0: Demo Mode always bypasses Clerk (even in production)
// This allows public demo deployments without real auth
if (process.env.NEXT_PUBLIC_DEMO_MODE === 'true') {
return false
}
// Priority 1: Never use test auth in production (safety first)
if (isProduction()) {
return true
}
// Priority 2: Use test auth in test environments (explicit disable)
if (isTestEnvironment()) {
return false
}
// Priority 3: In development, use Clerk if credentials are available
return hasClerkCredentials()
}
Entscheidungspriorität
Die Funktion wertet Bedingungen in einer festen Reihenfolge aus:
- Demo-Modus (Priorität 0) — Umgeht Clerk immer, auch in der Produktion. Dies ermöglicht öffentliche Demo-Deployments ohne echte Authentifizierung.
- Produktion (Priorität 1) — Verwendet Clerk immer. Das ist eine Sicherheitsmaßnahme – Produktion kann nie versehentlich im Test-Modus laufen.
- Test-Umgebung (Priorität 2) — Verwendet Clerk nie. Wird über
NEXT_PUBLIC_CLERK_ENABLED=falseoderNODE_ENV=testerkannt. - Entwicklung (Priorität 3) — Verwendet Clerk nur, wenn
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEYgesetzt ist. Fehlt der Key, fällt er elegant auf Mock-Auth zurück.
Zur Fehlersuche bietet das
authConfig-Objekt einen Snapshot der aktuellen Konfiguration:src/lib/auth/config.ts
export const authConfig = {
isTest: isTestEnvironment(),
isProd: isProduction(),
hasKeys: hasClerkCredentials(),
useClerk: shouldUseClerk(),
// Helper for debugging
debug() {
console.log('Auth Config:', {
NODE_ENV: process.env.NODE_ENV,
CLERK_ENABLED: process.env.NEXT_PUBLIC_CLERK_ENABLED,
VERCEL_ENV: process.env.VERCEL_ENV,
HAS_PUBLISHABLE_KEY: !!process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY,
USE_CLERK: this.useClerk,
})
},
}
Auth-Status abfragen
Kit stellt unterschiedliche APIs bereit, um den Authentifizierungsstatus abzufragen – je nachdem, ob du dich in einer Server Component oder einer Client Component befindest.
Serverseitig
Das direkte Aufrufen von
auth() aus @clerk/nextjs/server löst einen Fehler aus, wenn Clerk deaktiviert ist (Demo-Modus, Test-Modus). Das Clerk-SDK initialisiert sich beim Import, daher schlägt bereits ein statisches import { auth } from '@clerk/nextjs/server' am Anfang einer Datei fehl. Verwende stattdessen immer getServerAuth() – es nutzt dynamische Imports und gibt Mock-Daten zurück, wenn Clerk nicht aktiv ist.In Server Components, API-Routen und Server Actions verwende den
getServerAuth()-Helper. Er importiert Clerks auth()-Funktion in der Produktion dynamisch und gibt im Test-Modus Mock-Daten zurück – verhindert so, dass das Clerk-SDK gebündelt wird, wenn es nicht benötigt wird:src/lib/auth/server-helpers.ts
export async function getServerAuth() {
// Test mode: Return mock auth data
if (!shouldUseClerk()) {
return {
userId: testUser.id,
sessionId: 'test-session-123',
sessionClaims: null,
actor: null,
orgId: null,
orgRole: null,
orgSlug: null,
orgPermissions: null,
has: () => false,
debug: () => null,
}
}
// Production mode: Use real Clerk auth with dynamic import
try {
const { auth } = await import('@clerk/nextjs/server')
return await auth()
} catch (error) {
console.error('[Server Auth] Failed to load Clerk auth:', error)
throw new Error('Authentication unavailable')
}
}
Es gibt auch
getServerUser() zum Abrufen des vollständigen Benutzerobjekts (E-Mail, Name, Avatar) auf dem Server.Verwendung:
typescript
import { getServerAuth, getServerUser } from '@/lib/auth/server-helpers'
// In einer Server Component oder API-Route:
const { userId } = await getServerAuth()
const user = await getServerUser()
Clientseitig
In Client Components verwendest du die konditionalen Hooks aus
@/lib/auth/use-conditional-auth. Diese Wrapper rufen Clerk-Hooks in der Produktion auf und geben im Test-Modus Mock-Daten zurück:typescript
import { useConditionalAuth, useConditionalUser } from '@/lib/auth/use-conditional-auth'
function MyComponent() {
const { userId, isSignedIn } = useConditionalAuth()
const { user } = useConditionalUser()
if (!isSignedIn) return <p>Please sign in</p>
return <p>Hello, {user?.firstName}</p>
}
Verwende immer die konditionalen Hooks (
useConditionalAuth, useConditionalUser) statt direkter Imports aus @clerk/nextjs. Die konditionalen Hooks stellen sicher, dass deine Komponenten in allen Umgebungen funktionieren – Produktion, Test und Demo-Modus.Clerk-zu-Datenbank-Synchronisierung
Clerk verwaltet die Benutzeridentität (E-Mail, Passwort, Social Accounts), aber deine Anwendung benötigt auch Benutzerdaten in der Datenbank für Beziehungen, Abonnements, Credits und Abfragen. Kit überbrückt diese Lücke mit webhook-basierter Synchronisierung.
Wenn Clerk ein Benutzerereignis auslöst, verarbeitet der Webhook-Handler unter
/api/webhooks/clerk dieses:| Ereignis | Aktion |
|---|---|
user.created | Erstellt einen Datenbankdatensatz mit Free-Tier, anfänglichen Credits (500) und einem 30-Tage-Rücksetzdatum |
user.updated | Aktualisiert E-Mail und Name in der Datenbank |
user.deleted | Entfernt den Benutzerdatensatz aus der Datenbank |
Der Webhook verwendet SVIX-Signaturverifizierung, um sicherzustellen, dass Anfragen tatsächlich von Clerk stammen, sowie Upsert-Operationen für Idempotenz (sicher wiederholbar, ohne Duplikate zu erzeugen).
Weitere Details zur Einrichtung des Webhooks findest du unter Setup & Anbieter. Zum Datenbankbenutzer-Provider-Muster siehe Benutzerverwaltung.
Wichtige Dateien – Referenz
| Datei | Zweck |
|---|---|
apps/boilerplate/src/lib/auth/config.ts | Zentrale Umgebungserkennung (shouldUseClerk(), authConfig, testUser) |
apps/boilerplate/src/lib/auth/server-helpers.ts | Serverseitige Auth-Helpers (getServerAuth(), getServerUser()) |
apps/boilerplate/src/lib/auth/use-conditional-auth.ts | Clientseitige konditionale Hooks (useConditionalAuth(), useConditionalUser()) |
apps/boilerplate/src/lib/auth/sync-user.ts | On-Demand-Synchronisierung von Clerk zur Datenbank (syncUserFromClerk(), ensureUserExists()) |
apps/boilerplate/src/lib/auth/test-helpers.tsx | Mock-Auth-Komponenten und Hooks für Tests |
apps/boilerplate/src/middleware.ts | Routenschutz mit Clerk-Middleware |
apps/boilerplate/src/app/layout.tsx | Root-Layout mit konditionalem ClerkProvider |
apps/boilerplate/src/app/(auth)/layout.tsx | Auth-Seiten-Layout mit Error Boundary |
apps/boilerplate/src/components/auth/auth-form.tsx | Universelles Anmelde-/Registrierungsformular (funktioniert in allen Umgebungen) |
apps/boilerplate/src/app/api/webhooks/clerk/route.ts | Clerk-Webhook-Handler für die Datenbanksynchronisierung |
apps/boilerplate/src/providers/db-user-provider.tsx | React-Kontext, der dem Dashboard die Datenbankbenutzer-ID bereitstellt |