Overview
Users who forget their password have no way to recover their account. There is no forgot-password or reset-password endpoint. The MailService is set up and UsersService.update() can write a new passwordHash, so only the controller logic and reset token mechanism are missing.
Background
Files relevant:
backend/src/auth/auth.controller.ts — add two endpoints:
POST /api/auth/forgot-password — accepts { email }, sends reset link
POST /api/auth/reset-password — accepts { token, newPassword }, updates hash
backend/src/auth/auth.service.ts — add forgotPassword() and resetPassword() methods
backend/src/mail/mail.service.ts — add sendPasswordReset(to, token) method
Token approach: signed JWT with 1-hour expiry containing the user's ID and a purpose: 'password-reset' claim. Store no server-side state — just verify the JWT signature and expiry on the reset endpoint.
Acceptance Criteria
Overview
Users who forget their password have no way to recover their account. There is no forgot-password or reset-password endpoint. The
MailServiceis set up andUsersService.update()can write a newpasswordHash, so only the controller logic and reset token mechanism are missing.Background
Files relevant:
backend/src/auth/auth.controller.ts— add two endpoints:POST /api/auth/forgot-password— accepts{ email }, sends reset linkPOST /api/auth/reset-password— accepts{ token, newPassword }, updates hashbackend/src/auth/auth.service.ts— addforgotPassword()andresetPassword()methodsbackend/src/mail/mail.service.ts— addsendPasswordReset(to, token)methodToken approach: signed JWT with 1-hour expiry containing the user's ID and a
purpose: 'password-reset'claim. Store no server-side state — just verify the JWT signature and expiry on the reset endpoint.Acceptance Criteria
POST /api/auth/forgot-passwordalways returns200(do not reveal whether email exists)POST /api/auth/reset-passwordvalidates the token, hashes the new password, and updates the user400 Bad RequestsaltRounds: 12as registration)