The old key comparison was wrong — the localStorage key may have been
randomly generated rather than PBKDF2-derived. Fix:
- Add /accounts/check-password/ to validate the old password server-side
before touching any keys
- Use the localStorage key directly as the old decryption key (it is
always the correct source of truth, regardless of how it was generated)
- Derive the new key from the new password via PBKDF2
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Uses Django's built-in PasswordChangeForm and update_session_auth_hash
so the session stays valid after the change. Form is hidden in a
<details> element and opens automatically on validation errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>