Skip to content

[ENG-635] feat: Add password reset via OTP - #16600

Merged
yash-learner merged 8 commits into
developfrom
ENG-635-wire-password-reset-via-otp
Jul 31, 2026
Merged

[ENG-635] feat: Add password reset via OTP#16600
yash-learner merged 8 commits into
developfrom
ENG-635-wire-password-reset-via-otp

Conversation

@yash-learner

@yash-learner yash-learner commented Jul 26, 2026

Copy link
Copy Markdown
Member

Proposed Changes

Fixes https://openhealthcarenetwork.atlassian.net/browse/ENG-635

  • Wire password reset via phone number -> OTP
  • Move the common elements between email reset and otp reset into a shared component
  • Add required playwright tests
Password.reset.via.OTP.mp4

Tagging: @ohcnetwork/care-fe-code-reviewers

Merge Checklist

  • Add specs that demonstrate the bug or test the new feature.
  • Update product documentation.
  • Ensure that UI text is placed in I18n files.
  • Prepare a screenshot or demo video for the changelog entry and attach it to the issue.
  • Request peer reviews.
  • Complete QA on mobile devices.
  • Complete QA on desktop devices.
  • Add or update Playwright tests for related changes

Summary by CodeRabbit

  • New Features
    • Added phone-based password reset with OTP, including OTP confirmation, resend countdown, and new-password setup.
  • Improvements
    • Unified the forgot-password experience across login and reset flows with consistent two-step UI and validation.
    • Enhanced the UI for shared-phone scenarios by requesting a username when required.
    • Expanded English localization for email and phone reset messaging.
  • Bug Fixes
    • Improved handling and display of invalid OTP and server-returned reset errors.
  • Tests
    • Added end-to-end coverage for OTP reset success, invalid OTP, and unknown-phone scenarios.

@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Changes

Adds phone-based OTP password reset with typed unauthenticated API routes, reusable password validation fields, shared forgot-password UI integration, localized messages, and Playwright coverage for successful, invalid-OTP, and unknown-phone flows.

Password Reset Experience

Layer / File(s) Summary
Password reset OTP contracts and routes
src/types/otp/otp.ts, src/types/otp/otpApi.ts
Defines password-reset OTP request/response types and adds unauthenticated send and confirmation endpoints.
Reusable password fields and reset validation
src/components/Auth/PasswordFields.tsx, src/components/Auth/ResetPassword.tsx
Adds shared password inputs and validation, then uses them in token-based password reset.
Phone OTP reset flow
src/components/Auth/ForgotPasswordPhone.tsx, tests/helper/user.ts, tests/auth/passwordResetOtp.spec.ts
Implements phone entry, OTP confirmation, resend timing, conditional username handling, error mapping, navigation, disposable-user setup, and end-to-end tests.
Forgot-password panel integration and localized UI
src/components/Auth/ForgotPasswordPanel.tsx, src/components/Auth/Login.tsx, public/locale/en.json
Replaces duplicated login recovery markup with a shared email/phone panel and adds its localized text.

Suggested labels: Tested

Suggested reviewers: amjithtitus09

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: adding password reset via OTP.
Description check ✅ Passed The description follows the template with proposed changes, issue link, tagging, and a mostly complete merge checklist.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ENG-635-wire-password-reset-via-otp

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown

⚠️ Merge Checklist Incomplete

Thank you for your contribution! To help us review your PR efficiently, please complete the merge checklist in your PR description.

Your PR will be reviewed once you have marked the appropriate checklist items.

To update the checklist:

  • Change - [ ] to - [x] for completed items
  • Only check items that are relevant to your PR
  • Leave items unchecked if they don't apply

The checklist helps ensure code quality, testing coverage, and documentation are properly addressed.

@github-actions github-actions Bot added the Type Changes Contains changes in typescript types label Jul 26, 2026
@github-actions

github-actions Bot commented Jul 26, 2026

Copy link
Copy Markdown

🎭 Playwright Test Results

Status: ✅ Passed
Test Shards: 3

Metric Count
Total Tests 337
✅ Passed 336
❌ Failed 0
⏭️ Skipped 1

📊 Detailed results are available in the playwright-final-report artifact.

Run: #10517

@amjithtitus09
amjithtitus09 requested a review from Copilot July 27, 2026 08:11
@yash-learner

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an OTP-based password reset option (via phone number) to the authentication flow, alongside some refactoring to reuse password-field UI/validation.

Changes:

  • Added new OTP API routes and types for password reset via phone/OTP confirmation.
  • Introduced a new “Forgot password” panel with a phone-based reset flow and extracted reusable new-password fields/validation.
  • Added new English i18n strings for the phone reset UX.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/types/otp/otpApi.ts Adds password-reset OTP send/confirm endpoints.
src/types/otp/otp.ts Adds request/response types for password reset OTP confirmation.
src/components/Auth/ResetPassword.tsx Refactors to reuse shared new-password fields + validation; keeps existing reset-by-token flow.
src/components/Auth/NewPasswordFields.tsx New shared component + validator for password/confirm inputs and strength hints.
src/components/Auth/Login.tsx Integrates the new “Forgot password” panel into the login screen.
src/components/Auth/ForgotPasswordPhone.tsx New phone-number OTP password reset UI and mutations.
src/components/Auth/ForgotPasswordPanel.tsx New panel that switches between email reset and phone reset paths.
public/locale/en.json Adds i18n keys for phone reset flow strings.

Comment thread src/components/Auth/ResetPassword.tsx
Comment thread src/components/Auth/ForgotPasswordPhone.tsx
Comment thread src/components/Auth/ForgotPasswordPanel.tsx Outdated
Comment thread src/components/Auth/Login.tsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/Auth/ForgotPasswordPanel.tsx`:
- Around line 103-110: Replace the interactive <li> in the forgot-password
method selector with the existing Button-based interactive pattern used
elsewhere in ForgotPasswordPanel, preserving its styling, translation label, and
setForgotMethod("phone") action so it is keyboard and screen-reader accessible.

In `@src/components/Auth/NewPasswordFields.tsx`:
- Line 58: Document the NewPasswordFields component, including its
password-reset use case and accessibility behavior, following the existing
documentation conventions for reusable components under src/components/. Add the
documentation adjacent to the NewPasswordFields export without changing its
implementation.
- Around line 71-126: Update both PasswordInput instances in NewPasswordFields
to provide programmatic labels, expose their invalid state from
errors.password/errors.confirm, and associate each input with its corresponding
error element via stable ids and aria-describedby. Ensure the error elements use
matching ids and retain the existing validation messaging.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3ac59746-34fc-49f3-925c-f365abde9c43

📥 Commits

Reviewing files that changed from the base of the PR and between bcb288a and 742d5a2.

📒 Files selected for processing (8)
  • public/locale/en.json
  • src/components/Auth/ForgotPasswordPanel.tsx
  • src/components/Auth/ForgotPasswordPhone.tsx
  • src/components/Auth/Login.tsx
  • src/components/Auth/NewPasswordFields.tsx
  • src/components/Auth/ResetPassword.tsx
  • src/types/otp/otp.ts
  • src/types/otp/otpApi.ts

Comment thread src/components/Auth/ForgotPasswordPanel.tsx Outdated
Comment thread src/components/Auth/NewPasswordFields.tsx Outdated
Comment thread src/components/Auth/PasswordFields.tsx
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 27, 2026

Copy link
Copy Markdown

Deploying care-preview with  Cloudflare Pages  Cloudflare Pages

Latest commit: 789e6d4
Status: ✅  Deploy successful!
Preview URL: https://9bcf6a07.care-preview-a7w.pages.dev
Branch Preview URL: https://eng-635-wire-password-reset.care-preview-a7w.pages.dev

View logs

@yash-learner

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Comment thread src/components/Auth/ForgotPasswordPhone.tsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/components/Auth/ForgotPasswordPhone.tsx (2)

402-411: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Confirm button stays enabled even when a required username is missing.

The disabled condition doesn't account for requiresUsername, so the button is clickable when username is required but empty; the click only surfaces the error after client-side validation runs.

🐛 Proposed fix
           disabled={
             confirmPending ||
             otp.length !== OTP_LENGTH ||
             !passwordForm.password ||
-            !passwordForm.confirm
+            !passwordForm.confirm ||
+            (requiresUsername && !username.trim())
           }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Auth/ForgotPasswordPhone.tsx` around lines 402 - 411, Update
the Confirm button’s disabled condition in ForgotPasswordPhone so it also
disables when requiresUsername is true and the required username is empty, while
preserving the existing pending, OTP, password, and confirmation checks.

269-282: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Add the validation wiring at these inputs. PhoneInput already forwards props, but its built-in aria-describedby="phone-input-constraint" gets replaced unless you compose that id with the phone error id. InputOTP should also expose the OTP error via aria-invalid and aria-describedby, with the message marked as an alert.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Auth/ForgotPasswordPhone.tsx` around lines 269 - 282, Update
the PhoneInput and InputOTP validation wiring in ForgotPasswordPhone: preserve
PhoneInput’s existing constraint aria-describedby while composing it with the
phone error element ID, and add aria-invalid plus composed aria-describedby for
the OTP input. Assign the corresponding error-message IDs and mark both
validation messages as alerts.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/Auth/NewPasswordFields.tsx`:
- Around line 76-93: Add autoComplete="new-password" to both PasswordInput
instances in NewPasswordFields, including the new-password field and the
additional password field referenced by the review. Preserve all existing props
and behavior.

In `@tests/helper/user.ts`:
- Line 36: Update the username generation in the test helper to use Date.now()
or faker.string.alphanumeric() instead of the four-digit faker.string.numeric()
suffix, while preserving the existing lowercase first-name prefix and username
format.

---

Outside diff comments:
In `@src/components/Auth/ForgotPasswordPhone.tsx`:
- Around line 402-411: Update the Confirm button’s disabled condition in
ForgotPasswordPhone so it also disables when requiresUsername is true and the
required username is empty, while preserving the existing pending, OTP,
password, and confirmation checks.
- Around line 269-282: Update the PhoneInput and InputOTP validation wiring in
ForgotPasswordPhone: preserve PhoneInput’s existing constraint aria-describedby
while composing it with the phone error element ID, and add aria-invalid plus
composed aria-describedby for the OTP input. Assign the corresponding
error-message IDs and mark both validation messages as alerts.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e6688cbc-422d-4535-88c4-31001588fbe3

📥 Commits

Reviewing files that changed from the base of the PR and between 742d5a2 and 103dde7.

📒 Files selected for processing (6)
  • src/components/Auth/ForgotPasswordPanel.tsx
  • src/components/Auth/ForgotPasswordPhone.tsx
  • src/components/Auth/NewPasswordFields.tsx
  • src/components/Auth/ResetPassword.tsx
  • tests/auth/passwordResetOtp.spec.ts
  • tests/helper/user.ts

Comment thread src/components/Auth/PasswordFields.tsx
Comment thread tests/helper/user.ts
@yash-learner
yash-learner marked this pull request as ready for review July 27, 2026 12:57
@yash-learner
yash-learner requested review from a team July 27, 2026 12:57
@yash-learner
yash-learner requested a review from a team as a code owner July 27, 2026 12:57

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/Auth/ForgotPasswordPhone.tsx (1)

269-281: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Associate all field errors with their controls.

The phone, OTP, and username errors are rendered as standalone text, but their inputs lack aria-invalid and aria-describedby references. Add stable error IDs and associate each message with its corresponding control.

Proposed accessibility fix
+const phoneErrorId = "reset-phone-error";
...
 <PhoneInput
   id="reset_phone"
+  aria-invalid={Boolean(phoneError)}
+  aria-describedby={phoneError ? phoneErrorId : undefined}
 />
...
-<p className="text-sm text-red-500">{phoneError}</p>
+<p id={phoneErrorId} role="alert" className="text-sm text-red-500">
+  {phoneError}
+</p>

Apply the same pattern to InputOTP/errors.otp and the username input/error block.

As per coding guidelines, React components must meet WCAG 2.1 AA requirements and implement proper ARIA attributes.

Also applies to: 323-351, 363-390

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Auth/ForgotPasswordPhone.tsx` around lines 269 - 281,
Associate the phone, OTP, and username error messages with their respective
controls in ForgotPasswordPhone by adding stable unique error IDs, setting
aria-invalid when each field has an error, and referencing the matching ID
through aria-describedby. Apply the pattern to the PhoneInput/phoneError,
InputOTP/errors.otp, and username input/error blocks while preserving existing
validation behavior.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/Auth/ResetPassword.tsx`:
- Around line 54-58: Normalize or validate the error response in the
reset-password mutation’s onError handler before passing it to setErrors. Ensure
the resulting value matches the PasswordErrors shape expected by PasswordFields,
handling alternate API error envelopes safely and preserving a useful fallback
message when the response is not compatible.

---

Outside diff comments:
In `@src/components/Auth/ForgotPasswordPhone.tsx`:
- Around line 269-281: Associate the phone, OTP, and username error messages
with their respective controls in ForgotPasswordPhone by adding stable unique
error IDs, setting aria-invalid when each field has an error, and referencing
the matching ID through aria-describedby. Apply the pattern to the
PhoneInput/phoneError, InputOTP/errors.otp, and username input/error blocks
while preserving existing validation behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c5a036fc-29aa-409f-bcd4-42a0d14da687

📥 Commits

Reviewing files that changed from the base of the PR and between 103dde7 and bcaa7c6.

📒 Files selected for processing (3)
  • src/components/Auth/ForgotPasswordPhone.tsx
  • src/components/Auth/PasswordFields.tsx
  • src/components/Auth/ResetPassword.tsx

Comment thread src/components/Auth/ResetPassword.tsx
Comment thread public/locale/en.json Outdated
Comment thread src/components/Auth/ForgotPasswordPhone.tsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/Auth/ForgotPasswordPhone.tsx (1)

121-135: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Pass silent: true to sendOtp.

This mutation already handles failures inline with setPhoneError(...), so leaving it non-silent can surface the same OTP-send error twice: once from the global handler and once here.

Proposed fix
-    mutationFn: mutate(otpApi.sendPasswordResetOtp),
+    mutationFn: mutate(otpApi.sendPasswordResetOtp, { silent: true }),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Auth/ForgotPasswordPhone.tsx` around lines 121 - 135, Update
the sendOtp mutation configuration in ForgotPasswordPhone to pass silent: true
when invoking mutate with otpApi.sendPasswordResetOtp, while preserving the
existing inline onError handling and setPhoneError behavior.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/components/Auth/ForgotPasswordPhone.tsx`:
- Around line 121-135: Update the sendOtp mutation configuration in
ForgotPasswordPhone to pass silent: true when invoking mutate with
otpApi.sendPasswordResetOtp, while preserving the existing inline onError
handling and setPhoneError behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 78c109a8-62f9-4da4-bad2-7e20666a8bf8

📥 Commits

Reviewing files that changed from the base of the PR and between bcaa7c6 and 105f527.

📒 Files selected for processing (3)
  • public/locale/en.json
  • src/components/Auth/ForgotPasswordPanel.tsx
  • src/components/Auth/ForgotPasswordPhone.tsx

@NikhilA8606 NikhilA8606 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Playwright test are failing

@yash-learner

Copy link
Copy Markdown
Member Author

Playwright test are failing

Will fix it

@Jacobjeevan Jacobjeevan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just improvements, can be taken up as followup.

const data = cause as Record<string, unknown>;
const errors = data.errors;

if (Array.isArray(errors)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should be able to leverage the existing handlePydantic logic w/ a light refactor no?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will soon migrate to the shadcn form and RHF, then we will not need this

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prob don't need this component/can combine with ForgotPassword

@gigincg gigincg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Type changes LGTM

@yash-learner
yash-learner merged commit e4a79be into develop Jul 31, 2026
25 of 28 checks passed
@yash-learner
yash-learner deleted the ENG-635-wire-password-reset-via-otp branch July 31, 2026 08:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants