Claude Code & JWT: Sichere Authentifizierung implementieren

JWT-Authentifizierung ist eines jener Themen, bei denen die Grundidee schnell verstanden ist — aber die sichere Umsetzung steckt voller Details, die man beim ersten Mal fast zwangsläufig falsch macht: Secrets im Code, zu lange Token-Laufzeiten, fehlende Signaturverifikation, kein Refresh-Mechanismus. Claude Code beschleunigt nicht nur die Implementierung, sondern hilft dabei, genau diese Pitfalls von Anfang an zu vermeiden.

Dieser Artikel zeigt, wie du mit Claude Code eine vollständige JWT-Authentifizierung in Node.js aufbaust — von den Grundlagen bis zu NestJS Guards und JWKS.

Claude Code Mastery — Authentication, Agents, Hooks auf Deutsch

JWT ist ein Thema im Kurs. Lerne außerdem Agents, MCP-Server, Hooks und produktive Entwicklungsworkflows mit Claude Code — vollständig auf Deutsch, einmalig bezahlt.

Zum Kurs — Jetzt starten → Einmalzahlung · Kein Abo · 14 Tage Rückgaberecht

1. JWT-Grundlagen: Header, Payload, Signature

Ein JSON Web Token besteht aus drei Base64url-codierten Teilen, getrennt durch Punkte: header.payload.signature. Der Header beschreibt den Token-Typ und den verwendeten Algorithmus. Der Payload enthält die Claims — also die eigentlichen Daten wie User-ID, Rollen oder Ablaufzeit. Die Signature stellt sicher, dass der Token nicht manipuliert wurde.

// Beispiel eines decodierten JWT-Payloads
{
  "sub": "user_123",
  "email": "user@example.com",
  "role": "admin",
  "iat": 1725955200,
  "exp": 1725958800
}

Entscheidend: Ein JWT ist signiert, nicht verschlüsselt. Der Payload ist lesbar für jeden, der den Token hat. Speichere keine sensiblen Informationen wie Passwörter oder Zahlungsdaten im Payload. Was der JWT garantiert, ist die Integrität — dass die Daten nicht verändert wurden — und die Authentizität — dass der Token vom Server ausgestellt wurde, der das Secret kennt.

Claude Code Tipp: Übergib Claude Code deinen bestehenden Auth-Code und frage explizit: "Prüfe diesen Code auf JWT-Sicherheitslücken." Claude Code liest alle relevanten Dateien und nennt konkrete Probleme — nicht generische Warnungen.

2. Node.js: jsonwebtoken-Library einrichten

Die Standardlibrary für JWT in Node.js ist jsonwebtoken. Installation und Grundstruktur:

npm install jsonwebtoken
npm install --save-dev @types/jsonwebtoken
import jwt from 'jsonwebtoken';

const JWT_SECRET = process.env.JWT_SECRET;
const JWT_EXPIRES_IN = '15m';

export function signToken(payload: object): string {
  if (!JWT_SECRET) throw new Error('JWT_SECRET not set');
  return jwt.sign(payload, JWT_SECRET, { expiresIn: JWT_EXPIRES_IN });
}

export function verifyToken(token: string): jwt.JwtPayload {
  if (!JWT_SECRET) throw new Error('JWT_SECRET not set');
  return jwt.verify(token, JWT_SECRET) as jwt.JwtPayload;
}

Das Secret kommt ausschließlich aus der Umgebungsvariable process.env.JWT_SECRET — niemals hartcodiert im Code. Claude Code prüft beim Review automatisch, ob Secrets versehentlich in den Code geschrieben wurden, und warnt entsprechend.

3. Access Token + Refresh Token Pattern

Das größte Sicherheitsproblem bei JWTs ist eine zu lange Laufzeit. Ein gestohlener Token mit 24-Stunden-Gültigkeit kann 24 Stunden lang missbraucht werden. Die Lösung: kurze Access Tokens kombiniert mit langlebigen Refresh Tokens.

export function generateTokenPair(userId: string) {
  const accessToken = jwt.sign(
    { sub: userId, type: 'access' },
    process.env.JWT_SECRET!,
    { expiresIn: '15m' }
  );

  const refreshToken = jwt.sign(
    { sub: userId, type: 'refresh' },
    process.env.JWT_REFRESH_SECRET!,
    { expiresIn: '7d' }
  );

  return { accessToken, refreshToken };
}

export function refreshAccessToken(refreshToken: string): string {
  const payload = jwt.verify(
    refreshToken,
    process.env.JWT_REFRESH_SECRET!
  ) as jwt.JwtPayload;

  if (payload.type !== 'refresh') {
    throw new Error('Invalid token type');
  }

  return jwt.sign(
    { sub: payload.sub, type: 'access' },
    process.env.JWT_SECRET!,
    { expiresIn: '15m' }
  );
}

Access Tokens leben 15 Minuten, Refresh Tokens 7 Tage. Der Client speichert den Refresh Token sicher (HttpOnly Cookie) und tauscht ihn bei Bedarf gegen einen neuen Access Token aus. Der Server validiert dabei den Typ des Tokens — ein Access Token darf nicht zum Refreshen verwendet werden.

4. Secret sicher speichern

Das JWT_SECRET ist der kritischste Teil der gesamten Authentifizierung. Wer es kennt, kann beliebige gültige Tokens ausstellen. Drei Regeln:

# .env (niemals in Git)
JWT_SECRET=<openssl rand -hex 32 ausgabe>
JWT_REFRESH_SECRET=<openssl rand -hex 32 ausgabe>

Häufiger Fehler: Das Secret direkt im Code als Fallback zu hinterlegen: process.env.JWT_SECRET || 'fallback-secret'. Das ist gefährlich — in Produktionsumgebungen ohne gesetzte Umgebungsvariable läuft die App mit einem bekannten, unsicheren Secret. Besser: explizit werfen, wenn das Secret fehlt.

5. Middleware für Express und Fastify

Die Middleware validiert den Token bei jedem geschützten Request und legt die User-Daten am Request-Objekt ab:

Express Middleware

import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';

export function authenticate(req: Request, res: Response, next: NextFunction) {
  const authHeader = req.headers.authorization;
  if (!authHeader?.startsWith('Bearer ')) {
    return res.status(401).json({ error: 'Missing token' });
  }

  const token = authHeader.slice(7);
  try {
    const payload = jwt.verify(token, process.env.JWT_SECRET!) as jwt.JwtPayload;
    req.user = { id: payload.sub!, role: payload.role };
    next();
  } catch {
    return res.status(401).json({ error: 'Invalid or expired token' });
  }
}

Fastify Plugin

import fp from 'fastify-plugin';
import jwt from 'jsonwebtoken';

export default fp(async (fastify) => {
  fastify.decorate('authenticate', async (request: any, reply: any) => {
    const token = request.headers.authorization?.slice(7);
    if (!token) return reply.status(401).send({ error: 'Missing token' });

    try {
      request.user = jwt.verify(token, process.env.JWT_SECRET!);
    } catch {
      return reply.status(401).send({ error: 'Invalid token' });
    }
  });
});

6. Token-Blacklisting

JWTs sind zustandslos — einmal ausgestellt, gelten sie bis zur Ablaufzeit. Das ist ein Problem beim Logout: Der Client löscht den Token, aber ein Angreifer, der den Token kopiert hat, kann ihn weiter verwenden. Die Lösung ist eine Blacklist für invalidierte Tokens.

import { Redis } from 'ioredis';

const redis = new Redis(process.env.REDIS_URL!);

export async function blacklistToken(token: string, expiresIn: number) {
  await redis.setex(`blacklist:${token}`, expiresIn, '1');
}

export async function isBlacklisted(token: string): Promise {
  const result = await redis.get(`blacklist:${token}`);
  return result !== null;
}

// In der Middleware
const blacklisted = await isBlacklisted(token);
if (blacklisted) {
  return res.status(401).json({ error: 'Token revoked' });
}

Die TTL der Redis-Einträge entspricht der Restlaufzeit des Tokens — danach bereinigt sich die Blacklist automatisch. Für Access Tokens mit 15-minütiger Laufzeit ist die Blacklist damit minimal belastet.

7. JWKS und RS256

Für verteilte Systeme — mehrere Services, die Tokens validieren müssen — ist das symmetrische HS256 problematisch: Jeder Service braucht das Secret, und ein kompromittierter Service gefährdet alle anderen. Die Lösung ist RS256 mit einem asymmetrischen Schlüsselpaar.

# Schlüsselpaar generieren
openssl genrsa -out private.pem 2048
openssl rsa -in private.pem -pubout -out public.pem
// Ausstellen mit Private Key (nur Auth-Service)
const token = jwt.sign(payload, process.env.JWT_PRIVATE_KEY!, {
  algorithm: 'RS256',
  expiresIn: '15m',
  keyid: 'key-2026-09'
});

// Validieren mit Public Key (alle Services)
const decoded = jwt.verify(token, process.env.JWT_PUBLIC_KEY!, {
  algorithms: ['RS256']
});

Der Public Key kann über einen JWKS-Endpoint (JSON Web Key Set) öffentlich verfügbar gemacht werden. Services laden ihn beim Start und cachen ihn. Der Private Key bleibt ausschließlich beim Auth-Service.

8. NestJS Guards

NestJS bietet mit Guards eine elegante Abstraktion für Authentifizierung und Autorisierung:

import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { Request } from 'express';

@Injectable()
export class JwtAuthGuard implements CanActivate {
  constructor(private jwtService: JwtService) {}

  async canActivate(context: ExecutionContext): Promise {
    const request = context.switchToHttp().getRequest();
    const token = this.extractToken(request);

    if (!token) throw new UnauthorizedException();

    try {
      const payload = await this.jwtService.verifyAsync(token, {
        secret: process.env.JWT_SECRET,
      });
      request['user'] = payload;
    } catch {
      throw new UnauthorizedException();
    }

    return true;
  }

  private extractToken(request: Request): string | undefined {
    const [type, token] = request.headers.authorization?.split(' ') ?? [];
    return type === 'Bearer' ? token : undefined;
  }
}
// Verwendung am Controller
@UseGuards(JwtAuthGuard)
@Get('profile')
getProfile(@Request() req) {
  return req.user;
}

Claude Code kann den gesamten Guard-Code aus einer Beschreibung deiner Anforderungen generieren: Welche Rollen es gibt, welche Endpoints öffentlich bleiben, ob du Decorator-basierte Ausnahmen brauchst (@Public()). Die Implementierung eines vollständigen Role-Guards dauert damit Minuten statt einer Stunde.

9. Sicherheits-Pitfalls

Die häufigsten JWT-Fehler, auf die Claude Code beim Code-Review hinweist:

"Gib mir diesen Auth-Code und liste alle JWT-Sicherheitslücken auf — mit Schweregrad und konkretem Fix."

Dieser Prompt reicht, damit Claude Code eine vollständige Sicherheitsanalyse liefert. Kein generisches Checklist-Durchklicken, sondern eine Analyse des tatsächlichen Codes, der vor dir liegt.


Zwei verwandte Artikel die auf diesem Thema aufbauen:


Claude Code Mastery — von JWT bis zum produktiven Agenten

Authentifizierung ist ein Kapitel. Der Kurs zeigt außerdem Agents, MCP-Server, Hooks, Multi-Agent-Workflows und mehr — vollständig auf Deutsch, einmalig bezahlt.

Jetzt starten → Einmalzahlung · Kein Abo · 14 Tage Rückgaberecht

Kurs · Claude Code Mastery

Von JWT-Auth zum produktiven AI-Agenten

Authentication. Agents. MCP. Hooks. Multi-Agent-Workflows. Alles auf Deutsch, einmalig bezahlt — kein Abo, keine Plattformabhängigkeit.

Jetzt einsteigen → Kursübersicht ansehen →

Einmalzahlung · Kein Abo · 14 Tage Rückgaberecht