Skip to content

fix: route password rotation through the typed apiService contract - #400

Merged
Kingvic300 merged 1 commit into
Txio-labs:mainfrom
Shadow-MMN:fix/password-rotation-contract
Aug 17, 2026
Merged

fix: route password rotation through the typed apiService contract#400
Kingvic300 merged 1 commit into
Txio-labs:mainfrom
Shadow-MMN:fix/password-rotation-contract

Conversation

@Shadow-MMN

Copy link
Copy Markdown
Contributor

Summary

Password rotation had two incompatible client implementations for one backend route, and the one actually wired to the "Rotate Password" button sent a payload the deployed backend rejects. This consolidates everything onto the typed apiService.updatePassword contract, corrects its payload shape to match the deployed backend, and adds test coverage asserting the exact request body.

Root cause

  • SecurityTab.tsx submitted rotation through a standalone axios client (src/lib/api.ts) posting { current_password, new_password, confirm_password } — no email.
  • apiService.updatePassword(email, newPassword) in src/services/api.ts posted { email, new_password } — and had zero callers anywhere in src/.
  • Neither matched the deployed backend: verified live against txio-oyac.onrender.com, /auth/update-password rejects a body without email (HTTP 422 missing field email). (The backend repo's main branch has since moved to a claims-based handler requiring current_password instead — the deployed instance is running the older contract.)

Changes

  • src/services/api.tsupdatePassword(email, currentPassword, newPassword) now POSTs { email, current_password, new_password }. Sending both fields works against the deployed instance today (it uses email, ignores the rest) and survives the backend upgrade to the claims-based handler (which uses current_password, ignores email).
  • src/components/AuthModal/tabs/SecurityTab.tsx — the Rotate Password form now calls apiService.updatePassword(user.email, currentPassword, newPassword). Error handling switched from axios's error.response?.data?.message to ApiError.message (which surfaces the backend's message).
  • src/lib/api.tsdeleted. Its only consumer was SecurityTab; the duplicate axios client and its dead updatePassword export are gone.
  • src/services/api.test.ts — new test asserting the exact request body ({ email, current_password, new_password }) and URL for /auth/update-password.
  • src/components/AuthModal/tabs/SecurityTab.test.tsx — removed the dead lib/api mock; added tests asserting the form calls updatePassword with the signed-in email + passwords, and that invalid forms (mismatched confirmation) never submit.

Verification

  • Live end-to-end against the deployed backend: login → update-password with the new payload → HTTP 200 → re-login confirms success (test password was rotated back afterwards)
  • npm test — 112 tests pass (17 files)
  • [x]npm run lint — 0 errors
  • tsc --noEmit — no errors in changed files (pre-existing errors only in untouched src/wallet/*.test.ts)
  • Session review functionality unaffected (getSessions/revokeSession unchanged)

Acceptance criteria

  • Exactly one client-side implementation for password rotation, used by the UI
  • Request body matches what the deployed /auth/update-password handler expects
  • Tests assert the specific request body/fields, not just that a POST occurred
  • No dangling updatePassword / duplicate axios client remains
  • Existing session-review functionality unaffected
  • CI passes (lint, tests, typecheck)

Notes

  • The deployed backend resolves the account from email in the body (legacy contract). When the backend deploys the claims-based handler from txio-backend main, email can be dropped from this payload in a one-line change — current_password is already being sent, so nothing breaks in between.
  • Regression risk is low and scoped to SecurityTab's submit path and the shared API client.

Closes #382.

SecurityTab rotated passwords via a duplicate axios client
(src/lib/api.ts) that posted a payload the deployed backend rejects,
while the typed apiService.updatePassword contract had zero callers.

Route the form through apiService.updatePassword, which now sends
{ email, current_password, new_password }: the deployed
/auth/update-password handler requires "email" (older contract) and
the backend repo's current handler requires "current_password", so
sending both works against the deployed instance today and survives
the backend upgrade. Delete the duplicate client.

Add request-body assertions in api.test.ts and SecurityTab tests
covering the submitted arguments and client-side validation.

Closes Txio-labs#382
@vercel

vercel Bot commented Aug 17, 2026

Copy link
Copy Markdown

@Shadow-MMN is attempting to deploy a commit to the oladimejivictor611-5012's projects Team on Vercel.

A member of the Team first needs to authorize it.

@Kingvic300
Kingvic300 merged commit 7646015 into Txio-labs:main Aug 17, 2026
6 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SecurityTab password rotation sends a different request shape than the app's own documented updatePassword contract

2 participants