Authentifizierung – Übersicht

Wie Clerk-Authentifizierung im nextsaas.ai Kit integriert ist, mit umgebungsbewusstem Routing und Webhook-Synchronisierung

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.

Wie Authentifizierung funktioniert

Das Authentifizierungssystem verbindet vier Schichten, die zusammenarbeiten, um deine Anwendung zu schützen:
  1. 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.
  2. ClerkProvider umschließt deine Anwendung und macht den Auth-Status für alle Komponenten verfügbar – sowohl Server- als auch Client-Komponenten.
  3. Auth-Hooks und Helpers ermöglichen den Zugriff auf den aktuellen Benutzer in Server Components (getServerAuth()) und Client Components (useConditionalAuth()).
  4. 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 ClerkProvider vollstä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:
RouteZweckClerk-Routing
/loginAnmeldeseiterouting="hash" rendert <SignIn />
/registerRegistrierungsseiterouting="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.

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.
ModusWann aktivVerhalten
ProduktionVERCEL_ENV=productionEchte Clerk-Authentifizierung. Kann nicht überschrieben werden.
Test / CINODE_ENV=test oder CLERK_ENABLED=falseMock-Auth mit Test-Benutzer. Clerk-SDK wird nicht geladen.
DemoNEXT_PUBLIC_DEMO_MODE=trueDemo-Login-Formular mit Tier-Wechsel. Keine echte Auth.
EntwicklungStandardVerwendet 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:
  1. Demo-Modus (Priorität 0) — Umgeht Clerk immer, auch in der Produktion. Dies ermöglicht öffentliche Demo-Deployments ohne echte Authentifizierung.
  2. Produktion (Priorität 1) — Verwendet Clerk immer. Das ist eine Sicherheitsmaßnahme – Produktion kann nie versehentlich im Test-Modus laufen.
  3. Test-Umgebung (Priorität 2) — Verwendet Clerk nie. Wird über NEXT_PUBLIC_CLERK_ENABLED=false oder NODE_ENV=test erkannt.
  4. Entwicklung (Priorität 3) — Verwendet Clerk nur, wenn NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY gesetzt 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

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>
}

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:
EreignisAktion
user.createdErstellt einen Datenbankdatensatz mit Free-Tier, anfänglichen Credits (500) und einem 30-Tage-Rücksetzdatum
user.updatedAktualisiert E-Mail und Name in der Datenbank
user.deletedEntfernt 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

DateiZweck
apps/boilerplate/src/lib/auth/config.tsZentrale Umgebungserkennung (shouldUseClerk(), authConfig, testUser)
apps/boilerplate/src/lib/auth/server-helpers.tsServerseitige Auth-Helpers (getServerAuth(), getServerUser())
apps/boilerplate/src/lib/auth/use-conditional-auth.tsClientseitige konditionale Hooks (useConditionalAuth(), useConditionalUser())
apps/boilerplate/src/lib/auth/sync-user.tsOn-Demand-Synchronisierung von Clerk zur Datenbank (syncUserFromClerk(), ensureUserExists())
apps/boilerplate/src/lib/auth/test-helpers.tsxMock-Auth-Komponenten und Hooks für Tests
apps/boilerplate/src/middleware.tsRoutenschutz mit Clerk-Middleware
apps/boilerplate/src/app/layout.tsxRoot-Layout mit konditionalem ClerkProvider
apps/boilerplate/src/app/(auth)/layout.tsxAuth-Seiten-Layout mit Error Boundary
apps/boilerplate/src/components/auth/auth-form.tsxUniverselles Anmelde-/Registrierungsformular (funktioniert in allen Umgebungen)
apps/boilerplate/src/app/api/webhooks/clerk/route.tsClerk-Webhook-Handler für die Datenbanksynchronisierung
apps/boilerplate/src/providers/db-user-provider.tsxReact-Kontext, der dem Dashboard die Datenbankbenutzer-ID bereitstellt