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

7.4 KiB

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: USERNAMEUSER_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)

# 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)