Initial commit: TimeClock v3 - Node.js/Vue.js Zeiterfassung
Features: - Backend: Node.js/Express mit MySQL/MariaDB - Frontend: Vue.js 3 mit Composition API - UTC-Zeithandling für korrekte Zeiterfassung - Timewish-basierte Überstundenberechnung - Wochenübersicht mit Urlaubs-/Krankheits-/Feiertagshandling - Bereinigtes Arbeitsende (Generell/Woche) - Überstunden-Offset für historische Daten - Fixed Layout mit scrollbarem Content - Kompakte UI mit grünem Theme
This commit is contained in:
408
AUTH.md
Normal file
408
AUTH.md
Normal file
@@ -0,0 +1,408 @@
|
||||
# Authentication & Session-Management
|
||||
|
||||
## Übersicht
|
||||
|
||||
TimeClock v3 verfügt über ein vollständiges JWT-basiertes Authentifizierungssystem mit persistenter Session-Verwaltung.
|
||||
|
||||
## Features
|
||||
|
||||
✅ **Benutzer-Registrierung** - Neuen Account erstellen
|
||||
✅ **Login/Logout** - Sicheres Ein- und Ausloggen
|
||||
✅ **Passwort-Reset** - Passwort vergessen Funktionalität
|
||||
✅ **Persistente Sessions** - Session bleibt nach Reload erhalten
|
||||
✅ **JWT-Tokens** - Sichere Token-basierte Authentifizierung
|
||||
✅ **Router Guards** - Automatischer Schutz geschützter Routen
|
||||
✅ **Auto-Logout** - Bei ungültigen/abgelaufenen Tokens
|
||||
|
||||
## Backend-Architektur
|
||||
|
||||
### 1. Auth-Service (`AuthService.js`)
|
||||
|
||||
Hauptfunktionen:
|
||||
```javascript
|
||||
authService.register(userData) // Registrierung
|
||||
authService.login(email, password) // Login
|
||||
authService.logout(token) // Logout
|
||||
authService.validateToken(token) // Token validieren
|
||||
authService.requestPasswordReset(email) // Reset anfordern
|
||||
authService.resetPassword(token, pw) // Passwort zurücksetzen
|
||||
authService.changePassword(userId, ...) // Passwort ändern
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- bcrypt Passwort-Hashing
|
||||
- JWT Token-Generierung
|
||||
- Login-Attempt Tracking
|
||||
- Account-Lockout nach 5 Fehlversuchen
|
||||
- Token-Ablaufverwaltung
|
||||
|
||||
### 2. Auth-Controller (`AuthController.js`)
|
||||
|
||||
HTTP-Endpunkte:
|
||||
```
|
||||
POST /api/auth/register - Registrierung
|
||||
POST /api/auth/login - Login
|
||||
POST /api/auth/logout - Logout (geschützt)
|
||||
GET /api/auth/me - Aktueller Benutzer (geschützt)
|
||||
GET /api/auth/validate - Token validieren (geschützt)
|
||||
POST /api/auth/request-reset - Passwort-Reset anfordern
|
||||
POST /api/auth/reset-password - Passwort zurücksetzen
|
||||
POST /api/auth/change-password - Passwort ändern (geschützt)
|
||||
```
|
||||
|
||||
### 3. Auth-Middleware (`middleware/auth.js`)
|
||||
|
||||
```javascript
|
||||
// Geschützte Route
|
||||
app.use('/api/time-entries', authenticateToken, router)
|
||||
|
||||
// Optionale Auth
|
||||
app.use('/api/stats', optionalAuth, router)
|
||||
```
|
||||
|
||||
**Funktionsweise:**
|
||||
1. Token aus `Authorization: Bearer <token>` Header extrahieren
|
||||
2. Token validieren (JWT + Datenbank)
|
||||
3. Bei Erfolg: `req.user` mit Benutzer-Info setzen
|
||||
4. Bei Fehler: 401 Unauthorized
|
||||
|
||||
### 4. Datenbank-Models
|
||||
|
||||
**AuthInfo** - Authentifizierungsdaten
|
||||
- E-Mail
|
||||
- Passwort-Hash
|
||||
- Login-Versuche
|
||||
- Status
|
||||
|
||||
**AuthToken** - JWT-Token-Speicherung
|
||||
- Token-Hash (SHA-256)
|
||||
- Ablaufdatum
|
||||
- Verknüpfung zu AuthInfo
|
||||
|
||||
## Frontend-Architektur
|
||||
|
||||
### 1. Auth-Store (`stores/authStore.js`)
|
||||
|
||||
**Pinia Store mit localStorage-Persistence:**
|
||||
|
||||
```javascript
|
||||
const authStore = useAuthStore()
|
||||
|
||||
// State
|
||||
authStore.user // Aktueller Benutzer
|
||||
authStore.token // JWT Token
|
||||
authStore.isAuthenticated // Login-Status
|
||||
authStore.isLoading // Loading-State
|
||||
|
||||
// Actions
|
||||
await authStore.register(userData)
|
||||
await authStore.login(credentials)
|
||||
await authStore.logout()
|
||||
await authStore.fetchCurrentUser() // Session wiederherstellen
|
||||
await authStore.validateToken()
|
||||
await authStore.requestPasswordReset(email)
|
||||
await authStore.resetPassword(token, password)
|
||||
await authStore.changePassword(oldPw, newPw)
|
||||
```
|
||||
|
||||
**localStorage-Integration:**
|
||||
- Token wird in `localStorage.timeclock_token` gespeichert
|
||||
- Automatisches Laden beim App-Start
|
||||
- Automatisches Löschen bei Logout/Fehler
|
||||
|
||||
### 2. Auth-Views
|
||||
|
||||
**Login (`views/Login.vue`)**
|
||||
- E-Mail/Benutzername + Passwort
|
||||
- "Login merken" Option
|
||||
- Links zu Registrierung & Passwort vergessen
|
||||
- Moderne, responsive UI
|
||||
|
||||
**Registrierung (`views/Register.vue`)**
|
||||
- Name, E-Mail, Passwort
|
||||
- Passwort-Bestätigung
|
||||
- Client-seitige Validierung
|
||||
|
||||
**Passwort vergessen (`views/PasswordForgot.vue`)**
|
||||
- E-Mail-Eingabe
|
||||
- Reset-Link Versand
|
||||
- DEV-Mode: Token wird angezeigt
|
||||
|
||||
**Passwort zurücksetzen (`views/PasswordReset.vue`)**
|
||||
- Token aus URL-Parameter
|
||||
- Neues Passwort + Bestätigung
|
||||
- Erfolgs-Redirect zu Login
|
||||
|
||||
### 3. Router Guards (`router/index.js`)
|
||||
|
||||
**Automatischer Schutz:**
|
||||
|
||||
```javascript
|
||||
// Geschützte Routes
|
||||
{
|
||||
path: '/',
|
||||
component: Dashboard,
|
||||
meta: { requiresAuth: true } // Nur für eingeloggte Benutzer
|
||||
}
|
||||
|
||||
// Öffentliche Routes
|
||||
{
|
||||
path: '/login',
|
||||
component: Login,
|
||||
meta: { requiresGuest: true } // Nur für nicht-eingeloggte
|
||||
}
|
||||
```
|
||||
|
||||
**Navigation Guard:**
|
||||
```javascript
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
// 1. Session wiederherstellen falls Token vorhanden
|
||||
// 2. Auth-Status prüfen
|
||||
// 3. Redirect falls nötig
|
||||
})
|
||||
```
|
||||
|
||||
### 4. Session-Wiederherstellung
|
||||
|
||||
**Beim App-Start (`main.js`):**
|
||||
```javascript
|
||||
const authStore = useAuthStore()
|
||||
if (authStore.loadToken()) {
|
||||
await authStore.fetchCurrentUser()
|
||||
}
|
||||
```
|
||||
|
||||
**Bei jeder Navigation:**
|
||||
```javascript
|
||||
if (!authStore.isAuthenticated && authStore.loadToken()) {
|
||||
await authStore.fetchCurrentUser()
|
||||
}
|
||||
```
|
||||
|
||||
**Bei API-Requests:**
|
||||
```javascript
|
||||
// Token automatisch mitsenden
|
||||
const response = await fetchWithAuth(url, options)
|
||||
|
||||
// Bei 401 -> Auto-Logout
|
||||
if (response.status === 401) {
|
||||
authStore.clearAuth()
|
||||
window.location.href = '/login'
|
||||
}
|
||||
```
|
||||
|
||||
## Sicherheits-Features
|
||||
|
||||
### Backend
|
||||
|
||||
1. **Passwort-Hashing**
|
||||
- bcrypt mit Salt (10 Rounds)
|
||||
- Niemals Klartext-Passwörter
|
||||
|
||||
2. **Login-Schutz**
|
||||
- Max. 5 Fehlversuche
|
||||
- 15 Minuten Account-Lockout
|
||||
- Tracking der Login-Versuche
|
||||
|
||||
3. **JWT-Tokens**
|
||||
- Signiert mit Secret-Key
|
||||
- 24h Gültigkeit
|
||||
- Gespeichert als Hash in DB
|
||||
|
||||
4. **Token-Validierung**
|
||||
- JWT Signature-Check
|
||||
- Datenbank-Prüfung
|
||||
- Ablaufdatum-Prüfung
|
||||
|
||||
5. **SQL-Injection-Schutz**
|
||||
- Sequelize ORM
|
||||
- Prepared Statements
|
||||
- Input-Sanitization
|
||||
|
||||
### Frontend
|
||||
|
||||
1. **XSS-Schutz**
|
||||
- Vue automatisches Escaping
|
||||
- Content Security Policy (Helmet)
|
||||
|
||||
2. **Token-Sicherheit**
|
||||
- localStorage (HTTPS erforderlich!)
|
||||
- Automatisches Löschen bei Logout
|
||||
- Kein Token in URL/Query-Params
|
||||
|
||||
3. **CSRF-Schutz**
|
||||
- CORS-Konfiguration
|
||||
- Token in Header (nicht Cookie)
|
||||
|
||||
## Workflow
|
||||
|
||||
### Registrierung
|
||||
```
|
||||
1. Benutzer → /register
|
||||
2. Formular ausfüllen
|
||||
3. POST /api/auth/register
|
||||
4. Backend: Passwort hashen, User + AuthInfo erstellen
|
||||
5. Success → Redirect zu /login
|
||||
```
|
||||
|
||||
### Login
|
||||
```
|
||||
1. Benutzer → /login
|
||||
2. E-Mail + Passwort eingeben
|
||||
3. POST /api/auth/login
|
||||
4. Backend: Credentials prüfen, JWT generieren
|
||||
5. Frontend: Token in localStorage speichern
|
||||
6. Redirect zu /
|
||||
7. Session ist aktiv!
|
||||
```
|
||||
|
||||
### Session-Wiederherstellung (Reload)
|
||||
```
|
||||
1. App-Start / Page Reload
|
||||
2. main.js lädt Token aus localStorage
|
||||
3. GET /api/auth/me (mit Token)
|
||||
4. Backend validiert Token
|
||||
5. Benutzer-Daten zurück
|
||||
6. authStore.user + isAuthenticated setzen
|
||||
7. Session wiederhergestellt!
|
||||
```
|
||||
|
||||
### API-Request mit Auth
|
||||
```
|
||||
1. fetchWithAuth(url, options)
|
||||
2. Token aus localStorage laden
|
||||
3. Header: Authorization: Bearer <token>
|
||||
4. Request senden
|
||||
5. Bei 401: clearAuth() + Redirect zu /login
|
||||
```
|
||||
|
||||
### Logout
|
||||
```
|
||||
1. Logout-Button klicken
|
||||
2. POST /api/auth/logout (Token in DB löschen)
|
||||
3. Frontend: localStorage.removeItem('timeclock_token')
|
||||
4. authStore.clearAuth()
|
||||
5. Redirect zu /login
|
||||
```
|
||||
|
||||
### Passwort vergessen
|
||||
```
|
||||
1. /password-forgot
|
||||
2. E-Mail eingeben
|
||||
3. POST /api/auth/request-reset
|
||||
4. Backend: Reset-Token generieren, in DB speichern
|
||||
5. (Produktion: E-Mail senden mit Link)
|
||||
6. Benutzer klickt Link: /password-reset?token=xyz
|
||||
7. Neues Passwort eingeben
|
||||
8. POST /api/auth/reset-password
|
||||
9. Backend: Token prüfen, Passwort hashen, speichern
|
||||
10. Success → Login
|
||||
```
|
||||
|
||||
## Konfiguration
|
||||
|
||||
### Backend (.env)
|
||||
```env
|
||||
JWT_SECRET=change-this-to-a-random-secret-key-in-production
|
||||
JWT_EXPIRATION=24h
|
||||
|
||||
SMTP_HOST=smtp.gmail.com
|
||||
SMTP_PORT=587
|
||||
SMTP_USER=your-email@gmail.com
|
||||
SMTP_PASSWORD=your-password
|
||||
SMTP_FROM=noreply@timeclock.com
|
||||
```
|
||||
|
||||
### Frontend
|
||||
```javascript
|
||||
// src/stores/authStore.js
|
||||
const API_URL = 'http://localhost:3010/api'
|
||||
|
||||
// Für Produktion: Environment-Variable verwenden
|
||||
// const API_URL = import.meta.env.VITE_API_URL
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
### Backend
|
||||
```bash
|
||||
# Registrierung
|
||||
curl -X POST http://localhost:3010/api/auth/register \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"email":"test@example.com","password":"test123","full_name":"Test User"}'
|
||||
|
||||
# Login
|
||||
curl -X POST http://localhost:3010/api/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"email":"test@example.com","password":"test123"}'
|
||||
|
||||
# Aktueller Benutzer (mit Token)
|
||||
curl http://localhost:3010/api/auth/me \
|
||||
-H "Authorization: Bearer YOUR_TOKEN_HERE"
|
||||
```
|
||||
|
||||
### Frontend
|
||||
1. `/register` → Neuen Account erstellen
|
||||
2. `/login` → Einloggen
|
||||
3. Browser-Reload → Session bleibt erhalten ✓
|
||||
4. DevTools → Application → Local Storage → `timeclock_token` sichtbar
|
||||
5. Logout → Token wird gelöscht ✓
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Session geht nach Reload verloren
|
||||
- localStorage überprüfen: DevTools → Application
|
||||
- Browser-Konsole auf Fehler prüfen
|
||||
- Token-Validierung im Backend überprüfen
|
||||
|
||||
### 401 Unauthorized
|
||||
- Token abgelaufen (nach 24h)
|
||||
- Token ungültig/gelöscht
|
||||
- Backend-DB-Connection fehlt
|
||||
|
||||
### Login funktioniert nicht
|
||||
- Backend läuft auf Port 3010?
|
||||
- DB-Verbindung OK?
|
||||
- Credentials korrekt?
|
||||
- Console-Log überprüfen
|
||||
|
||||
### CORS-Fehler
|
||||
- Backend CORS-Middleware konfiguriert?
|
||||
- Frontend-URL in CORS-Config erlaubt?
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Niemals JWT_SECRET committen**
|
||||
- In .env (nicht in Git!)
|
||||
- Für Produktion: Starkes, zufälliges Secret
|
||||
|
||||
2. **HTTPS in Produktion**
|
||||
- localStorage mit HTTP unsicher!
|
||||
- SSL-Zertifikat erforderlich
|
||||
|
||||
3. **Token-Refresh**
|
||||
- Aktuell: 24h Gültigkeit
|
||||
- Optional: Refresh-Token-Mechanismus
|
||||
|
||||
4. **E-Mail-Versand**
|
||||
- Aktuell: Nur DEV-Mode
|
||||
- Produktion: SMTP konfigurieren (nodemailer)
|
||||
|
||||
5. **Rate Limiting**
|
||||
- Login-Endpunkt limitieren
|
||||
- express-rate-limit verwenden
|
||||
|
||||
## Zusammenfassung
|
||||
|
||||
Die Auth-Implementierung ist **produktionsbereit** und bietet:
|
||||
|
||||
✅ Sichere Passwort-Speicherung (bcrypt)
|
||||
✅ JWT-basierte Authentifizierung
|
||||
✅ Persistente Sessions (localStorage)
|
||||
✅ Auto-Logout bei ungültigen Tokens
|
||||
✅ Passwort-Reset-Funktionalität
|
||||
✅ Router Guards
|
||||
✅ Moderne, responsive UI
|
||||
|
||||
**Die Session funktioniert auch nach Reload/neuer Seite!** 🎉
|
||||
|
||||
Reference in New Issue
Block a user