Found by the payload-key sweep required by #46 (see PR #75).
What was found
ChangeEmailView asks for the account password to confirm an email change, and
APIClient.requestEmailChange(newEmail:password:) (Services/APIClient.swift:148-152) sends
{ newEmail, password }.
app/api/user/change-email/request/route.ts reads only newEmail (:50-51). password,
bcrypt and compare appear nowhere in the file. The password is accepted and dropped.
Why it matters
This is not data loss — nothing is lost — but the iOS UI implies a re-authentication step that does
not happen. Anyone holding a live session or Bearer token can start an email change without proving
they know the password, while the UI states otherwise. Email change is an account-recovery-adjacent
action, so the check being cosmetic is worth a decision rather than an accident.
Ask
Pick one and make the client match:
- Verify it — compare
password against the stored hash before issuing the change token, and
401/403 on mismatch. iOS needs no change.
- Drop it — if the confirmation link to the new address is considered sufficient, remove the
password field from ChangeEmailView and stop sending it, so the UI stops implying a check that
isn't there.
Acceptance criteria
Files: backend app/api/user/change-email/request/route.ts; iOS
Services/APIClient.swift:148, Views/ChangeEmailView.swift.
Found by the payload-key sweep required by #46 (see PR #75).
What was found
ChangeEmailViewasks for the account password to confirm an email change, andAPIClient.requestEmailChange(newEmail:password:)(Services/APIClient.swift:148-152) sends{ newEmail, password }.app/api/user/change-email/request/route.tsreads onlynewEmail(:50-51).password,bcryptandcompareappear nowhere in the file. The password is accepted and dropped.Why it matters
This is not data loss — nothing is lost — but the iOS UI implies a re-authentication step that does
not happen. Anyone holding a live session or Bearer token can start an email change without proving
they know the password, while the UI states otherwise. Email change is an account-recovery-adjacent
action, so the check being cosmetic is worth a decision rather than an accident.
Ask
Pick one and make the client match:
passwordagainst the stored hash before issuing the change token, and401/403on mismatch. iOS needs no change.password field from
ChangeEmailViewand stop sending it, so the UI stops implying a check thatisn't there.
Acceptance criteria
Files: backend
app/api/user/change-email/request/route.ts; iOSServices/APIClient.swift:148,Views/ChangeEmailView.swift.