Skip to main content

Password Management (Forgot / Reset / Change)

Overview

This feature covers every path a CMS user's password can take: forgetting it and receiving a reset link by email, checking whether a reset code is still valid, setting a new password, and changing the password while logged in — backed by a password history mechanism that blocks reuse of previous passwords.

All the routes sit under /api/user/..., but the code lives in the auth module, not the user module. When the service was ported from NestJS, the password routes came across together with the rest of auth. Separately, internal/modules/passwordhistory is a standalone one-for-one port so that other modules such as user and registration can call it.

Business Flow

Forgot password

  1. Call POST /api/user/forgot-password with an email field in the body, rate-limited to 3 calls per 10 seconds.
  2. The system generates a random 16-character hex code from 8 bytes of crypto/rand and writes a row into forgot_password with expired_date set to now plus one day, truncating sub-second precision to match the original JavaScript behaviour.
  3. It sends an email through the Mailer, formatting the expiry in the Asia/Bangkok timezone — for example, Please reset your password by July 5, 2026 on 5:33 PM (UTC+7).
  4. If no user matches the email address, the system returns error AUT_101 (Not found user for reset password).

Validating the code and setting a new password

  1. GET /api/user/reset-password/:code — cms-web calls this when the emailed link opens, to confirm the code has neither expired nor already been used.
  2. POST /api/user/reset-password with the code and the new password hashes it with bcrypt, updates user.password, records the change in password_history, and marks the code as consumed.

Changing the password while logged in

  1. POST /api/user/change-password runs behind JwtAuth and is rate-limited to 5 calls per 60 seconds. It verifies the current password, checks the new one against the history, then updates the password and appends a new history entry.

Key Files & Functions

The main code lives in internal/modules/auth/password.go, internal/modules/auth/hash.go, and internal/modules/auth/controller.go for route registration. internal/modules/passwordhistory/service.go is the standalone port, and internal/modules/user/service.go supplies createForgotPassword and getExpiredDateText.

MethodRouteHandlerThrottle
POST/api/user/forgot-passwordm.forgotPassword3 / 10s
POST/api/user/reset-passwordm.resetPassword3 / 10s
GET/api/user/reset-password/:codem.resetPasswordCheckCode3 / 10s
POST/api/user/change-passwordm.changePassword5 / 60s, plus JwtAuth

Note: internal/modules/passwordhistory/controller.go has an intentionally empty RegisterRoutes, because the original NestJS controller was empty too — the module is provider-only.

Connections to Other Services

  • Permission — none required; these are public routes, except change-password, which needs JwtAuth.
  • Tablesuser, forgot_password, and password_history.
  • Mailerinternal/mail/mail.go, configured from the MAIL_SMTP_* environment variables.
  • Hashing — bcrypt from golang.org/x/crypto/bcrypt.
  • Throttlinginternal/core/middleware/throttle.go via middleware.Throttle(limit, ttlMs).
  • Related pages — Login & JWT, User Management, and Sign-up.