feat(opaque): improve password change

- rebuild the opaque envoloppe
- revoke all other useer's sessions
- send a security email to user
This commit is contained in:
Edouard Vanbelle
2026-08-05 23:35:05 +02:00
parent 60cf9d976b
commit 21607e3e7f
29 changed files with 611 additions and 33 deletions
+11 -1
View File
@@ -71,7 +71,17 @@
const usernameClaimed = $derived(!!session.user?.username);
const isAdmin = $derived(session.user?.role === 'admin');
const canEditImage = $derived(session.user?.can_edit_image === true && isLocal);
const showPasswordCard = $derived(isLocal && passwordLoginEnabled);
// Show the change-password card when the user CAN change their
// local password: they have `password_hash` on file AND the
// deployment offers password login (backend `change_password`
// refuses on either count — see `AuthApplicationService::change_password`).
// Distinct from the OLD `isLocal && passwordLoginEnabled` gate,
// which refused any SSO-linked account regardless of whether they
// carried a local password. Hybrid accounts (OIDC + local
// password) are a legitimate posture and MUST be able to rotate
// their local credential; the new gate lets them, and the backend
// refusal covers the pure-SSO case where has_password is false.
const showPasswordCard = $derived((session.user?.has_password ?? false) && passwordLoginEnabled);
/**
* Mandatory change-password mode. TRUE when the backend has
+4 -2
View File
@@ -14,7 +14,8 @@ const { session, ui } = vi.hoisted(() => ({
role: 'admin',
storage_used_bytes: 100,
storage_quota_bytes: 1000,
is_external: false
is_external: false,
has_password: true
}
},
ui: { notify: vi.fn() }
@@ -54,7 +55,8 @@ beforeEach(() => {
role: 'admin',
storage_used_bytes: 100,
storage_quota_bytes: 1000,
is_external: false
is_external: false,
has_password: true
};
m(profile.listAppPasswords).mockResolvedValue([]);
m(profile.updateProfile).mockResolvedValue(undefined);