จัดการรหัสผ่าน (ลืม / รีเซ็ต / เปลี่ยน)
ภาพรวม
feature นี้ครอบคลุมทุกเส้นทางที่เกี่ยวข้องกับรหัสผ่านของผู้ใช้ CMS ได้แก่ ลืมรหัสผ่านและส่งอีเมลลิงก์รีเซ็ต, ตรวจสอบว่าโค้ดรีเซ็ตยังใช้ได้อยู่หรือไม่, ตั้งรหัสผ่านใหม่ และเปลี่ยนรหัสผ่านขณะที่ยังล็อกอินอยู่ พร้อมกลไก password history ที่ป้องกันการนำรหัสผ่านเดิมกลับมาใช้ซ้ำ
route ทั้งหมดอยู่ใต้ /api/user/... แต่ โค้ดอยู่ในโมดูล auth ไม่ใช่โมดูล user
เนื่องจากตอน port มาจาก NestJS เส้นทางเกี่ยวกับรหัสผ่านถูกยกมาพร้อมกับ auth ทั้งชุด
ส่วน internal/modules/passwordhistory เป็น port แบบหนึ่งต่อหนึ่งที่แยกออกมาเป็น standalone
เพื่อให้โมดูลอื่น เช่น user และ registration เรียกใช้ได้
Business Flow
ลืมรหัสผ่าน
- เรียก
POST /api/user/forgot-passwordพร้อม body ที่มีฟิลด์emailโดยจำกัด 3 ครั้งต่อ 10 วินาที - ระบบสร้างโค้ดสุ่มความยาว 16 ตัวอักษรแบบ hex จาก
crypto/randขนาด 8 bytes แล้วบันทึกแถวลงตารางforgot_passwordพร้อมกำหนดexpired_dateเป็นเวลาปัจจุบันบวกหนึ่งวัน โดยตัดเศษวินาทีทิ้งเพื่อให้ตรงกับพฤติกรรมของโค้ด JavaScript เดิม - ส่งอีเมลผ่าน Mailer โดยจัดรูปแบบเวลาหมดอายุเป็นโซนเวลา Asia/Bangkok
ตัวอย่างข้อความคือ
Please reset your password by July 5, 2026 on 5:33 PM (UTC+7). - หากค้นหาผู้ใช้จากอีเมลไม่พบ ระบบตอบ error
AUT_101(Not found user for reset password)
ตรวจสอบโค้ดและตั้งรหัสผ่านใหม่
GET /api/user/reset-password/:code— cms-web เรียกตอนเปิดหน้าจากลิงก์ในอีเมล เพื่อตรวจว่าโค้ดยังไม่หมดอายุและยังไม่ถูกใช้งานPOST /api/user/reset-passwordพร้อมโค้ดและรหัสผ่านใหม่ ระบบจะ hash ด้วย bcrypt แล้วอัปเดตฟิลด์user.passwordบันทึกลงpassword_historyและทำเครื่องหมายว่าโค้ดนั้นถูกใช้แล้ว
เปลี่ยนรหัสผ่านขณะล็อกอินอยู่
POST /api/user/change-passwordต้องผ่านJwtAuthและจำกัด 5 ครั้งต่อ 60 วินาที โดยระบบจะตรวจรหัสผ่านเดิม ตรวจว่ารหัสใหม่ไม่ซ้ำกับประวัติ แล้วจึงอัปเดตและบันทึกประวัติเพิ่ม
ไฟล์และฟังก์ชันหลัก
โค้ดหลักอยู่ที่ internal/modules/auth/password.go, internal/modules/auth/hash.go
และ internal/modules/auth/controller.go สำหรับการ register route
ส่วน internal/modules/passwordhistory/service.go เป็น port แบบ standalone
และ internal/modules/user/service.go ให้ createForgotPassword กับ getExpiredDateText
| Method | Route | Handler | Throttle |
|---|---|---|---|
| POST | /api/user/forgot-password | m.forgotPassword | 3 ครั้ง / 10 วินาที |
| POST | /api/user/reset-password | m.resetPassword | 3 ครั้ง / 10 วินาที |
| GET | /api/user/reset-password/:code | m.resetPasswordCheckCode | 3 ครั้ง / 10 วินาที |
| POST | /api/user/change-password | m.changePassword | 5 ครั้ง / 60 วินาที และต้องผ่าน JwtAuth |
หมายเหตุ: internal/modules/passwordhistory/controller.go มี RegisterRoutes ที่ว่างเปล่าโดยเจตนา
เนื่องจาก controller ใน NestJS เดิมก็ว่างเช่นกัน โมดูลนี้จึงเป็น provider-only
จุดเชื่อมต่อกับ Service อื่น
- Permission — ไม่ต้องมีสิทธิ์ใด ๆ เพราะเป็น public ยกเว้น
change-passwordที่ต้องผ่านJwtAuth - ตาราง —
user,forgot_passwordและpassword_history - Mailer —
internal/mail/mail.goตั้งค่าจาก environment กลุ่มMAIL_SMTP_* - Hashing — bcrypt จาก
golang.org/x/crypto/bcrypt - Throttle —
internal/core/middleware/throttle.goผ่านmiddleware.Throttle(limit, ttlMs) - เอกสารที่เกี่ยวข้อง — การเข้าสู่ระบบและ JWT, User Management และ สมัครใช้งาน