Autorisierung und Authentifizierung in TypeScript: von der Theorie zur Praxis
Bei der Entwicklung moderner Webanwendungen mit TypeScript ist die Sicherheit von Benutzerdaten eine vorrangige Aufgabe. Zwei Schlüsselprozesse — Authentifizierung (Identitätsbestätigung) und Autorisierung (Überprüfung der Zugriffsrechte) — bilden das Fundament jedes geschützten Systems. In diesem Leitfaden analysieren wir den vollständigen Zyklus der Implementierung von Authentifizierung und Autorisierung in TypeScript unter Verwendung beliebter Bibliotheken und Best Practices.
1. Grundlegende Konzepte und Unterschiede
- Authentifizierung — der Prozess der Überprüfung, ob ein Benutzer derjenige ist, der er vorgibt zu sein (Login + Passwort, Biometrie, OAuth).
- Autorisierung — der Prozess der Bestimmung, welche Ressourcen und Aktionen einem authentifizierten Benutzer zur Verfügung stehen (Rollen, Zugriffsrechte).
Zum Beispiel: Ein Benutzer hat das richtige Passwort eingegeben (Authentifizierung), aber es ist ihm verboten, fremde Beiträge zu löschen (Autorisierung).
2. Erforderliche Werkzeuge und Bibliotheken
Für die Implementierung verwenden wir:
bcrypt— zum Hashen von Passwörternjsonwebtoken(JWT) — zum Erstellen und Überprüfen von Tokenexpress+@types/express— Serverseitezod— Validierung von Eingabedaten
3. Benutzerregistrierung (Authentifizierung)
Bei der Registrierung wird das Passwort niemals im Klartext gespeichert. Wir verwenden bcrypt, um einen Hash zu erstellen:
import bcrypt from 'bcrypt';import { z } from 'zod';
const registerSchema = z.object({ email: z.string().email(), password: z.string().min(8)});
async function registerUser(email: string, password: string) { const validated = registerSchema.parse({ email, password }); const saltRounds = 10; const hashedPassword = await bcrypt.hash(validated.password, saltRounds); // Speichere email + hashedPassword in der Datenbank // await db.user.create({ data: { email, password: hashedPassword } }); return { success: true };}4. Anmeldung und Ausstellung von JWT
Nach erfolgreicher Passwortüberprüfung generieren wir einen Access-Token:
import jwt from 'jsonwebtoken';
const JWT_SECRET = process.env.JWT_SECRET!; // unbedingt in .env
async function loginUser(email: string, password: string) { // 1. Benutzer in der DB per E-Mail finden // const user = await db.user.findUnique({ where: { email } }); // if (!user) throw new Error('User not found'); // 2. Passwort vergleichen const isMatch = await bcrypt.compare(password, user.password); if (!isMatch) throw new Error('Invalid password'); // 3. JWT erstellen const token = jwt.sign( { userId: user.id, role: user.role }, JWT_SECRET, { expiresIn: '1h' } ); return { token, user: { id: user.id, email: user.email } };}5. Middleware zur JWT-Überprüfung (Authentifizierung)
Wir erstellen eine Middleware, die den Token bei jeder geschützten Anfrage überprüft:
import { Request, Response, NextFunction } from 'express';
interface AuthRequest extends Request { user?: { userId: string; role: string };}
export function authMiddleware(req: AuthRequest, res: Response, next: NextFunction) { const authHeader = req.headers.authorization; if (!authHeader?.startsWith('Bearer ')) { return res.status(401).json({ error: 'No token provided' }); } const token = authHeader.split(' ')[1]; try { const decoded = jwt.verify(token, JWT_SECRET) as { userId: string; role: string }; req.user = decoded; next(); } catch (err) { return res.status(403).json({ error: 'Invalid or expired token' }); }}6. Rollenbasierte Autorisierung (RBAC)
Wir erstellen eine Middleware