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>
214 lines
7.4 KiB
Markdown
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)
|