NextAuth.js Einstieg: Credentials-Login und Session-Management – Komplett-Guide

Wenn Sie die NextAuth.js-Dokumentation öffnen, wirken die unzähligen Konfigurationsoptionen überwältigend. Provider, Session, Adapter, JWT, Callbacks – jedes Wort kennen Sie, zusammen ergibt sich trotzdem kein klares Bild. Die Doku sagt „Standard ist JWT“, im nächsten Absatz „bei Datenbanknutzung wechselt es automatisch zu Session“ – welche Option nehmen Sie also?
Damals war ich kurz davor aufzugeben und einfach Clerk zu nehmen. Aber Clerk ist zwar schnell, ab 10.000 monatlich aktiven Nutzern wird es kostenpflichtig – und viele Anpassungen sind eingeschränkt. NextAuth.js hat eine steilere Lernkurve, ist aber kostenlos und vollständig unter Ihrer Kontrolle.
Dieser Artikel listet nicht alle Konfigurationsoptionen auf (das wäre ermüdend), sondern konzentriert sich auf das häufigste Szenario: Credentials-Login – Benutzername/Passwort, Session-Management, Datenbankintegration. Wenn Sie diese Kernpunkte verstanden haben, ist NextAuth.js deutlich weniger abschreckend.
Die Kernkonzepte von NextAuth.js verstehen
Was macht NextAuth.js eigentlich?
Kurz gesagt: NextAuth.js ist eine Authentifizierungs-Middleware, die verwaltet, wer angemeldet ist und wie der Login-Status gespeichert wird. Welche Datenbank Sie nutzen oder wie Ihre UI aussieht, ist Nebensache – NextAuth.js prüft Identität und hält den Login-Status.
Unterschied zu Clerk und Supabase Auth:
- Clerk: Schöne Login-UI, Benutzerverwaltung, in 30 Minuten einsatzbereit – kostenpflichtig ab 10.000 monatlich aktiven Nutzern
- Supabase Auth: Wenn Sie Supabase als DB nutzen, ist Auth praktisch inklusive und sehr gut integriert
- NextAuth.js: Komplett kostenlos und Open Source – Login-UI, Registrierung und DB-Logik bauen Sie selbst
2025 ist die Lage interessant: NextAuth.js wird weiterhin viel genutzt, aber „out of the box“-Lösungen wie Clerk gewinnen Marktanteile. Verständlich – Tage in Auth-Code zu investieren, statt Kernfeatures zu bauen, will kaum jemand. Für persönliche Projekte oder volle Kontrolle lohnt sich NextAuth.js aber weiterhin.
Drei Konzepte, die Sie verstehen müssen
1. Provider: Wie melden sich Nutzer an?
Das ist die Login-Methode. NextAuth.js unterstützt über 50 Varianten, die häufigsten:
- OAuth-Provider: Google, GitHub, Facebook – ein Klick, kein Passwort-Management auf Ihrer Seite
- Credentials-Provider: Benutzername + Passwort – klassisch und Schwerpunkt dieses Artikels
Ein wichtiger Hinweis: Credentials ist am flexibelsten, erfordert aber den meisten eigenen Code. Offiziell wird es sogar nicht empfohlen – Sicherheitsrisiken (Passwort-Hashing, Brute-Force-Schutz, Session-Handling) liegen bei Ihnen.
2. Session: Wie merkt sich die App den Login?
Nach einmaligem Login soll niemand bei jedem Request das Passwort erneut eingeben. Session ist der Mechanismus dafür – zwei Varianten:
- JWT Session: Login-Infos verschlüsselt im Cookie, Server speichert nichts
- Database Session: Cookie enthält nur eine ID, echte Session-Daten liegen in der DB
Die Wahl zwischen beiden verwirrt Anfänger am meisten – im nächsten Abschnitt geht es im Detail darum.
3. Adapter: Wo liegen Benutzerdaten?
Wenn Sie Nutzerdaten (E-Mail, Registrierungszeit usw.) in einer DB speichern wollen, brauchen Sie einen Adapter. NextAuth.js unterstützt Prisma, MongoDB, MySQL und mehr.
Wichtig: Der Credentials-Provider legt keine Benutzerkonten automatisch in der DB an. Laut Doku verwalten Sie Accounts selbst – NextAuth.js prüft nur beim Login, nicht Registrierung oder Speicherung.
JWT vs. Session: Was soll ich wählen?
Das ist die zentrale Frage. Nach vielen Vergleichsartikeln wurde mir erst klar, worin der Unterschied wirklich liegt.
JWT Session: Pass-Modus
Stellen Sie sich Reisen ins Ausland vor: Im Pass stehen Foto, Name, Gültigkeit. Bei der Einreise reicht der Pass – kein Abgleich mit einem zentralen System. JWT funktioniert ähnlich: Nach dem Login erzeugt der Server ein verschlüsseltes Token mit userId, E-Mail usw. und legt es im Browser-Cookie ab.
Vorteile:
- Schnell – keine DB-Abfrage, Entschlüsseln reicht
- Günstig – keine Session-Tabelle, ideal für Serverless
- Skalierbar – keine Session-Tabelle, die bei vielen Nutzern explodiert
Nachteile:
- Kein erzwungenes Abmelden – Token gültig bis Ablauf (Blacklist wäre wieder DB)
- Kein Limit gleichzeitiger Geräte (z. B. „max. 3 Geräte“)
- Token-Inhalt bis Ablauf statisch – geänderte Rollen wirken erst nach Neu-Login
Database Session: Hotel-Schlüsselkarten-Modus
Die Karte trägt nur die Zimmernummer; Details liegen im Hotelsystem. Bei jedem Scan prüft das System die Berechtigung. Database Session: Cookie mit Session-ID, Nutzerdaten in der DB.
Vorteile:
- Login jederzeit widerrufbar – „Alle Geräte abmelden“ = DB-Einträge löschen
- Gerätelimits möglich
- Nutzerinfos live aktualisierbar (z. B. geänderte Rolle beim nächsten Request)
Nachteile:
- Jede Anfrage trifft die DB
- Session-Tabelle pflegen (anlegen, abgelaufene löschen)
- Bei hoher Last mehr DB-Druck
Entscheidungsbaum
Brauchen Sie erzwungenes Neu-Anmelden (Passwort geändert, Konto gesperrt)?
Brauchen Sie „Nutzer zum Neu-Anmelden zwingen“? (Passwort geändert, Konto gesperrt)
├─ Ja → Database Session
└─ Nein → weiter
Wollen Sie DB sparen oder Serverless deployen?
├─ Ja → JWT
└─ Nein → weiter
Persönliches Projekt/MVP, schnell live?
├─ Ja → JWT (einfach)
└─ Nein → Database Session (Unternehmen, sicherer)
Mein erstes Projekt nutzte JWT; mit Admin-Backend und „Nutzer kicken“ wechselte ich zu Database Session – der Umstieg war aufwendig. Lieber von Anfang an durchdenken.
Sonderfall: Credentials + Database Session
Credentials plus Database Session ist laut offizieller Doku nicht unterstützt. OAuth (Google, GitHub) funktioniert nahtlos; bei Credentials erwartet NextAuth.js keine automatische Session-Erstellung.
Workarounds existieren (manuell in signIn callback), siehe diese Diskussion. Als Einsteiger: JWT oder OAuth – nicht unnötig komplizieren.
Credentials Provider – vollständige Konfiguration
Theorie reicht – jetzt Code. Drei Stufen: minimal, Konfiguration im Detail, vollständiges Beispiel.
Basis: Minimal lauffähige Version
Abhängigkeiten:
npm install next-auth
Datei anlegen. Next.js 13+ App Router: app/api/auth/[...nextauth]/route.ts; Pages Router: pages/api/auth/[...nextauth].js. Hier App Router.
app/api/auth/[…nextauth]/route.ts:
import NextAuth from "next-auth"
import CredentialsProvider from "next-auth/providers/credentials"
const handler = NextAuth({
providers: [
CredentialsProvider({
name: 'Credentials',
credentials: {
email: { label: "E-Mail", type: "email" },
password: { label: "Passwort", type: "password" }
},
async authorize(credentials) {
// Hardcodierter Testnutzer
if (credentials?.email === "[email protected]" &&
credentials?.password === "123456") {
return {
id: "1",
name: "Testnutzer",
email: "[email protected]"
}
}
return null // Login fehlgeschlagen
}
})
],
session: {
strategy: "jwt" // JWT-Session
},
pages: {
signIn: '/login' // Eigene Login-Seite (optional)
}
})
export { handler as GET, handler as POST }
Umgebungsvariablen .env.local:
NEXTAUTH_SECRET=your-super-secret-key-change-this
NEXTAUTH_URL=http://localhost:3000
Wichtig:
NEXTAUTH_SECRET: Verschlüsselt Tokens – in Produktion Pflicht, lokal empfohlen. Generieren:openssl rand -base64 32authorize: Kern der Validierung – Benutzerobjekt = Erfolg,null= Fehler
Läuft, aber Nutzer sind hardcodiert – als Nächstes DB-Anbindung.
Wichtige Konfigurationsoptionen im Detail
1. session.strategy: „jwt“ oder „database“?
Standard ist "jwt". Mit Adapter wechselt es zu "database". Bei Credentials + JWT explizit strategy: "jwt" setzen, sonst Fehler.
2. callbacks: Eigene Felder in der Session
Standard liefert useSession nur name, email, image. Für userId oder role brauchen Sie callbacks:
callbacks: {
async jwt({ token, user }) {
// user nur beim Login gesetzt
if (user) {
token.userId = user.id
}
return token
},
async session({ session, token }) {
session.user.userId = token.userId
return session
}
}
Dann hat session.user.userId einen Wert.
3. pages: Eigene Login-Seite
Die Standardseite unter /api/auth/signin ist funktional, aber unschön. Eigene UI: pages: { signIn: '/login' }.
Login-Seite:
import { signIn } from "next-auth/react"
const handleSubmit = async (e) => {
e.preventDefault()
const result = await signIn('credentials', {
redirect: false,
email,
password
})
if (result?.error) {
// Login fehlgeschlagen
} else {
// Erfolg, weiterleiten
}
}
Vollständiges Beispiel: Registrierung + Login + Session
Prisma + PostgreSQL angenommen.
1. Benutzertabelle
prisma/schema.prisma:
model User {
id String @id @default(cuid())
email String @unique
password String
name String?
createdAt DateTime @default(now())
}
npx prisma migrate dev ausführen.
2. Registrierungs-API
app/api/register/route.ts:
import { NextResponse } from "next/server"
import bcrypt from "bcryptjs"
import { prisma } from "@/lib/prisma"
export async function POST(req: Request) {
try {
const { email, password, name } = await req.json()
const existingUser = await prisma.user.findUnique({
where: { email }
})
if (existingUser) {
return NextResponse.json(
{ error: "E-Mail bereits registriert" },
{ status: 400 }
)
}
const hashedPassword = await bcrypt.hash(password, 10)
const user = await prisma.user.create({
data: {
email,
password: hashedPassword,
name
}
})
return NextResponse.json({
user: {
id: user.id,
email: user.email,
name: user.name
}
})
} catch (error) {
return NextResponse.json(
{ error: "Registrierung fehlgeschlagen" },
{ status: 500 }
)
}
}
Wichtig: Passwörter hashen – bcrypt oder argon2, niemals Klartext.
3. NextAuth mit Datenbank
app/api/auth/[...nextauth]/route.ts:
import NextAuth from "next-auth"
import CredentialsProvider from "next-auth/providers/credentials"
import bcrypt from "bcryptjs"
import { prisma } from "@/lib/prisma"
const handler = NextAuth({
providers: [
CredentialsProvider({
credentials: {
email: { label: "E-Mail", type: "email" },
password: { label: "Passwort", type: "password" }
},
async authorize(credentials) {
if (!credentials?.email || !credentials?.password) {
return null
}
const user = await prisma.user.findUnique({
where: { email: credentials.email }
})
if (!user) {
return null
}
const isValid = await bcrypt.compare(
credentials.password,
user.password
)
if (!isValid) {
return null
}
return {
id: user.id,
email: user.email,
name: user.name
}
}
})
],
session: {
strategy: "jwt",
maxAge: 30 * 24 * 60 * 60 // 30 Tage
},
callbacks: {
async jwt({ token, user }) {
if (user) {
token.userId = user.id
}
return token
},
async session({ session, token }) {
session.user.userId = token.userId as string
return session
}
},
pages: {
signIn: '/login'
}
})
export { handler as GET, handler as POST }
4. Session in der App nutzen
Server Component:
import { getServerSession } from "next-auth"
export default async function ProfilePage() {
const session = await getServerSession()
if (!session) {
redirect('/login')
}
return <div>Willkommen, {session.user.name}</div>
}
Client Component:
'use client'
import { useSession } from "next-auth/react"
export default function Dashboard() {
const { data: session, status } = useSession()
if (status === "loading") {
return <div>Laden…</div>
}
if (!session) {
return <div>Bitte anmelden</div>
}
return <div>Ihre Benutzer-ID: {session.user.userId}</div>
}
Hinweis:
- Server:
getServerSession() - Client:
useSession()mit'use client' - Client-Teil in
<SessionProvider>(typisch im Layout)
Session-Strategien und häufige Probleme
JWT-Session richtig nutzen
1. Zusatzinfos im JWT (userId, role)
jwt-Callback füllt das Token, session-Callback mappt auf die Session.
Rollen-Beispiel:
callbacks: {
async jwt({ token, user }) {
if (user) {
token.userId = user.id
token.role = user.role
}
return token
},
async session({ session, token }) {
session.user.userId = token.userId as string
session.user.role = token.role as string
return session
}
}
2. Token-Ablauf
Standard 30 Tage, anpassbar:
session: {
strategy: "jwt",
maxAge: 7 * 24 * 60 * 60 // 7 Tage
}
Hinweis: Token bleibt nach Browser-Schließen (bis manuell abgemeldet). „Beim Schließen abmelden“ ist clientseitig – JWT allein reicht nicht.
3. JWT-Limit: kein sofortiger Widerruf
Kompromittiertes Konto sofort sperren? Solange das Token gültig ist, geht das nicht.
Alternativen:
- Kurze Laufzeit (z. B. 1 Stunde)
- Token-Blacklist (wieder DB nötig)
- Database Session
Database Session (inkl. Credentials)
Mit OAuth (Google, GitHub) ist Database Session einfach:
npm install @next-auth/prisma-adapter
import { PrismaAdapter } from "@next-auth/prisma-adapter"
import { prisma } from "@/lib/prisma"
const handler = NextAuth({
adapter: PrismaAdapter(prisma),
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!
})
]
// strategy wird automatisch „database“
})
Tabellen User, Session, Account entstehen automatisch.
Credentials + Database Session unterstützt die Doku nicht. Workarounds über manuelle Session-Erstellung im signIn-Callback – für Einsteiger riskant. Diskussionen:
Alternativ Clerk oder Supabase Auth.
Die fünf häufigsten Fallstricke
1. Fehlendes NEXTAUTH_SECRET in Produktion
Lokal optional (mit Warnung), auf Vercel/Railway ohne Variable → 500. Generieren: openssl rand -base64 32, als Env-Variable setzen.
2. Credentials + database session
Adapter + Credentials → Fehler. Entweder session: { strategy: "jwt" } oder manuelle Session-Logik.
3. useSession in Server Components
App Router = Server Components standardmäßig. useSession() dort → Fehler. Server: getServerSession().
4. Cookie-Domain / Cross-Origin
localhost:3000 vs. example.com – falsche Domain, Session weg. NEXTAUTH_URL in Produktion korrekt setzen, nicht hardcoden.
5. JWEDecryptionFailed
Ursache: NEXTAUTH_SECRET geändert, alter Token im Browser. Lösung: Cookies löschen oder neu anmelden.
Bonus: Routen mit Middleware schützen
middleware.ts:
export { default } from "next-auth/middleware"
export const config = {
matcher: ["/dashboard/:path*", "/profile/:path*"]
}
/dashboard und /profile erfordern Login.
Projektempfehlungen und nächste Schritte
Welche Lösung passt?
Szenario 1: Persönliches Projekt / MVP / Blog
- Empfehlung: NextAuth.js + JWT + Credentials
- Grund: Kostenlos, einfach, keine Session-Tabelle
- Nachteil: Spätere Features wie „Kick“ erfordern Migration
Szenario 2: Unternehmen / SaaS (mit Budget)
- Empfehlung: Clerk
- Grund: Schnell, gute UI, Benutzerverwaltung – spart 40–80 Entwicklungsstunden
- Nachteil: Kosten ab 10.000 MAU, weniger Flexibilität
- Ergänzung: Mit Supabase-DB ist Supabase Auth attraktiv (50.000 MAU free tier)
Szenario 3: Unternehmen (ohne Budget / volle Kontrolle)
- Empfehlung: NextAuth.js + Database Session + OAuth (Google/GitHub)
- Grund: Kostenlos, widerrufbare Sessions
- Nachteil: Eigene Login-UI und DB-Pflege
Szenario 4: Bestehendes Usersystem, nur Auth-Schicht
- Empfehlung: NextAuth.js + Credentials + JWT
- Grund: Flexibel, bestehende DB-Struktur bleibt
- Achtung: Sicherheit (Hashing, Brute-Force) selbst umsetzen
Meine Erfahrung: Erstes Projekt JWT, später zwei Tage Migration zu Database Session. Beim zweiten Projekt von Anfang an richtig gewählt.
Weiterführende Themen
1. OAuth-Provider (oft einfacher)
Google/GitHub-Login: weniger Passwort- und Registrierungs-Code, bessere UX.
2. Middleware
Eine Zeile schützt ganze Verzeichnisse – besser als pro Seite prüfen.
3. RBAC
role in der Session, Inhalte und APIs danach filtern; CASL für feingranulare Rechte.
4. E-Mail-Verifizierung und Passwort-Reset
Email Provider von NextAuth.js oder eigene Reset-Links.
Referenzen
Offizielle Doku:
Tutorials:
GitHub:
Vergleich:
Fazit
NextAuth.js reduziert sich auf drei Entscheidungen:
- Login-Methode? Credentials oder OAuth – meist ist OAuth (Google/GitHub) einfacher
- Session-Speicher? JWT oder Database – persönliche Projekte JWT, Unternehmen Database
- Validierung? Credentials = eigene Logik, OAuth = Provider
Starten Sie mit dem Minimalbeispiel in diesem Artikel – erst loginfähig, dann erweitern. Nicht alle Optionen auf einmal verstehen wollen.
NextAuth.js hat eine Lernkurve, danach kontrollieren Sie den Auth-Flow vollständig. Schnell geht es mit Clerk; sparen und lernen mit NextAuth.js.
Weiterlesen:
-
Als Nächstes: „Next.js Middleware – Komplett-Guide“ (Routenschutz, A/B-Tests)
-
Für Berechtigungen: „RBAC-Praxis – Berechtigungsdesign“
NextAuth.js Credentials-Login – vollständiger Konfigurationsablauf
Vom Installieren bis zur Implementierung von Benutzername/Passwort-Login und Session-Management
⏱️ Estimated time: 2 hr
- 1
Step 1: NextAuth.js installieren und initialisieren
Abhängigkeiten installieren:
• npm install next-auth
• Datei app/api/auth/[...nextauth]/route.ts anlegen
Grundkonfiguration:
• NEXTAUTH_URL setzen (lokal: http://localhost:3000)
• NEXTAUTH_SECRET setzen (zufällige Zeichenkette generieren)
• providers-Array konfigurieren - 2
Step 2: Credentials Provider konfigurieren
Validierungslogik implementieren:
1. Benutzer in der authorize-Funktion von CredentialsProvider validieren
2. Benutzer aus der Datenbank abfragen (oder hardcodierten Test nutzen)
3. Passwort prüfen (z. B. mit bcrypt)
4. Benutzerobjekt zurückgeben (mit id, name, email usw.)
Hinweis:
• authorize muss ein Benutzerobjekt oder null zurückgeben
• Das zurückgegebene Objekt wird in der Session gespeichert
• Passwortprüfung muss serverseitig erfolgen - 3
Step 3: Session-Strategie wählen (JWT oder Session)
JWT-Strategie (Standard):
• Geeignet für zustandslose Anwendungen
• Session-Infos liegen im JWT-Token
• Keine Datenbank nötig
• Konfiguration: session: { strategy: 'jwt' }
Session-Strategie (Datenbank erforderlich):
• Geeignet, wenn Logins widerrufbar sein müssen
• Session-Infos liegen in der Datenbank
• Adapter nötig (z. B. Prisma, MongoDB)
• Konfiguration: session: { strategy: 'database' } - 4
Step 4: Callbacks für individuelle Abläufe konfigurieren
Häufige Callbacks:
• signIn: steuert, ob Login erlaubt ist
• jwt: JWT-Token-Inhalt anpassen (JWT-Strategie)
• session: Session-Inhalt anpassen
Beispiel:
callbacks: {
async jwt({ token, user }) {
if (user) token.role = user.role
return token
},
async session({ session, token }) {
session.user.role = token.role
return session
}
} - 5
Step 5: Login-Seite und Komponenten erstellen
NextAuth.js-API nutzen:
• signIn('credentials', { username, password }): Login auslösen
• signOut(): Abmelden
• useSession(): aktuelle Session abrufen
• SessionProvider: App umschließen und Session-Kontext bereitstellen
Beispiel:
const { data: session } = useSession()
if (session) {
return <div>Angemeldet: {session.user.name}</div>
} - 6
Step 6: Testen und Debuggen
Testpunkte:
• Korrekte Anmeldedaten führen zum Login
• Falsche Credentials werden abgelehnt
• Session bleibt persistent
• Abmelden löscht die Session
Debug-Methoden:
• Browser-Konsole auf Fehler prüfen
• Server-Logs kontrollieren
• NextAuth.js-Debug-Modus nutzen
• Umgebungsvariablen prüfen
FAQ
Was unterscheidet NextAuth.js von Clerk und Supabase Auth?
• Vollständig kostenlose, selbst gehostete Open-Source-Lösung
• Login-UI und Benutzerverwaltung müssen selbst implementiert werden
• Aber vollständig kontrollierbar
Clerk:
• Komplette UI und Verwaltungsoberfläche
• Ab 10.000 monatlich aktiven Nutzern kostenpflichtig
Supabase Auth:
• Ideal für Projekte mit Supabase-Datenbank
• Einfache Integration, aber an die Datenbank gebunden
JWT oder Session – was soll ich wählen?
Ist Credentials-Login sicher?
1) Passwörter verschlüsselt speichern (z. B. bcrypt)
2) Validierung muss serverseitig erfolgen
3) HTTPS für die Übertragung
4) Passwortstärke-Anforderungen
5) Captcha gegen Brute-Force in Betracht ziehen
Wie passe ich die Login-Seite an?
Dann eine eigene Login-Seite erstellen und signIn('credentials', { username, password }) aufrufen.
Alternativ UI komplett selbst bauen und nur die NextAuth.js-API nutzen.
Wie rufe ich den aktuell angemeldeten Benutzer ab?
const { data: session } = useSession()
Serverseitig getServerSession():
const session = await getServerSession(authOptions)
Das session-Objekt enthält user-Infos (id, name, email usw.).
Wie implementiere ich Rollen und Berechtigungen?
Welche Datenbanken unterstützt NextAuth.js?
10 Min. Lesezeit · Veröffentlicht am: 19. Dez. 2025 · Aktualisiert am: 14. Juli 2026
Next.js Komplettleitfaden
Wenn du über die Suche hier gelandet bist, kommst du am schnellsten weiter, indem du zum vorherigen oder nächsten Beitrag dieser Serie springst.
Vorheriger
React Server Components Performance-Optimierung: Datenabruf und Caching in der Praxis
React Server Components Performance-Optimierung in der Praxis: vom Waterfall-Problem zur Streaming-Architektur – Datenabruf und Caching-Strategien im Detail. Optimierungspfad von TTFB 450 ms auf 45 ms, mit 4 Lösungsvergleichen und 5 Cache-API-Anleitungen
Teil 47 von 51
Nächster
JWT oder Session? Nach diesem Guide ist die Entscheidung klar
JWT oder Session? Praxisvergleich beider Sitzungsstrategien: NextAuth.js-Konfiguration, Performance, Sicherheit – damit Sie die richtige Wahl treffen.
Teil 49 von 51
Ähnliche Beiträge
Next.js App Router Einstieg: Kernkonzepte und Grundlagen im Überblick

Next.js App Router Einstieg: Kernkonzepte und Grundlagen im Überblick
Next.js 15 in der Praxis: Wie ich an einem Wochenende ein produktionsreifes Blog-System aufgebaut habe


Kommentare
Melde dich mit GitHub an, um einen Kommentar zu hinterlassen