spawner/IMPLEMENTATION-GUIDE.md
XPS\Micro 20a0f3d6af feat: Implement passwordless authentication with Magic Links
Major changes:
- Remove username and password_hash from User model
- Add MagicLinkToken table for one-time-use email authentication
- Implement Magic Link email sending with 15-minute expiration
- Update all auth endpoints (/login, /signup) to use email only
- Create verify-signup and verify-login pages for token verification
- Container URLs now use slug instead of username (e.g., /u-a3f9c2d1)
- Add rate limiting: max 3 Magic Links per email per hour
- Remove password reset functionality (no passwords to reset)

Backend changes:
- api.py: Complete rewrite of auth routes (magic link based)
- models.py: Remove username/password, add slug and MagicLinkToken
- email_service.py: Add Magic Link generation and email sending
- admin_api.py: Remove password reset, update to use email identifiers
- container_manager.py: Use slug instead of username for routing
- config.py: Add MAGIC_LINK_TOKEN_EXPIRY and MAGIC_LINK_RATE_LIMIT

Frontend changes:
- src/lib/api.ts: Update auth functions and User interface
- src/hooks/use-auth.tsx: Implement verifySignup/verifyLogin
- src/app/login/page.tsx: Email-only login form
- src/app/signup/page.tsx: Email-only signup form
- src/app/verify-signup/page.tsx: NEW - Signup token verification
- src/app/verify-login/page.tsx: NEW - Login token verification
- src/app/dashboard/page.tsx: Display slug instead of username

Infrastructure:
- install.sh: Simplified, no migration needed (db.create_all handles it)
- .env.example: Add MAGIC_LINK_TOKEN_EXPIRY and MAGIC_LINK_RATE_LIMIT
- Add IMPLEMENTATION-GUIDE.md with detailed setup instructions

Security improvements:
- No password storage = no password breaches
- One-time-use tokens prevent replay attacks
- 15-minute token expiration limits attack window
- Rate limiting prevents email flooding

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-01-31 16:19:22 +01:00

214 lines
7.4 KiB
Markdown

# Passwordless Authentication - Implementierungsanleitung
## ✅ Was wurde implementiert
Das Container Spawner System wurde komplett auf **Passwordless Authentication mit Magic Links** umgestellt.
### Backend-Änderungen (Python/Flask)
#### 1. **Datenbank-Schema** (`models.py`)
- ❌ Entfernt: `username` Spalte
- ❌ Entfernt: `password_hash` Spalte
- ✅ Hinzugefügt: `slug` Spalte (unique, 12 Zeichen, basierend auf Email-Hash)
- ✅ Neue Tabelle: `MagicLinkToken` für Magic Link Tokens
- `token` - der Magic Link Token (unique)
- `token_type` - 'signup' oder 'login'
- `expires_at` - Ablaufzeit (15 Minuten)
- `used_at` - Zeitstempel wenn Token verwendet wurde
- `is_valid()` - Methode zur Validierung
- `mark_as_used()` - Methode zum Markieren als verwendet
#### 2. **Email-Service** (`email_service.py`)
-`generate_slug_from_email(email)` - Generiert eindeutigen Slug aus Email
-`generate_magic_link_token()` - Generiert sicheren Token
-`send_magic_link_email(email, token, token_type)` - Sendet Magic Link per Email
-`check_rate_limit(email)` - Rate-Limiting (max 3 Tokens/Stunde)
#### 3. **API Routes** (`api.py`)
-`POST /api/auth/login` - Sendet Magic Link statt Passwort zu prüfen
-`POST /api/auth/signup` - Sendet Magic Link für Registrierung
-`GET /api/auth/verify-signup` - Verifiziert Signup Token & erstellt JWT
-`GET /api/auth/verify-login` - Verifiziert Login Token & erstellt JWT
- ❌ Gelöscht: `/api/auth/verify` (alte Email-Verifizierung)
- ❌ Gelöscht: `/api/auth/resend-verification`
- ✅ Angepasst: `/api/user/me` - Nutzt `slug` statt `username`
- ✅ Angepasst: `/api/container/restart` - Nutzt `slug`
#### 4. **Admin API** (`admin_api.py`)
- ❌ Gelöscht: `/api/admin/users/{id}/reset-password`
- ✅ Angepasst: `resend_verification()` - Sendet Magic Link statt Password-Reset
- ✅ Alle `user.username` Referenzen → `user.email`
#### 5. **Container Manager** (`container_manager.py`)
-`spawn_container(user_id, slug)` - Nutzt `slug` statt `username`
- ✅ Traefik-Labels aktualisiert: `/username``/slug`
- ✅ Environment: `USERNAME``USER_SLUG`
-`start_container()` - Neue Methode zum Starten gestoppter Container
-`_get_user_container()` - Nutzt `slug` statt `username`
#### 6. **Konfiguration** (`config.py`)
-`MAGIC_LINK_TOKEN_EXPIRY = 900` (15 Minuten)
-`MAGIC_LINK_RATE_LIMIT = 3` (3 Tokens pro Stunde)
### Frontend-Änderungen (TypeScript/React)
#### 1. **API Client** (`src/lib/api.ts`)
- ✅ Neue `User` Interface: `email`, `slug`, `state`, keine `username`
-`api.auth.login(email)` - nur Email statt username+password
-`api.auth.signup(email)` - nur Email
-`api.auth.verifySignup(token)` - Verifiziert Signup Token
-`api.auth.verifyLogin(token)` - Verifiziert Login Token
- ✅ QueryParams Support in `fetchApi()`
#### 2. **Auth Hook** (`src/hooks/use-auth.tsx`)
-`login(email)` - Magic Link Request
-`signup(email)` - Magic Link Request
-`verifySignup(token)` - Token-Verifizierung
-`verifyLogin(token)` - Token-Verifizierung
- ✅ State Management: Error Tracking, isAuthenticated
#### 3. **Login Page** (`src/app/login/page.tsx`)
- ✅ Email-Input statt Username+Password
- ✅ "Email gesendet" Nachricht nach Submit
- ✅ Option, neue Email anzufordern
#### 4. **Signup Page** (`src/app/signup/page.tsx`)
- ✅ Email-Input nur (kein Username/Password mehr)
- ✅ "Email gesendet" Nachricht nach Submit
- ✅ Link zu Login
#### 5. **Neue Pages**
-`src/app/verify-signup/page.tsx` - Signup-Token Verifizierung
- Token aus URL auslesen
- API aufrufen
- JWT speichern
- Zu Dashboard umleiten
-`src/app/verify-login/page.tsx` - Login-Token Verifizierung
- Token aus URL auslesen
- API aufrufen
- JWT speichern
- Zu Dashboard umleiten
#### 6. **Dashboard** (`src/app/dashboard/page.tsx`)
- ✅ Container Slug anzeigen
- ✅ Email statt Username in Header
- ✅ Service-URL nutzt Slug
## 🚀 Erste Schritte nach Deployment
### 1. SMTP konfigurieren
Stelle sicher, dass deine `.env` folgendes enthält:
```
SMTP_HOST=dein-smtp-server.com
SMTP_PORT=587
SMTP_USER=deine-email@domain.com
SMTP_PASSWORD=dein-app-passwort
SMTP_FROM=noreply@domain.com
FRONTEND_URL=https://coder.deine-domain.com
```
**Datenbank:** Wird automatisch beim Start erstellt (alle Tabellen inkl. MagicLinkToken)
### 2. Magic Link Einstellungen anpassen (optional)
```env
# Token Gültigkeitsdauer in Sekunden (Standard: 900 = 15 Minuten)
MAGIC_LINK_TOKEN_EXPIRY=900
# Rate-Limiting: Max Tokens pro Stunde (Standard: 3)
MAGIC_LINK_RATE_LIMIT=3
```
## 📧 User Journey
### Registrierung
1. User klickt auf "Registrierung"
2. Gibt Email ein
3. Backend sendet Magic Link per Email (Gültig 15 Minuten)
4. User klickt Link → Token wird verifiziert
5. Account wird erstellt & Container spawnt
6. JWT wird gespeichert
7. Auto-Redirect zu Dashboard
### Login
1. User klickt auf "Login"
2. Gibt Email ein
3. Backend sendet Magic Link per Email
4. User klickt Link → Token wird verifiziert
5. JWT wird gespeichert
6. Auto-Redirect zu Dashboard
## 🔗 Container URLs
**Alt (deprecated):**
```
https://coder.domain.com/username
```
**Neu (mit Slug):**
```
https://coder.domain.com/u-a3f9c2d1 # Beispiel: erste 12 Zeichen von SHA256(email)
```
Der Slug ist eindeutig und kann im Dashboard angesehen werden.
## 🔒 Security Features
- **One-Time Use Tokens** - Magic Link kann nur einmal verwendet werden
- **Token Expiration** - Tokens verfallen nach 15 Minuten
- **Rate Limiting** - Max 3 Token-Anfragen pro Email pro Stunde
- **User Enumeration Protection** - Gleiche Meldung ob Email registriert oder nicht
- **Container Isolation** - User-Container haben keinen Zugriff auf Docker Socket
- **No Passwords** - Keine Passwort-Speicherung, kein Passwort-Reset möglich
## 📝 Wichtige Änderungen im Überblick
| Bereich | Alt | Neu |
|---------|-----|-----|
| **Login Feld** | Username + Password | Email nur |
| **Signup Felder** | Username + Email + Password | Email nur |
| **Container ID** | user-{username}-{id} | user-{slug}-{id} |
| **Container URL** | /username | /{slug} |
| **User Identifier** | Username | Email |
| **Authentifizierung** | Username/Password | Magic Link (Email) |
| **Auth-Endpunkte** | /verify | /verify-signup, /verify-login |
## 🐛 Troubleshooting
### "Email konnte nicht gesendet werden"
- Überprüfe SMTP-Konfiguration in `.env`
- Teste: `docker logs spawner` → SMTP Fehler?
- Sind alle SMTP-Credentials korrekt?
### "Token ist abgelaufen"
- Standard-Expiration: 15 Minuten
- Kann in `.env` angepasst werden: `MAGIC_LINK_TOKEN_EXPIRY=900`
### Rate-Limiting blockiert
- Max 3 Requests pro Email pro Stunde
- Warte 1 Stunde oder ändere in `.env`: `MAGIC_LINK_RATE_LIMIT=5`
### Container spawnt nicht
- `docker logs spawner` überprüfen
- Container Template Image existiert? `docker images | grep user-template`
- Traefik Network konfiguriert?
## 📚 Weitere Ressourcen
- **CLAUDE.md** - Projekt-Übersicht und Architektur
- **Backend Logs** - `docker logs spawner`
- **Frontend Logs** - Browser Console (F12)
- **Container Logs** - `docker logs user-{slug}-{id}`
## ✨ Nächste Phase (optional)
1. **Admin Magic Link** - Admins können Benutzern Magic Links senden
2. **Two-Factor Auth** - Optional 2FA mit TOTP
3. **WebAuthn** - Biometric/FIDO2 support
4. **Session Management** - Token-Refresh, Logout überall
---
**Implementiert von:** Claude Code
**Datum:** 2026-01-31
**Version:** 1.0.0 (Passwordless Auth)