## 13. Phase-3-Decisions-Lockdown (vom User bestätigt 2026-06-03) Diese Section bündelt alle offenen Punkte aus der Pattern-Summary, die vor Phase 4a-Start geklärt wurden. Diese Defaults sind **gelockt** und gelten für alle nachfolgenden Phasen, bis explizit geändert. ### 13.1 JWT-Library: `python-jose[cryptography]==3.3.0` - **Entscheidung:** python-jose (nicht PyJWT) - **Begründung:** Pattern-Reuse aus wochenplaner, gleiche API wie dort - **Verwendung:** `from jose import jwt` (encode, decode) - **Algorithmus:** HS256 (single-secret, single-server) - **Expiry:** 24h (gemäß 01-requirements FR-1.2), konfigurierbar via `JWT_EXPIRY_HOURS` - **Refresh-Token:** v1.1 (nicht v1) ### 13.2 DB-Setup: SQLite-only-Dev (kein paralleles PostgreSQL) - **Entscheidung:** SQLite in Dev, PostgreSQL erst in Prod (via Coolify) - **Driver:** `aiosqlite` für async SQLite - **Begründung:** Schneller Dev-Loop, weniger Test-Aufwand, Production-Driver in 4d definiert - **Config:** `DATABASE_URL=sqlite+aiosqlite:///./dev.db` (default) oder via ENV - **Production-Override:** `DATABASE_URL=postgresql+asyncpg://...` in Coolify - **Test-Override:** `DATABASE_URL=sqlite+aiosqlite:///:memory:` in pytest ### 13.3 CSP-Header: FastAPI-Middleware (zentral, app-aware) - **Entscheidung:** CSP-Header in FastAPI-Middleware (nicht nginx.conf) - **Begründung:** App-aware, zentral in `app/main.py` registriert, einfacher zu testen - **Header-Wert (Dev):** ``` Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.tailwindcss.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:; object-src 'none'; ``` - **Header-Wert (Prod):** `'unsafe-inline'` für Scripts entfernen (Alpine.js inline-eval → v1.1 Fix mit Nonce) - **Weitere Security-Header:** `X-Content-Type-Options: nosniff`, `X-Frame-Options: DENY`, `Strict-Transport-Security` (Prod) ### 13.4 LoginAttempt-Tabelle: v1.1 (nicht v1) - **Entscheidung:** Brute-Force-Schutz via LoginAttempt erst in v1.1 - **Begründung:** Demo-Scope, JWT-Token-Diebstahl ist größeres Risiko als Brute-Force - **v1-Verhalten:** Login-Endpoint hat kein Rate-Limit (siehe A-4 Risiko) - **v1.1-Plan:** LoginAttempt-Tabelle (user_id, ip_address, attempted_at, success), Middleware für Rate-Limit (z.B. 5 Versuche / 15 min) ### 13.5 Security-Anforderungen (übernommen aus patterns-summary Risiken 3, 4, 5, 8) - **R-3 KEIN Default-User:** Kein `admin/admin` Bootstrap-User, Registrierung erfolgt explizit via `POST /api/v1/auth/register` - **R-4 CORS-Whitelist:** Erlaubte Origins in `CORS_ORIGINS` ENV-Var (Komma-getrennt), KEIN `"*"` - Dev: `http://localhost:5500,http://localhost:8000` - Prod: `https://crm.media-on.de` - **R-5 KEIN JWT-Secret-Fallback:** Wenn `AUTH_SECRET` fehlt oder < 32 Zeichen → App-Start bricht ab (Hard-Fail, keine `dev-secret` Fallback) - **R-8 PostgreSQL-Service in Prod:** Production-Deployment MUSS PostgreSQL-Service in Coolify nutzen, KEIN SQLite in Prod ### 13.6 Library-Pinning (aus wochenplaner-requirements.txt übernommen) | Library | Version | Zweck | |---|---|---| | fastapi | >=0.111.0,<0.116 | Web-Framework | | uvicorn[standard] | >=0.29.0 | ASGI-Server | | sqlalchemy | ==2.0.35 | ORM (async via aiosqlite/asyncpg) | | alembic | >=1.13 | Migrationen | | pydantic | >=2.5 | Schema-Validation | | pydantic-settings | >=2.1 | Settings-Management | | python-jose[cryptography] | ==3.3.0 | JWT-Encode/Decode | | passlib[bcrypt] | ==1.7.4 | Password-Hashing-Wrapper | | bcrypt | ==4.0.1 | Password-Hashing-Algorithmus | | python-multipart | >=0.0.7 | Form-Data-Parsing | | aiosqlite | >=0.19 | Async-SQLite-Driver | | asyncpg | >=0.29 | Async-PostgreSQL-Driver (Prod) | | aiofiles | >=23.2 | Async-File-I/O für Static-Files | | jinja2 | >=3.1 | Template-Engine (optional, für Error-Pages) | | pytest | >=8.0 | Test-Framework | | pytest-asyncio | >=0.23 | Async-Tests | | httpx | >=0.27 | Async-HTTP-Client (Tests + API-Wrapper) | | ruff | >=0.4 | Linting (Phase 7) | | mypy | >=1.10 | Type-Check (Phase 7) | ### 13.7 Async-Pflicht (R-2 + ADR-1) - **Alle Routers:** `async def` - **Alle Services:** `async def` - **Alle DB-Operations:** `await session.execute(...)` - **SQLAlchemy:** `AsyncSession` (nicht `Session`) - **Tests:** `pytest-asyncio` mit `asyncio_mode=auto` - **Alembic:** async-template (`asyncio.run` in env.py) --- **Nächste Phase nach User-Approval:** implementation_engineer (Phase 4a) → Backend-Skeleton + Auth + Health