Luồng chuẩn: user nhập email → server gửi link chứa token dùng một lần, hết hạn ngắn → user mở link, đặt mật khẩu mới → token bị huỷ.
Yêu cầu với token:
- Sinh bằng CSPRNG, tối thiểu 128 bit entropy (crypto.randomBytes(32)), không phải Math.random(), không phải hash email + timestamp (đoán được).
- Lưu dạng hash trong DB (SHA-256 là đủ vì token đã đủ entropy, không cần hash chậm). Rò rỉ DB không cho phép reset mật khẩu người khác.
- Hết hạn ngắn: 15-60 phút.
- Dùng một lần — xoá ngay khi đổi mật khẩu thành công, và mỗi lần yêu cầu mới phải vô hiệu token cũ.
- Nằm ở query string hoặc path của link, gửi qua HTTPS; trang reset không được nhúng tài nguyên bên thứ ba làm rò token qua header Referer.
Không rò rỉ email có tồn tại hay không: phản hồi luôn giống nhau — "Nếu email tồn tại, chúng tôi đã gửi hướng dẫn" — cùng mã trạng thái, cùng thời gian phản hồi. Nếu email không có trong hệ thống thì chỉ đơn giản không gửi mail, đừng báo lỗi.
Sau khi đổi mật khẩu thành công, bắt buộc:
- Huỷ toàn bộ phiên đang mở và refresh token của tài khoản (trừ phiên hiện tại nếu muốn) — nếu không, kẻ đã chiếm phiên vẫn ở lại.
- Gửi email thông báo "mật khẩu vừa được đổi" tới địa chỉ cũ.
- Không tự động đăng nhập user chỉ dựa vào token; và không bao giờ gửi mật khẩu mới qua email.
Cuối cùng, giới hạn tần suất endpoint gửi mail theo email và theo IP để không biến nó thành công cụ gửi thư rác.