Skip to content

feat: add OTP based password recovery system#100

Merged
Akash504-ai merged 15 commits into
CodePlaygroundHub:mainfrom
nihar-ux18:feature/otp-password-recovery
May 31, 2026
Merged

feat: add OTP based password recovery system#100
Akash504-ai merged 15 commits into
CodePlaygroundHub:mainfrom
nihar-ux18:feature/otp-password-recovery

Conversation

@nihar-ux18

@nihar-ux18 nihar-ux18 commented May 27, 2026

Copy link
Copy Markdown
Contributor

Description

This PR adds an OTP-based password recovery system as a secure alternative to security-question-based password reset.

Features Added

  • Email OTP based password recovery
  • OTP verification flow
  • Password reset using secure reset token
  • OTP expiry handling
  • OTP resend protection / rate limiting
  • Secure OTP hashing using bcrypt
  • Frontend integration for forgot password flow
  • Automated tests for OTP flow

Security Improvements

  • OTPs are hashed before storage
  • OTP expiry validation added
  • OTPs are cleared after verification/reset
  • Generic responses used to prevent email enumeration
  • Added resend cooldown/rate limiting

Backend Changes

Modified:

  • auth.controller.js
  • auth.route.js
  • user.model.js
  • sendEmail.js
  • rateLimiter.js

Added:

  • otp.test.js

Frontend Changes

Modified:

  • ForgotPassword.jsx
  • useAuthStore.js

Testing Done

Tested:

  • Send OTP
  • Invalid OTP
  • Expired OTP
  • OTP verification
  • Password reset
  • Login with new password
  • OTP resend flow

Notes

The existing security-question-based flow was kept intact for backward compatibility.

Summary by CodeRabbit

  • New Features
    • Introduced one-time password (OTP) recovery for forgotten passwords; users receive a verification code via email to securely reset their password
    • Added password visibility toggles on the password reset form for improved usability

@vercel

vercel Bot commented May 27, 2026

Copy link
Copy Markdown

@nihar-ux18 is attempting to deploy a commit to the Akash Santra 's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented May 27, 2026

Copy link
Copy Markdown

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

Walkthrough

Adds end-to-end email OTP–based password recovery. Backend: user schema adds OTP fields, rate limiters updated, Brevo OTP email sender added, auth controllers implement send/verify OTP with bcrypt hashing and cooldown, routes expose endpoints. Frontend: store manages OTP state and actions, ForgotPassword page implements 3-step recovery (email → OTP entry → password reset) with optional fallback to security questions. Comprehensive integration tests validate the full flow.

Changes

OTP-based Password Recovery

Layer / File(s) Summary
Data model & rate limiting
backend/src/models/user.model.js, backend/src/middleware/rateLimiter.js
User schema extends with resetOtp (hidden from default select) and resetOtpExpiry fields; forgotPasswordLimiter and new otpRateLimiter skip enforcement in test environment.
Email helper (sendOtpEmail)
backend/src/lib/sendEmail.js
sendWelcomeEmail uses configurable process.env.BREVO_EMAIL; new sendOtpEmail(email, otp) logs dispatch, skips on missing/placeholder API key, and POSTs OTP email to Brevo's SMTP endpoint with error/success logging.
Auth controllers and reset flow
backend/src/controllers/auth.controller.js
Imports sendOtpEmail; refactors signup/login/logout handlers for clarity; implements sendOtp controller (email normalization, 60s cooldown enforcement, 6-digit OTP generation, bcrypt hashing, 5-minute expiry, async email send); implements verifyOtp controller (input validation, expiry/hash check, OTP field clearance, resetToken generation, passwordResetSession storage); resetPassword now clears both passwordResetSession and resetOtp/resetOtpExpiry; getSecurityQuestions validates email type.
Route wiring
backend/src/routes/auth.route.js
Imports sendOtp/verifyOtp controllers and otpRateLimiter; adds POST /send-otp and POST /verify-otp endpoints both rate-limited by otpRateLimiter; wires existing verify-security and reset-password with forgotPasswordLimiter.
Frontend auth store OTP actions
frontend/src/store/useAuthStore.js
Adds isSendingOtp and isVerifyingOtp loading flags; sendOtp(email) POSTs to /auth/send-otp, shows success toast with backend message, returns boolean; verifyOtp(data) POSTs to /auth/verify-otp, stores resetToken on success, shows success/error toasts, returns boolean.
ForgotPassword page refactor
frontend/src/pages/ForgotPassword.jsx
Step 1: email entry with "Send OTP" button and fallback "Use Security Questions Instead" option. Step 2 (OTP): 6 one-character inputs with numeric enforcement, paste handling, arrow/Backspace navigation, 60s cooldown timer, "Resend OTP" button (active after cooldown). Step 2 (security-questions): alternative branch preserving prior flow. Step 3: password reset with show/hide toggles for both password fields. All steps integrated with store actions; resend triggers same handleSendOtp; cooldown decrement via useEffect.
OTP integration tests
backend/test/auth/otp.test.js
Comprehensive test suite: POST /send-otp validates non-existent-email safety (no email send), existing-email OTP generation/hashing/expiry/persistence, 60s cooldown (resend blocks email on second immediate request); POST /verify-otp tests expiry/mismatch rejection (400) and success (200 with resetToken); POST /reset-password via resetToken succeeds and prevents reuse. Uses global sendOtpEmail Jest mock, test DB connection, Redis cleanup, and per-test user seeding.
Test mock updates
backend/test/auth/checkAuth.test.js, backend/test/auth/login.test.js, backend/test/auth/signup.test.js, backend/test/message/message.test.js, backend/test/socket/socket.test.js, backend/test/admin/admin.test.js, backend/test/group/group.test.js, backend/test/integration/integration.test.js, backend/test/middleware/auth.middleware.test.js, backend/test/security/security.test.js
Each test file's Jest unstable_mockModule setup for sendEmail.js extended to include sendOtpEmail: jest.fn() alongside existing sendWelcomeEmail mock for test isolation.
Environment configuration
backend/.env.example
Restructured with section headers (# Database, # Server, # Authentication, # Frontend URL, # Cloudinary, # Groq AI, # ZegoCloud, # Brevo, # ML Service, # Redis); no new variables added, reflow improves readability.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

🐰 Hops through the OTP gates with joy,
Six digits dance, no tricks to foil!
Cooldown timers tick with care,
Password reset, free from despair. 🔐
Tests and toggles all align—
Recovery flow is now divine!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add OTP based password recovery system' directly and clearly describes the main feature added in this changeset—an OTP-based password recovery system with frontend and backend integration.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (2)
frontend/src/pages/ForgotPassword.jsx (1)

175-180: ⚡ Quick win

Keep a visible focus style on the resend button.

focus:outline-none removes the only keyboard focus indicator here, and there is no replacement focus-visible styling.

Suggested fix
-                        className="text-xs font-bold text-primary hover:underline focus:outline-none"
+                        className="text-xs font-bold text-primary hover:underline focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary"
🤖 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 `@frontend/src/pages/ForgotPassword.jsx` around lines 175 - 180, The resend
button currently removes the keyboard focus indicator via the className entry
(it includes focus:outline-none) — update the button rendered where
handleSendOtp is used (the element using onClick={handleSendOtp} and
disabled={isSendingOtp}) to remove focus:outline-none and add a visible
focus-visible/focus style (for example a focus-visible:ring or focus:ring with
an accessible ring color and offset) so keyboard users retain a clear focus
indicator while preserving existing styling and hover behavior.
backend/test/auth/otp.test.js (1)

63-63: ⚡ Quick win

Consider more resilient async waiting pattern.

The wait(20) pattern gives time for fire-and-forget email operations to complete, but a fixed 20ms delay could theoretically cause flakiness in slower CI environments or under load.

Consider increasing the timeout (e.g., wait(100)) for better reliability, or implementing a polling pattern if stricter guarantees are needed.

🔄 Low-effort improvement: increase timeout margin
-await wait(20);
+await wait(100);

This provides more headroom for async operations across different test environments.

Also applies to: 75-75, 101-101, 140-140, 156-156, 184-184

🤖 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 `@backend/test/auth/otp.test.js` at line 63, The short fixed sleep wait(20) in
backend/test/auth/otp.test.js is flaky; replace the 20ms sleeps with a more
resilient strategy—either increase the margin (e.g., change wait(20) to
wait(100) for all occurrences of wait in this file) or implement a polling/retry
helper that checks the expected condition (e.g., email sent flag or mock call
count) until success or timeout; update every occurrence of wait(...) in this
file (including the instances currently using 20ms) to use the chosen approach
so tests are stable under CI load.
🤖 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 `@backend/src/controllers/auth.controller.js`:
- Around line 397-401: The cooldown branch currently returns a distinct 429 and
countdown using timeSinceLastSent/secondsRemaining and res.status(429) only when
the account exists, allowing email enumeration; change the endpoint (e.g., the
OTP request handler where timeSinceLastSent and secondsRemaining are computed)
so that requests for both existing and non-existing accounts return the same
generic response and status (do not return a different 429/countdown only for
valid accounts). Ensure the handler always returns a uniform message like "If an
account exists, an OTP has been sent" (and a consistent HTTP status) regardless
of account presence or cooldown, while enforcing cooldown server‑side without
revealing it in the response.

In `@backend/src/lib/sendEmail.js`:
- Line 42: The console.log in the OTP sending flow leaks sensitive data (OTP and
full recipient email); update the logging in the sendEmail/sendOtp function to
remove the raw otp and email from logs—log only non-sensitive info such as “OTP
sent” with a masked or hashed email or a request id, and use the existing app
logger (e.g., processLogger or logger) instead of printing secrets to stdout;
locate the console.log(`[OTP Verification] Sent OTP code: ${otp} to ${email}`)
and replace it with a privacy-safe log entry.

In `@backend/src/routes/auth.route.js`:
- Line 33: The /verify-otp endpoint is unthrottled; add a rate-limiter
middleware and attach it to the router.post("/verify-otp", ...) call to prevent
brute-force OTP attempts. Import or require an express rate-limiter (e.g.,
rateLimit from "express-rate-limit"), create a limiter instance (e.g.,
otpRateLimiter) configured for short window + low max attempts (suitable for
6-digit OTPs), then change the route to use the limiter before the verifyOtp
handler (router.post("/verify-otp", otpRateLimiter, verifyOtp)). Ensure the
limiter returns proper 429 responses and customize the message if needed.

In `@frontend/src/pages/ForgotPassword.jsx`:
- Around line 10-17: The component now always uses the OTP flow
(sendOtp/verifyOtp) and removed the legacy security-question branch; restore
conditional rendering so users who rely on security-question recovery still see
that UI and logic. Update ForgotPassword.jsx to detect which recovery modes are
available from useAuthStore (e.g., presence of sendOtp/verifyOtp vs.
security-question handlers like fetchSecurityQuestion/verifySecurityAnswer or a
flag such as supportsSecurityQuestion) and render either the OTP flow (using
sendOtp, verifyOtp, resetPassword, isSendingOtp, isVerifyingOtp,
isResettingPassword) or the security-question flow (reintroduce the prior JSX
and handlers) accordingly; ensure handlers call the correct store methods and
preserve the previous state/validation logic that existed in the 124-190 region
so both flows work and are reachable.

---

Nitpick comments:
In `@backend/test/auth/otp.test.js`:
- Line 63: The short fixed sleep wait(20) in backend/test/auth/otp.test.js is
flaky; replace the 20ms sleeps with a more resilient strategy—either increase
the margin (e.g., change wait(20) to wait(100) for all occurrences of wait in
this file) or implement a polling/retry helper that checks the expected
condition (e.g., email sent flag or mock call count) until success or timeout;
update every occurrence of wait(...) in this file (including the instances
currently using 20ms) to use the chosen approach so tests are stable under CI
load.

In `@frontend/src/pages/ForgotPassword.jsx`:
- Around line 175-180: The resend button currently removes the keyboard focus
indicator via the className entry (it includes focus:outline-none) — update the
button rendered where handleSendOtp is used (the element using
onClick={handleSendOtp} and disabled={isSendingOtp}) to remove
focus:outline-none and add a visible focus-visible/focus style (for example a
focus-visible:ring or focus:ring with an accessible ring color and offset) so
keyboard users retain a clear focus indicator while preserving existing styling
and hover 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b9643848-df07-4905-a64c-e92f95479e0c

📥 Commits

Reviewing files that changed from the base of the PR and between 37afc5f and c1d187b.

⛔ Files ignored due to path filters (3)
  • backend/package-lock.json is excluded by !**/package-lock.json
  • frontend/package-lock.json is excluded by !**/package-lock.json
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (13)
  • backend/src/controllers/auth.controller.js
  • backend/src/lib/sendEmail.js
  • backend/src/middleware/rateLimiter.js
  • backend/src/models/user.model.js
  • backend/src/routes/auth.route.js
  • backend/test/auth/checkAuth.test.js
  • backend/test/auth/login.test.js
  • backend/test/auth/otp.test.js
  • backend/test/auth/signup.test.js
  • backend/test/message/message.test.js
  • backend/test/socket/socket.test.js
  • frontend/src/pages/ForgotPassword.jsx
  • frontend/src/store/useAuthStore.js

Comment thread backend/src/controllers/auth.controller.js Outdated
Comment thread backend/src/lib/sendEmail.js Outdated
Comment thread backend/src/routes/auth.route.js Outdated
Comment thread frontend/src/pages/ForgotPassword.jsx
@nihar-ux18

Copy link
Copy Markdown
Contributor Author

Restored the legacy security question recovery flow alongside the OTP recovery flow to preserve the original PR behavior.

Also updated the forgot password flow to support switching between both recovery methods and addressed OTP email enumeration concerns.

@Akash504-ai Akash504-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks @nihar-ux18 for the contribution. Could you share a screen recording of the entire flow? It will help me verify the end-to-end functionality before completing the review.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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)
backend/src/routes/auth.route.js (1)

27-30: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fix duplicate /verify-security and /reset-password route registrations bypassing forgotPasswordLimiter.

/verify-security and /reset-password are each registered twice in backend/src/routes/auth.route.js: the first pair uses verifySecurityAnswers / resetPassword with no limiter, while the second pair adds forgotPasswordLimiter. Because verifySecurityAnswers and resetPassword are (req, res) handlers that don’t call next(), the first matching registration handles the request and the limiter-wrapped duplicates never run—leaving both endpoints unthrottled.

🔒️ Proposed fix
-router.post("/verify-security", verifySecurityAnswers);
-router.post("/reset-password", resetPassword);
 router.post("/verify-security", forgotPasswordLimiter, verifySecurityAnswers);
 router.post("/reset-password", forgotPasswordLimiter, resetPassword);
🤖 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 `@backend/src/routes/auth.route.js` around lines 27 - 30, Remove the duplicate
unprotected route registrations so the limiter is actually applied: delete the
two lines that call router.post("/verify-security", verifySecurityAnswers) and
router.post("/reset-password", resetPassword) and keep the limiter-wrapped
registrations router.post("/verify-security", forgotPasswordLimiter,
verifySecurityAnswers) and router.post("/reset-password", forgotPasswordLimiter,
resetPassword); verify there are no other duplicate registrations of
verifySecurityAnswers or resetPassword in the same module so the
forgotPasswordLimiter is always reached.
🧹 Nitpick comments (1)
backend/src/controllers/auth.controller.js (1)

135-152: 💤 Low value

Remove the duplicate input-type guard.

Lines 135-142 already reject non-string email/password before the try, so the identical check at Lines 145-152 is unreachable dead code (and the two messages diverge: "Invalid input" vs "Invalid input types").

♻️ Proposed cleanup
   if (
     typeof email !== "string" ||
     typeof password !== "string"
   ) {
     return res.status(400).json({
       message: "Invalid input",
     });
   }

   try {
-    if (
-      typeof email !== "string" ||
-      typeof password !== "string"
-    ) {
-      return res.status(400).json({
-        message: "Invalid input types",
-      });
-    }
-
     const user = await User.findOne({
       email: email.toLowerCase(),
     }).select("+password");
🤖 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 `@backend/src/controllers/auth.controller.js` around lines 135 - 152, Duplicate
input-type guard for email/password exists: remove the redundant conditional
inside the try block (the second typeof checks that return "Invalid input
types") so only the initial pre-try guard remains; ensure any remaining
error/return messages use the same wording ("Invalid input") or the desired
canonical message, and keep the try block for runtime/auth logic only (refer to
the email and password variables and the try block surrounding them to locate
the redundant check).
🤖 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 `@backend/src/controllers/auth.controller.js`:
- Around line 549-576: The getSecurityQuestions handler lacks validation and a
try/catch: validate that req.body.email exists and is a string before calling
email.toLowerCase, then wrap the async body (including the User.findOne call and
access to user.securityQuestions.length) in a try/catch; on invalid input return
a 400 with a clear message, if user not found or has no securityQuestions return
400/404 as appropriate, and in the catch log the error and return a 500 JSON
error response so rejections don’t leave the request hanging.

---

Outside diff comments:
In `@backend/src/routes/auth.route.js`:
- Around line 27-30: Remove the duplicate unprotected route registrations so the
limiter is actually applied: delete the two lines that call
router.post("/verify-security", verifySecurityAnswers) and
router.post("/reset-password", resetPassword) and keep the limiter-wrapped
registrations router.post("/verify-security", forgotPasswordLimiter,
verifySecurityAnswers) and router.post("/reset-password", forgotPasswordLimiter,
resetPassword); verify there are no other duplicate registrations of
verifySecurityAnswers or resetPassword in the same module so the
forgotPasswordLimiter is always reached.

---

Nitpick comments:
In `@backend/src/controllers/auth.controller.js`:
- Around line 135-152: Duplicate input-type guard for email/password exists:
remove the redundant conditional inside the try block (the second typeof checks
that return "Invalid input types") so only the initial pre-try guard remains;
ensure any remaining error/return messages use the same wording ("Invalid
input") or the desired canonical message, and keep the try block for
runtime/auth logic only (refer to the email and password variables and the try
block surrounding them to locate the redundant check).
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2d7fdc1d-0817-46ff-bdb0-31fee861161a

📥 Commits

Reviewing files that changed from the base of the PR and between c1d187b and 2201fd9.

⛔ Files ignored due to path filters (2)
  • .DS_Store is excluded by !**/.DS_Store
  • backend/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (9)
  • backend/src/controllers/auth.controller.js
  • backend/src/lib/sendEmail.js
  • backend/src/routes/auth.route.js
  • backend/test/auth/checkAuth.test.js
  • backend/test/auth/login.test.js
  • backend/test/auth/signup.test.js
  • backend/test/message/message.test.js
  • frontend/src/pages/ForgotPassword.jsx
  • test.txt
✅ Files skipped from review due to trivial changes (1)
  • test.txt
🚧 Files skipped from review as they are similar to previous changes (5)
  • backend/test/auth/checkAuth.test.js
  • backend/test/message/message.test.js
  • backend/test/auth/signup.test.js
  • backend/src/lib/sendEmail.js
  • frontend/src/pages/ForgotPassword.jsx

Comment thread backend/src/controllers/auth.controller.js
@nihar-ux18

Copy link
Copy Markdown
Contributor Author

Uploading Paso-otp.mp4…

Implemented OTP-based password recovery with secure OTP hashing, expiry validation, reset session handling, and email enumeration protection.

Also addressed multiple CodeRabbit review suggestions related to security and error handling. I’m currently working through the remaining review comments and will fix the pending CodeRabbit suggestions soon.

@nihar-ux18

Copy link
Copy Markdown
Contributor Author

Hi @Akash504-ai ! I've completed the requested changes and resolved the CodeRabbit review comments. The latest commits contain all the fixes discussed during review.

Could you please review the updated PR when convenient? Thank you!

@Akash504-ai

Copy link
Copy Markdown
Collaborator

Could you share a screen recording of the entire flow?

@nihar-ux18

Copy link
Copy Markdown
Contributor Author

Hi @Akash504-ai,

I've implemented the requested OTP password recovery flow and recorded a short demo showing:

OTP generation
OTP verification
Password reset
Successful login with the new password

Demo video:
https://github.com/user-attachments/assets/45375d1e-852b-4aed-b8fd-3562d4a5be45

Please let me know if any further changes are needed. Thanks!

@Akash504-ai

Copy link
Copy Markdown
Collaborator

Thanks @nihar-ux18 for sharing the demo. I'll review complete flow locally before proceeding with the final review. I'll let you know if any further changes are required.

@Akash504-ai Akash504-ai self-assigned this May 31, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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)
frontend/src/pages/ForgotPassword.jsx (1)

189-193: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Step 2 message doesn't reflect the recovery method.

When recoveryMethod === "security", line 191 still displays "Enter the 6-digit OTP sent to your email" instead of a message about answering security questions.

💡 Proposed fix
 <p className="text-base-content/50 text-xs mt-1">
   {step === 1 && "Verify your email to continue"}
-  {step === 2 && "Enter the 6-digit OTP sent to your email"}
+  {step === 2 && recoveryMethod === "otp" && "Enter the 6-digit OTP sent to your email"}
+  {step === 2 && recoveryMethod === "security" && "Answer your security questions"}
   {step === 3 && "Set your new account password"}
 </p>
🤖 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 `@frontend/src/pages/ForgotPassword.jsx` around lines 189 - 193, The step 2
help text always shows the OTP message; update the render logic in
ForgotPassword.jsx (the element that uses step and recoveryMethod) so when step
=== 2 and recoveryMethod === "security" it shows a security-question prompt
(e.g., "Answer your security questions to continue"), otherwise show the OTP
message; implement this by changing the second conditional (the expression
currently producing "Enter the 6-digit OTP sent to your email") to branch on
recoveryMethod (or extract a small helper like getStepMessage(step,
recoveryMethod)) so the correct message renders for security vs email recovery.
🤖 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 `@frontend/src/pages/ForgotPassword.jsx`:
- Around line 248-258: The click handler for fetchSecurityQuestions uses a stale
closure over securityQuestions, causing setAnswers(new
Array(securityQuestions.length).fill("")) to use an outdated length; fix by
removing the direct initialization from the onClick and instead add a useEffect
that watches recoveryMethod and securityQuestions and calls setAnswers(prev =>
prev.length === securityQuestions.length ? prev : new
Array(securityQuestions.length).fill("")) when recoveryMethod === "security" and
securityQuestions.length > 0; keep fetchSecurityQuestions,
setRecoveryMethod("security") and setStep(2) in the onClick but let the
useEffect synchronize answers after securityQuestions updates.

---

Outside diff comments:
In `@frontend/src/pages/ForgotPassword.jsx`:
- Around line 189-193: The step 2 help text always shows the OTP message; update
the render logic in ForgotPassword.jsx (the element that uses step and
recoveryMethod) so when step === 2 and recoveryMethod === "security" it shows a
security-question prompt (e.g., "Answer your security questions to continue"),
otherwise show the OTP message; implement this by changing the second
conditional (the expression currently producing "Enter the 6-digit OTP sent to
your email") to branch on recoveryMethod (or extract a small helper like
getStepMessage(step, recoveryMethod)) so the correct message renders for
security vs email recovery.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 06b98329-6637-4a89-a724-e53bd3e66687

📥 Commits

Reviewing files that changed from the base of the PR and between 82e8a87 and 694d26f.

📒 Files selected for processing (5)
  • backend/.env.example
  • backend/src/lib/sendEmail.js
  • backend/test/auth/otp.test.js
  • frontend/src/pages/ForgotPassword.jsx
  • frontend/src/store/useAuthStore.js
💤 Files with no reviewable changes (1)
  • frontend/src/store/useAuthStore.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • backend/src/lib/sendEmail.js
  • backend/test/auth/otp.test.js

Comment thread frontend/src/pages/ForgotPassword.jsx

@Akash504-ai Akash504-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for the contribution! I reviewed the implementation locally and pushed a few follow-up fixes/improvements to the PR branch after testing the flow. Appreciate your work on this feature.

@Akash504-ai Akash504-ai merged commit a74ff2a into CodePlaygroundHub:main May 31, 2026
3 of 5 checks passed
@nihar-ux18

Copy link
Copy Markdown
Contributor Author

Thank you @Akash504-ai for reviewing and merging the PR! I appreciate the feedback and the follow-up improvements. It was a great learning experience contributing to the project.

@Akash504-ai

Copy link
Copy Markdown
Collaborator

Thanks again for the contribution, @nihar-ux18! Appreciate the effort and collaboration throughout the review process. Looking forward to your future contributions 👍.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants