🔐 Security & Auth

JWT Authentication mit Claude Code: Tokens, Refresh und Sicherheit 2026

📅 5. Mai 2026 ⏱ 11 min Lesezeit ✍️ SpockyMagicAI Team

JSON Web Tokens sind der de-facto Standard für zustandslose Authentifizierung in modernen Web-Apps — und einer der häufigsten Quellen von Sicherheitslücken. In diesem Guide zeigen wir, wie Claude Code eine vollständige, sichere JWT-Implementierung für Next.js generiert: von der Token-Struktur über Refresh-Rotation bis hin zu Revocation und Middleware.

1. JWT Struktur: Header, Payload, Signature

Ein JWT besteht aus drei Base64URL-kodierten Teilen, getrennt durch Punkte:

Token-Anatomie

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 . eyJ1c2VySWQiOiI0MiIsInJvbGUiOiJ1c2VyIn0 . SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
TeilInhaltVeränderbar?
HeaderAlgorithmus (HS256/RS256) + TypPrüfen!
PayloadClaims: sub, exp, iat, role …Öffentlich lesbar
SignatureHMAC/RSA über Header+PayloadUnfälschbar
⚠️ Wichtig: Payload-Daten sind nur Base64-kodiert, nicht verschlüsselt. Speichere niemals Passwörter, Kreditkarten oder sensible PII im JWT-Payload!

Claude Code generiert den Token-Kern in wenigen Sekunden. Prompt-Beispiel:

// Prompt an Claude Code: // "Erstelle eine JWT-Utility mit sign/verify für HS256 und RS256, // TypeScript, jose-Library, strict typing" import { SignJWT, jwtVerify, generateKeyPair } from 'jose' export interface TokenPayload { sub: string // User-ID role: 'user' | 'admin' iat?: number exp?: number } const SECRET = new TextEncoder().encode(process.env.JWT_SECRET!) export async function signAccessToken(payload: TokenPayload): Promise<string> { return new SignJWT(payload) .setProtectedHeader({ alg: 'HS256' }) .setIssuedAt() .setExpirationTime('15m') // Kurze Laufzeit! .sign(SECRET) } export async function verifyToken(token: string): Promise<TokenPayload> { const { payload } = await jwtVerify(token, SECRET, { algorithms: ['HS256'], // NIEMALS ['*'] oder 'none' erlauben! }) return payload as TokenPayload }

2. Access Token + Refresh Token Pattern

Zwei-Token-Strategie

Access Token — 15 min Refresh Token — 7 Tage

Access Tokens sind kurzlebig → kompromittierte Tokens werden schnell ungültig. Refresh Tokens rotieren bei jeder Nutzung und werden in der DB revoked.

Claude Code generiert den kompletten Auth-Flow inklusive DB-Schema:

// lib/auth/tokens.ts — von Claude Code generiert import { db } from '@/lib/db' import { randomBytes } from 'crypto' export async function issueTokenPair(userId: string, role: string) { const accessToken = await signAccessToken({ sub: userId, role }) const refreshToken = randomBytes(64).toString('hex') const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) await db.refreshToken.create({ data: { token: refreshToken, userId, expiresAt, used: false } }) return { accessToken, refreshToken } } export async function rotateRefreshToken(oldToken: string) { const record = await db.refreshToken.findUnique({ where: { token: oldToken }}) if (!record || record.used || record.expiresAt < new Date()) { // Refresh Token Reuse Detection: alle Tokens des Users invalidieren! if (record?.used) await revokeAllUserTokens(record.userId) throw new Error('Invalid refresh token') } await db.refreshToken.update({ where: { token: oldToken }, data: { used: true } // Alten Token sperren }) return issueTokenPair(record.userId, record.role) }
✅ Best Practice: Refresh Token Reuse Detection — wenn ein bereits genutzter Refresh Token erneut eingesetzt wird, liegt höchstwahrscheinlich ein Token-Diebstahl vor. Alle Sessions des Users sofort invalidieren!

3. JWT in Next.js: httpOnly Cookie vs. localStorage

SpeicherortXSS-sicherCSRF-RisikoEmpfehlung
localStorage❌ Nein✅ NeinNicht für JWTs!
sessionStorage❌ Nein✅ NeinNicht für Auth
httpOnly Cookie✅ Ja⚠️ Mitigation nötig✅ Empfohlen
Memory (React State)✅ Ja✅ NeinFür kurze Sessions
// app/api/auth/login/route.ts — Next.js App Router import { NextResponse } from 'next/server' import { cookies } from 'next/headers' export async function POST(req: Request) { const { email, password } = await req.json() const user = await authenticateUser(email, password) if (!user) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) const { accessToken, refreshToken } = await issueTokenPair(user.id, user.role) const cookieStore = await cookies() // Refresh Token: httpOnly, Secure, SameSite=Strict cookieStore.set('refresh_token', refreshToken, { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'strict', maxAge: 60 * 60 * 24 * 7, // 7 Tage path: '/api/auth', // Nur Auth-Endpunkte }) // Access Token: kurzlebig, für Client sichtbar (kein httpOnly) return NextResponse.json({ accessToken, expiresIn: 900 }) }

4. Token Rotation und Revocation Strategien

Rotation-Endpunkt

// app/api/auth/refresh/route.ts export async function POST(req: Request) { const cookieStore = await cookies() const oldRefresh = cookieStore.get('refresh_token')?.value if (!oldRefresh) return NextResponse.json({ error: 'No token' }, { status: 401 }) try { const { accessToken, refreshToken } = await rotateRefreshToken(oldRefresh) cookieStore.set('refresh_token', refreshToken, { httpOnly: true, secure: true, sameSite: 'strict', maxAge: 60 * 60 * 24 * 7, path: '/api/auth' }) return NextResponse.json({ accessToken }) } catch { cookieStore.delete('refresh_token') // Session komplett beenden return NextResponse.json({ error: 'Token invalid' }, { status: 401 }) } }

Revocation via Denylist

Da Access Tokens zustandslos sind, werden sie nach Ablauf automatisch ungültig. Für sofortige Invalidierung (z. B. Logout, Passwort-Reset) nutzt Claude Code eine Redis-Denylist:

// lib/auth/revocation.ts import { redis } from '@/lib/redis' export async function revokeAccessToken(token: string, exp: number) { const ttl = exp - Math.floor(Date.now() / 1000) if (ttl > 0) await redis.setex(`revoked:${hashToken(token)}`, ttl, '1') } export async function isRevoked(token: string): Promise<boolean> { return !!(await redis.get(`revoked:${hashToken(token)}`)) } export async function revokeAllUserTokens(userId: string) { await db.refreshToken.updateMany({ where: { userId, used: false }, data: { used: true } }) await redis.set(`user_revoked:${userId}`, Date.now(), { ex: 86400 }) }

5. JWT Pitfalls: Was Claude Code automatisch verhindert

Häufige Fehler und ihre Fixes

PitfallRisikoFix
Algorithm Confusion alg: "none" → kein Verify Whitelist erlaubter Algorithmen
Kein exp-Claim Token gilt ewig setExpirationTime('15m') Pflicht
Schwacher Secret Brute-Force möglich ≥256 bit Secret, rotieren
JWT in localStorage XSS → Token-Diebstahl httpOnly Cookie
Kein Reuse Detection Replay-Angriff möglich used-Flag + Revocation
⚠️ Algorithm Confusion Attack: Manche Libraries erlauben alg: "none" oder wechseln von RS256 zu HS256 mit dem Public Key als Secret. Immer explizit algorithms: ['HS256'] setzen!

6. Middleware für geschützte Routen

Claude Code erstellt Next.js Middleware, die alle geschützten Routen automatisch absichert — ohne dass jede API-Route eigene Auth-Logik braucht:

// middleware.ts (Next.js Root) import { NextRequest, NextResponse } from 'next/server' import { verifyToken } from '@/lib/auth/jwt' import { isRevoked } from '@/lib/auth/revocation' const PUBLIC_ROUTES = ['/login', '/signup', '/api/auth', '/blog'] export async function middleware(req: NextRequest) { const { pathname } = req.nextUrl // Public Routes überspringen if (PUBLIC_ROUTES.some(r => pathname.startsWith(r))) return NextResponse.next() const authHeader = req.headers.get('authorization') const token = authHeader?.replace('Bearer ', '') if (!token) return unauthorized() try { const payload = await verifyToken(token) // Denylist-Check (Redis) if (await isRevoked(token)) return unauthorized() // User-Info in Request-Header injizieren const res = NextResponse.next() res.headers.set('x-user-id', payload.sub) res.headers.set('x-user-role', payload.role) return res } catch { return unauthorized() } } function unauthorized() { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) } export const config = { matcher: ['/dashboard/:path*', '/api/protected/:path*', '/admin/:path*'] }
✅ Middleware-Vorteil: Middleware läuft auf der Edge Runtime — extrem schnell, bevor der Request die eigentliche Route erreicht. Kein Auth-Boilerplate in jeder Route nötig.

7. Secret Management und Key Rotation

// .env.local — NIEMALS committen! JWT_SECRET=$(openssl rand -base64 64) JWT_SECRET_PREVIOUS=alter_secret_fuer_graceful_rotation // lib/auth/jwt.ts — Graceful Key Rotation const SECRETS = [ process.env.JWT_SECRET!, process.env.JWT_SECRET_PREVIOUS, // Fallback für alte Tokens ].filter(Boolean).map(s => new TextEncoder().encode(s)) export async function verifyTokenWithRotation(token: string) { for (const secret of SECRETS) { try { return await jwtVerify(token, secret, { algorithms: ['HS256'] }) } catch {} } throw new Error('Invalid token') }
✅ Claude Code Workflow: Gib Claude Code dein Datenbankschema und deine Next.js-Projektstruktur. Es generiert den kompletten Auth-Stack inklusive Tests, Prisma-Schema, Redis-Config und Middleware — in unter 5 Minuten produktionsreif.

Zusammenfassung: JWT Security Checklist

Mit Claude Code sicher starten

JWT, OAuth, Passkeys — Claude Code implementiert jeden Auth-Stack produktionsreif. Teste es jetzt kostenlos.

Kostenlosen Trial starten →