Im Konto-Menü (Profilbild) gibt es unter "Konto" jetzt "Passwort ändern". Die Seite fragt aktuelles Passwort, neues Passwort und Wiederholung ab (mindestens 8 Zeichen, "Passwörter anzeigen"). Am Handy ist sie eine Unterseite des Tabs, aus dem man kam; Direktlink /dashboard.html#passwort. Im Ansichtsmodus ist sie gesperrt. Server (POST /api/auth/password): - Das aktuelle Passwort wird über eine kurze Appwrite-Session geprüft, die gleich wieder gelöscht wird. Tippfehler lösen nicht die globale Login-Sperre aus. Es gibt höchstens 5 Prüfungen je Kunde und Stunde, das bleibt unter Appwrites Grenze (10 Anmeldungen je E-Mail und Stunde). - Das neue Passwort wird über die Users-API gesetzt. Fehler von Appwrite (zuletzt benutzt, zu häufig, persönliche Daten) kommen als verständliche Meldung. - Die Klartext-Kopie in customers.portalPassword und ein Alt-Eintrag in den Notizen werden geleert. Ein selbst gewähltes Passwort wird nie im Klartext gespeichert; das Ticketsystem zeigt dann "–". - Der Kunde bekommt eine Bestätigungsmail (Kopie an die Admins wie bei allen Kundenmails). Andere Geräte werden abgemeldet: Kunden-Sessions merken sich beim Login den Passwortstand (Appwrite passwordUpdate, pwv). requireSession und /api/auth/me prüfen ihn, 60 s zwischengespeichert. Das gilt auch, wenn wir im Ticketsystem ein neues Passwort setzen. Ist Appwrite nicht erreichbar, bleibt die Session gültig. Sessions von vor diesem Update haben keinen Stand und laufen normal aus (höchstens 7 Tage). Login-Seite: Hinweis auf "Passwort ändern" statt "über das Ticketsystem", dazu "Passwort vergessen?" mit support@webklar.com. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
64 lines
2.3 KiB
JavaScript
64 lines
2.3 KiB
JavaScript
import { getUserById } from './appwriteAdmin.js'
|
|
|
|
/**
|
|
* Passwortstand je Appwrite-Konto (users.passwordUpdate, aendert sich nur mit dem Passwort).
|
|
* Kunden-Sessions merken sich den Stand beim Login (pwv). Wird das Passwort geaendert - im
|
|
* Kundenbereich oder durch uns im Ticketsystem -, gelten aeltere Sessions nicht mehr: der Kunde
|
|
* ist auf allen anderen Geraeten abgemeldet. Sessions ohne pwv (vor dieser Pruefung angemeldet)
|
|
* laufen normal aus. Kurz zwischengespeichert, damit nicht jede API-Anfrage Appwrite fragt;
|
|
* Aenderungen ueber diesen Server gelten sofort (rememberPasswordVersion).
|
|
*/
|
|
const CACHE_MS = 60 * 1000
|
|
const cache = new Map() // appwriteUserId -> { value, at }
|
|
let lastWarnAt = 0
|
|
|
|
function remember(userId, value) {
|
|
if (cache.size > 2000) {
|
|
const now = Date.now()
|
|
for (const [key, entry] of cache) if (now - entry.at > CACHE_MS) cache.delete(key)
|
|
}
|
|
cache.set(userId, { value, at: Date.now() })
|
|
}
|
|
|
|
export function rememberPasswordVersion(userId, passwordUpdate) {
|
|
if (userId) remember(userId, passwordUpdate || '')
|
|
}
|
|
|
|
export function forgetPasswordVersion(userId) {
|
|
cache.delete(userId)
|
|
}
|
|
|
|
/** Aktueller Stand; '' = Konto ohne Passwort bzw. geloescht. Wirft bei Appwrite-Fehlern. */
|
|
async function currentPasswordVersion(userId) {
|
|
const hit = cache.get(userId)
|
|
if (hit && Date.now() - hit.at < CACHE_MS) return hit.value
|
|
let value
|
|
try {
|
|
const user = await getUserById(userId)
|
|
value = user?.passwordUpdate || ''
|
|
} catch (err) {
|
|
if (err.status !== 404) throw err
|
|
value = ''
|
|
}
|
|
remember(userId, value)
|
|
return value
|
|
}
|
|
|
|
/**
|
|
* false, wenn die Kunden-Session von vor der letzten Passwortaenderung stammt. Admin-Sessions
|
|
* ("Als Kunde ansehen") und Sessions ohne pwv werden nicht geprueft. Ist Appwrite nicht
|
|
* erreichbar, gilt die Session weiter (sonst waeren bei jeder Stoerung alle abgemeldet).
|
|
*/
|
|
export async function isSessionPasswordCurrent(session) {
|
|
if (!session?.pwv || session.role === 'admin' || !session.appwriteUserId) return true
|
|
try {
|
|
return (await currentPasswordVersion(session.appwriteUserId)) === session.pwv
|
|
} catch (err) {
|
|
if (Date.now() - lastWarnAt > 10 * 60 * 1000) {
|
|
lastWarnAt = Date.now()
|
|
console.warn('[auth] Passwortstand nicht prüfbar, Session gilt weiter:', err.message)
|
|
}
|
|
return true
|
|
}
|
|
}
|