feat(auth): Implementiere vollständiges Auth-System

- JWT Access + Refresh Tokens mit djwt
- Argon2 Password Hashing (OWASP konfig)
- Rate Limiting für Auth-Endpoints
- Rollen-basierte Zugriffskontrolle (owner, admin, manager, user)
- DSGVO Audit Logging
- Email-Verifizierung (Struktur)
- Passwort-Reset Flow
- Multi-Device Logout

Neue Dateien:
- src/types/index.ts - TypeScript Interfaces
- src/db/connection.ts - PostgreSQL Pool
- src/services/password.ts - Argon2 Hashing
- src/services/jwt.ts - Token Generation
- src/services/audit.ts - DSGVO Audit Log
- src/middleware/auth.ts - Auth Middleware
- src/repositories/user.ts - User DB Queries
- src/repositories/organization.ts - Org DB Queries
- src/utils/response.ts - API Response Helpers

Task: #8 Authentifizierung & Benutzerverwaltung
This commit is contained in:
2026-02-11 10:30:37 +00:00
parent cc74d66fad
commit d0f1c242a3
13 changed files with 1888 additions and 107 deletions

View File

@@ -8,8 +8,12 @@ import { dealsRouter } from "./routes/deals.ts";
import { activitiesRouter } from "./routes/activities.ts";
import { pipelinesRouter } from "./routes/pipelines.ts";
// Database
import { checkHealth as checkDbHealth } from "./db/connection.ts";
const app = new Application();
const PORT = parseInt(Deno.env.get("PORT") || "8000");
const NODE_ENV = Deno.env.get("NODE_ENV") || "development";
// ============================================
// MIDDLEWARE
@@ -17,8 +21,15 @@ const PORT = parseInt(Deno.env.get("PORT") || "8000");
// CORS Middleware
app.use(async (ctx, next) => {
const origin = ctx.request.headers.get("origin") || "*";
ctx.response.headers.set("Access-Control-Allow-Origin", origin);
const allowedOrigins = Deno.env.get("CORS_ORIGINS")?.split(",") || ["*"];
const origin = ctx.request.headers.get("origin");
if (origin && (allowedOrigins.includes("*") || allowedOrigins.includes(origin))) {
ctx.response.headers.set("Access-Control-Allow-Origin", origin);
} else if (allowedOrigins.includes("*")) {
ctx.response.headers.set("Access-Control-Allow-Origin", "*");
}
ctx.response.headers.set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, PATCH");
ctx.response.headers.set("Access-Control-Allow-Headers", "Content-Type, Authorization");
ctx.response.headers.set("Access-Control-Allow-Credentials", "true");
@@ -32,7 +43,16 @@ app.use(async (ctx, next) => {
await next();
});
// Logger Middleware
// Security Headers
app.use(async (ctx, next) => {
ctx.response.headers.set("X-Content-Type-Options", "nosniff");
ctx.response.headers.set("X-Frame-Options", "DENY");
ctx.response.headers.set("X-XSS-Protection", "1; mode=block");
ctx.response.headers.set("Referrer-Policy", "strict-origin-when-cross-origin");
await next();
});
// Request Logger
app.use(async (ctx, next) => {
const start = Date.now();
await next();
@@ -48,14 +68,18 @@ app.use(async (ctx, next) => {
await next();
} catch (err) {
console.error("Error:", err);
ctx.response.status = 500;
const status = err.status || 500;
ctx.response.status = status;
ctx.response.body = {
success: false,
error: {
code: "INTERNAL_ERROR",
message: Deno.env.get("NODE_ENV") === "development"
code: status === 500 ? "INTERNAL_ERROR" : "ERROR",
message: NODE_ENV === "development"
? err.message
: "An internal error occurred",
: status === 500
? "An internal error occurred"
: err.message,
},
};
}
@@ -65,20 +89,35 @@ app.use(async (ctx, next) => {
// SYSTEM ROUTES
// ============================================
// Health Check
// Health Check (includes DB status)
app.use(async (ctx, next) => {
if (ctx.request.url.pathname === "/health") {
const dbHealthy = await checkDbHealth();
ctx.response.status = dbHealthy ? 200 : 503;
ctx.response.body = {
status: "ok",
status: dbHealthy ? "ok" : "degraded",
service: "pulse-crm-backend",
version: "0.1.0",
timestamp: new Date().toISOString(),
checks: {
database: dbHealthy ? "ok" : "error",
},
};
return;
}
await next();
});
// Liveness probe (simple check)
app.use(async (ctx, next) => {
if (ctx.request.url.pathname === "/live") {
ctx.response.body = { status: "ok" };
return;
}
await next();
});
// API Info
app.use(async (ctx, next) => {
if (ctx.request.url.pathname === "/api" || ctx.request.url.pathname === "/api/v1") {
@@ -86,12 +125,17 @@ app.use(async (ctx, next) => {
name: "Pulse CRM API",
version: "1.0.0",
description: "Der Herzschlag deines Business",
documentation: "/api/v1/docs",
endpoints: {
auth: {
"POST /api/v1/auth/register": "Register new user",
"POST /api/v1/auth/register": "Register new user & organization",
"POST /api/v1/auth/login": "Login",
"POST /api/v1/auth/refresh": "Refresh token",
"POST /api/v1/auth/logout": "Logout",
"POST /api/v1/auth/refresh": "Refresh access token",
"POST /api/v1/auth/logout": "Logout (revoke token)",
"POST /api/v1/auth/logout-all": "Logout from all devices",
"POST /api/v1/auth/forgot-password": "Request password reset",
"POST /api/v1/auth/reset-password": "Reset password with token",
"POST /api/v1/auth/verify-email": "Verify email address",
"GET /api/v1/auth/me": "Get current user",
},
contacts: {
@@ -100,6 +144,8 @@ app.use(async (ctx, next) => {
"POST /api/v1/contacts": "Create contact",
"PUT /api/v1/contacts/:id": "Update contact",
"DELETE /api/v1/contacts/:id": "Delete contact",
"POST /api/v1/contacts/import": "Import contacts (CSV)",
"GET /api/v1/contacts/export": "Export contacts (DSGVO)",
},
deals: {
"GET /api/v1/deals": "List deals",
@@ -173,6 +219,7 @@ console.log(" ====================");
console.log(` 📡 Server: http://localhost:${PORT}`);
console.log(` 📚 API: http://localhost:${PORT}/api/v1`);
console.log(` ❤️ Health: http://localhost:${PORT}/health`);
console.log(` 🔧 Mode: ${NODE_ENV}`);
console.log("");
await app.listen({ port: PORT });