internal/session: Selbstbedienungs-Sitzungsuebersicht mit widerrufbarem, SERVERSEITIGEM Zustand — bewusst ein separater Mechanismus neben IAM-02s zustandslosem JWT (das fuer schnelle Modul-zu-Modul-Verifikation ohne Core-Rueckfrage gewaehlt wurde, siehe API-05). Sofortige Widerrufbarkeit ist fuer dieses Selbstbedienungs-Sicherheitsfeature wichtiger als Zustandslosigkeit — kein Konflikt mit der API-05-Entscheidung, da es sich um verschiedene Anwendungsfaelle handelt. Store.Create gibt den Klartext-Sitzungs-Token nur einmal zurueck, gespeichert wird ausschliesslich der SHA-256-Hash. Validate prueft direkt gegen die Datenbank (kein Cache) und aktualisiert last_seen_at bei jedem Zugriff (Akzeptanzkriterium 1). Revoke/RevokeAllExcept setzen revoked_at — ein widerrufenes Token ist ab dem naechsten Validate-Aufruf sofort ungueltig (Akzeptanzkriterium 2), RevokeAllExcept beendet gezielt alle Sitzungen ausser der aktuellen (Akzeptanzkriterium 3). LoginAndCreateSession verwendet auth.VerifyPassword (IAM-02) fuer den timing-safen Credential-Check — kein zweiter Passwort-Pruefmechanismus, liefert aber ein Sitzungs-Token statt eines JWT zurueck. Pruefungen (ausgefuehrt auf root@192.168.1.131, go build/vet/test PASS): 1. Zwei Sitzungen desselben Nutzers angelegt, beide in der Uebersicht sichtbar — TestListForUser_ShowsAllActiveSessions. PASS. 2. Widerruf einer Sitzung macht das Token sofort ungueltig — TestRevoke_InvalidatesTokenImmediately. PASS. 3. "Alle anderen beenden" funktioniert korrekt, aktuelle bleibt aktiv — TestRevokeAllExcept_KeepsCurrentSessionActive: zwei fremde Sitzungen widerrufen, aktuelle bleibt gueltig und einzig uebrige in der Liste. PASS. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
36 lines
1.2 KiB
Go
36 lines
1.2 KiB
Go
package session
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"gitea.perlbach24.de/scripte/nexarch/internal/auth"
|
|
"gitea.perlbach24.de/scripte/nexarch/internal/user"
|
|
)
|
|
|
|
// LoginAndCreateSession prueft die Anmeldedaten mit demselben timing-safen
|
|
// bcrypt-Vergleich wie IAM-02s LoginService (kein zweiter Credential-Check),
|
|
// erzeugt bei Erfolg aber eine widerrufbare Sitzung statt eines
|
|
// zustandslosen JWT — das ist der Bearer-Token fuer diese Selbstbedienungs-
|
|
// Funktion.
|
|
func LoginAndCreateSession(ctx context.Context, users *user.TenantUserStore, sessions *Store, email, password, deviceInfo, ip string) (sessionToken string, err error) {
|
|
creds, err := users.GetByEmailForAuth(ctx, email)
|
|
if err != nil {
|
|
return "", fmt.Errorf("anmeldedaten pruefen: %w", err)
|
|
}
|
|
if creds.User.Status != user.StatusActive {
|
|
return "", fmt.Errorf("konto nicht aktiv")
|
|
}
|
|
// Wiederverwendung von IAM-02s timing-safem bcrypt-Vergleich, kein
|
|
// zweiter Passwort-Pruefmechanismus.
|
|
if !auth.VerifyPassword(creds.PasswordHash, password) {
|
|
return "", fmt.Errorf("anmeldedaten ungueltig")
|
|
}
|
|
|
|
_, sessionToken, err = sessions.Create(ctx, creds.User.ID, deviceInfo, ip)
|
|
if err != nil {
|
|
return "", fmt.Errorf("sitzung anlegen: %w", err)
|
|
}
|
|
return sessionToken, nil
|
|
}
|