1. Giới thiệu
Trong bài viết này, chúng ta sẽ thảo luận về cách triển khai chức năng đặt lại mật khẩu an toàn trong ứng dụng Node.js Express. Đặt lại mật khẩu là một tính năng quan trọng để đảm bảo tính bảo mật và trải nghiệm người dùng tốt. Để đạt được điều này, chúng tôi sẽ thực hiện từng bước quy trình, từ thiết lập môi trường đến gửi email đặt lại và cuối cùng là cập nhật mật khẩu của người dùng.
2. Thiết lập dự án
2.1 Khởi tạo dự án
Đầu tiên, tạo một thư mục mới cho dự án của bạn và điều hướng vào đó bằng dòng lệnh. Sau đó, chạy lệnh sau để khởi tạo dự án bằng tệp package.json
:
1 2 | <span class="token function">npm</span> init -y |
2.2 Cài đặt phụ thuộc
Tiếp theo, cài đặt các phụ thuộc cần thiết cho dự án này bằng cách chạy lệnh sau:
1 2 | <span class="token function">npm</span> <span class="token function">install</span> express mongoose bcryptjs jsonwebtoken nodemailer dotenv |
Những phụ thuộc này bao gồm:
express
: Khung Express cốt lõimongoose
: Thư viện mô hình hóa đối tượng MongoDB cho Node.jsbcryptjs
: Một thư viện để băm và so sánh mật khẩujsonwebtoken
: Một thư viện để tạo và xác minh JSON Web Tokensnodemailer
: Một mô-đun để gửi emaildotenv
: Một mô-đun để tải các biến môi trường từ.env file
3. Thiết lập môi trường
3.1 Tạo tệp .env
Tạo tệp .env
trong thư mục gốc của dự án để lưu trữ dữ liệu nhạy cảm và cấu hình dành riêng cho môi trường. Thêm các dòng sau vào tệp:
1 2 3 4 5 6 | <span class="token constant">MONGODB_URI</span> <span class="token operator">=</span> mongodb <span class="token operator">:</span> <span class="token operator">/</span> <span class="token operator">/</span> localhost <span class="token operator">:</span> <span class="token number">27017</span> <span class="token operator">/</span> password <span class="token operator">-</span> reset <span class="token constant">EMAIL_SERVICE</span> <span class="token operator">=</span> your_email_service <span class="token constant">EMAIL_USER</span> <span class="token operator">=</span> your_email_address <span class="token constant">EMAIL_PASS</span> <span class="token operator">=</span> your_email_password <span class="token constant">JWT_SECRET</span> <span class="token operator">=</span> your_jwt_secret |
Thay thế các trình giữ chỗ bằng các giá trị thích hợp cho thông tin xác thực tài khoản và dịch vụ email của bạn.
3.2 Tải biến môi trường
Trong tệp ứng dụng chính (ví dụ: app.js
), hãy nhập và định cấu hình mô-đun dotenv
để tải các biến môi trường từ tệp .env
:
1 2 | <span class="token function">require</span> <span class="token punctuation">(</span> <span class="token string">'dotenv'</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">config</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
4. Thiết lập cơ sở dữ liệu
4.1 Kết nối với MongoDB
Sử dụng Mongoose để thiết lập kết nối tới cơ sở dữ liệu MongoDB. Cập nhật tệp app.js
của bạn bằng mã sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 | <span class="token keyword">const</span> express <span class="token operator">=</span> <span class="token function">require</span> <span class="token punctuation">(</span> <span class="token string">'express'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> mongoose <span class="token operator">=</span> <span class="token function">require</span> <span class="token punctuation">(</span> <span class="token string">'mongoose'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> app <span class="token operator">=</span> <span class="token function">express</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> app <span class="token punctuation">.</span> <span class="token function">use</span> <span class="token punctuation">(</span> express <span class="token punctuation">.</span> <span class="token function">json</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> mongoose <span class="token punctuation">.</span> <span class="token function">connect</span> <span class="token punctuation">(</span> process <span class="token punctuation">.</span> env <span class="token punctuation">.</span> <span class="token constant">MONGODB_URI</span> <span class="token punctuation">,</span> <span class="token punctuation">{</span> useNewUrlParser <span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">,</span> useUnifiedTopology <span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">then</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">=></span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> <span class="token string">'Connected to MongoDB'</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">catch</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span> <span class="token parameter">err</span> <span class="token punctuation">)</span> <span class="token operator">=></span> console <span class="token punctuation">.</span> <span class="token function">error</span> <span class="token punctuation">(</span> <span class="token string">'Failed to connect to MongoDB:'</span> <span class="token punctuation">,</span> err <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token constant">PORT</span> <span class="token operator">=</span> process <span class="token punctuation">.</span> env <span class="token punctuation">.</span> <span class="token constant">PORT</span> <span class="token operator">||</span> <span class="token number">3000</span> <span class="token punctuation">;</span> app <span class="token punctuation">.</span> <span class="token function">listen</span> <span class="token punctuation">(</span> <span class="token constant">PORT</span> <span class="token punctuation">,</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">=></span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> <span class="token template-string"><span class="token template-punctuation string">`</span> <span class="token string">Server is running on port </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span> <span class="token constant">PORT</span> <span class="token interpolation-punctuation punctuation">}</span></span> <span class="token template-punctuation string">`</span></span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
4.2 Xác định lược đồ người dùng và mô hình
Tạo một thư mục mới có tên models
và bên trong nó, tạo một tệp có tên User.js
. Xác định lược đồ và mô hình Người dùng bằng mã sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <span class="token keyword">const</span> mongoose <span class="token operator">=</span> <span class="token function">require</span> <span class="token punctuation">(</span> <span class="token string">'mongoose'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> bcrypt <span class="token operator">=</span> <span class="token function">require</span> <span class="token punctuation">(</span> <span class="token string">'bcryptjs'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> userSchema <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">mongoose <span class="token punctuation">.</span> Schema</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> email <span class="token operator">:</span> <span class="token punctuation">{</span> type <span class="token operator">:</span> String <span class="token punctuation">,</span> required <span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">,</span> unique <span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> password <span class="token operator">:</span> <span class="token punctuation">{</span> type <span class="token operator">:</span> String <span class="token punctuation">,</span> required <span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> passwordResetToken <span class="token operator">:</span> <span class="token punctuation">{</span> type <span class="token operator">:</span> String <span class="token punctuation">}</span> <span class="token punctuation">,</span> passwordResetExpires <span class="token operator">:</span> <span class="token punctuation">{</span> type <span class="token operator">:</span> Date <span class="token punctuation">}</span> <span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// Hash the password before saving it to the database</span> userSchema <span class="token punctuation">.</span> <span class="token function">pre</span> <span class="token punctuation">(</span> <span class="token string">'save'</span> <span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token punctuation">(</span> <span class="token parameter">next</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token operator">!</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> <span class="token function">isModified</span> <span class="token punctuation">(</span> <span class="token string">'password'</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token function">next</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> salt <span class="token operator">=</span> <span class="token keyword">await</span> bcrypt <span class="token punctuation">.</span> <span class="token function">genSalt</span> <span class="token punctuation">(</span> <span class="token number">10</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> password <span class="token operator">=</span> <span class="token keyword">await</span> bcrypt <span class="token punctuation">.</span> <span class="token function">hash</span> <span class="token punctuation">(</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> password <span class="token punctuation">,</span> salt <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token function">next</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// Instance method to validate the user's password</span> userSchema <span class="token punctuation">.</span> methods <span class="token punctuation">.</span> <span class="token function-variable function">validatePassword</span> <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token punctuation">(</span> <span class="token parameter">password</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">await</span> bcrypt <span class="token punctuation">.</span> <span class="token function">compare</span> <span class="token punctuation">(</span> password <span class="token punctuation">,</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> password <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> User <span class="token operator">=</span> mongoose <span class="token punctuation">.</span> <span class="token function">model</span> <span class="token punctuation">(</span> <span class="token string">'User'</span> <span class="token punctuation">,</span> userSchema <span class="token punctuation">)</span> <span class="token punctuation">;</span> module <span class="token punctuation">.</span> exports <span class="token operator">=</span> User <span class="token punctuation">;</span> |
5. Triển khai chức năng Đặt lại mật khẩu
5.1 Thiết lập các tuyến đường
Tạo một thư mục mới có tên là routes
và bên trong thư mục đó, tạo một tệp có tên auth.js
. Thiết lập các tuyến đường sau:
/auth/forgot-password
: Để bắt đầu quá trình đặt lại mật khẩu/auth/reset-password
: Để xử lý việc đặt lại mật khẩu thực tế
1 2 3 4 5 6 7 8 9 | <span class="token keyword">const</span> express <span class="token operator">=</span> <span class="token function">require</span> <span class="token punctuation">(</span> <span class="token string">'express'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> router <span class="token operator">=</span> express <span class="token punctuation">.</span> <span class="token function">Router</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token punctuation">{</span> forgotPassword <span class="token punctuation">,</span> resetPassword <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">require</span> <span class="token punctuation">(</span> <span class="token string">'../controllers/authController'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> router <span class="token punctuation">.</span> <span class="token function">post</span> <span class="token punctuation">(</span> <span class="token string">'/forgot-password'</span> <span class="token punctuation">,</span> forgotPassword <span class="token punctuation">)</span> <span class="token punctuation">;</span> router <span class="token punctuation">.</span> <span class="token function">post</span> <span class="token punctuation">(</span> <span class="token string">'/reset-password'</span> <span class="token punctuation">,</span> resetPassword <span class="token punctuation">)</span> <span class="token punctuation">;</span> module <span class="token punctuation">.</span> exports <span class="token operator">=</span> router <span class="token punctuation">;</span> |
Bây giờ, hãy nhập và sử dụng bộ định tuyến auth.js
trong tệp app.js
:
1 2 3 4 | <span class="token keyword">const</span> authRoutes <span class="token operator">=</span> <span class="token function">require</span> <span class="token punctuation">(</span> <span class="token string">'./routes/auth'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> app <span class="token punctuation">.</span> <span class="token function">use</span> <span class="token punctuation">(</span> <span class="token string">'/auth'</span> <span class="token punctuation">,</span> authRoutes <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
5.2 Tạo bộ điều khiển xác thực
Tạo một thư mục mới có tên là controllers
và bên trong nó, tạo một tệp có tên authController.js
. Tệp này sẽ chứa các chức năng của bộ điều khiển để xử lý quá trình đặt lại mật khẩu.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <span class="token keyword">const</span> User <span class="token operator">=</span> <span class="token function">require</span> <span class="token punctuation">(</span> <span class="token string">'../models/User'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> jwt <span class="token operator">=</span> <span class="token function">require</span> <span class="token punctuation">(</span> <span class="token string">'jsonwebtoken'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> bcrypt <span class="token operator">=</span> <span class="token function">require</span> <span class="token punctuation">(</span> <span class="token string">'bcryptjs'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> nodemailer <span class="token operator">=</span> <span class="token function">require</span> <span class="token punctuation">(</span> <span class="token string">'nodemailer'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> transporter <span class="token operator">=</span> nodemailer <span class="token punctuation">.</span> <span class="token function">createTransport</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> service <span class="token operator">:</span> process <span class="token punctuation">.</span> env <span class="token punctuation">.</span> <span class="token constant">EMAIL_SERVICE</span> <span class="token punctuation">,</span> auth <span class="token operator">:</span> <span class="token punctuation">{</span> user <span class="token operator">:</span> process <span class="token punctuation">.</span> env <span class="token punctuation">.</span> <span class="token constant">EMAIL_USER</span> <span class="token punctuation">,</span> pass <span class="token operator">:</span> process <span class="token punctuation">.</span> env <span class="token punctuation">.</span> <span class="token constant">EMAIL_PASS</span> <span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">forgotPassword</span> <span class="token punctuation">(</span> <span class="token parameter">req <span class="token punctuation">,</span> res</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// TODO: Implement the forgotPassword function</span> <span class="token punctuation">}</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">resetPassword</span> <span class="token punctuation">(</span> <span class="token parameter">req <span class="token punctuation">,</span> res</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// TODO: Implement the resetPassword function</span> <span class="token punctuation">}</span> module <span class="token punctuation">.</span> exports <span class="token operator">=</span> <span class="token punctuation">{</span> forgotPassword <span class="token punctuation">,</span> resetPassword <span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token punctuation">;</span> |
5.3 Thực hiện chức năng quên mật khẩu
Trong tệp authController.js
, hãy triển khai chức năng forgetPassword để xử lý yêu cầu đặt lại mật khẩu.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">forgotPassword</span> <span class="token punctuation">(</span> <span class="token parameter">req <span class="token punctuation">,</span> res</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Find the user by email</span> <span class="token keyword">const</span> user <span class="token operator">=</span> <span class="token keyword">await</span> User <span class="token punctuation">.</span> <span class="token function">findOne</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> email <span class="token operator">:</span> req <span class="token punctuation">.</span> body <span class="token punctuation">.</span> email <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token operator">!</span> user <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> res <span class="token punctuation">.</span> <span class="token function">status</span> <span class="token punctuation">(</span> <span class="token number">404</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">json</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> error <span class="token operator">:</span> <span class="token string">'User not found'</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// Generate a password reset token and set its expiration date</span> <span class="token keyword">const</span> token <span class="token operator">=</span> jwt <span class="token punctuation">.</span> <span class="token function">sign</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> id <span class="token operator">:</span> user <span class="token punctuation">.</span> _id <span class="token punctuation">}</span> <span class="token punctuation">,</span> process <span class="token punctuation">.</span> env <span class="token punctuation">.</span> <span class="token constant">JWT_SECRET</span> <span class="token punctuation">,</span> <span class="token punctuation">{</span> expiresIn <span class="token operator">:</span> <span class="token string">'1h'</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> user <span class="token punctuation">.</span> passwordResetToken <span class="token operator">=</span> token <span class="token punctuation">;</span> user <span class="token punctuation">.</span> passwordResetExpires <span class="token operator">=</span> Date <span class="token punctuation">.</span> <span class="token function">now</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">3600000</span> <span class="token punctuation">;</span> <span class="token comment">// 1 hour</span> <span class="token keyword">await</span> user <span class="token punctuation">.</span> <span class="token function">save</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// Send the password reset email</span> <span class="token keyword">const</span> resetUrl <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span> <span class="token string">http://</span> <span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span> req <span class="token punctuation">.</span> headers <span class="token punctuation">.</span> host <span class="token interpolation-punctuation punctuation">}</span></span> <span class="token string">/auth/reset-password?token=</span> <span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span> token <span class="token interpolation-punctuation punctuation">}</span></span> <span class="token template-punctuation string">`</span></span> <span class="token punctuation">;</span> <span class="token keyword">const</span> mailOptions <span class="token operator">=</span> <span class="token punctuation">{</span> from <span class="token operator">:</span> process <span class="token punctuation">.</span> env <span class="token punctuation">.</span> <span class="token constant">EMAIL_USER</span> <span class="token punctuation">,</span> to <span class="token operator">:</span> user <span class="token punctuation">.</span> email <span class="token punctuation">,</span> subject <span class="token operator">:</span> <span class="token string">'Password Reset Request'</span> <span class="token punctuation">,</span> html <span class="token operator">:</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string"> <p>You requested a password reset. Click the link below to reset your password:</p> <a href="</span> <span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span> resetUrl <span class="token interpolation-punctuation punctuation">}</span></span> <span class="token string">"></span> <span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span> resetUrl <span class="token interpolation-punctuation punctuation">}</span></span> <span class="token string"></a> </span><span class="token template-punctuation string">`</span></span> <span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token punctuation">;</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token keyword">await</span> transporter <span class="token punctuation">.</span> <span class="token function">sendMail</span> <span class="token punctuation">(</span> mailOptions <span class="token punctuation">)</span> <span class="token punctuation">;</span> res <span class="token punctuation">.</span> <span class="token function">json</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> message <span class="token operator">:</span> <span class="token string">'Password reset email sent'</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span> err <span class="token punctuation">)</span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">error</span> <span class="token punctuation">(</span> <span class="token string">'Failed to send password reset email:'</span> <span class="token punctuation">,</span> err <span class="token punctuation">)</span> <span class="token punctuation">;</span> res <span class="token punctuation">.</span> <span class="token function">status</span> <span class="token punctuation">(</span> <span class="token number">500</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">json</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> error <span class="token operator">:</span> <span class="token string">'Failed to send password reset email'</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
5.4 Thực hiện chức năng resetPassword
Trong tệp authController.js
, hãy triển khai chức năng resetPassword
để xử lý xác nhận đặt lại mật khẩu và cập nhật mật khẩu của người dùng.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">resetPassword</span> <span class="token punctuation">(</span> <span class="token parameter">req <span class="token punctuation">,</span> res</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Validate the password reset token</span> <span class="token keyword">const</span> token <span class="token operator">=</span> req <span class="token punctuation">.</span> query <span class="token punctuation">.</span> token <span class="token punctuation">;</span> <span class="token keyword">const</span> decodedToken <span class="token operator">=</span> jwt <span class="token punctuation">.</span> <span class="token function">verify</span> <span class="token punctuation">(</span> token <span class="token punctuation">,</span> process <span class="token punctuation">.</span> env <span class="token punctuation">.</span> <span class="token constant">JWT_SECRET</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// Find the user by their ID and token, and check if the token is still valid</span> <span class="token keyword">const</span> user <span class="token operator">=</span> <span class="token keyword">await</span> User <span class="token punctuation">.</span> <span class="token function">findOne</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> _id <span class="token operator">:</span> decodedToken <span class="token punctuation">.</span> id <span class="token punctuation">,</span> passwordResetToken <span class="token operator">:</span> token <span class="token punctuation">,</span> passwordResetExpires <span class="token operator">:</span> <span class="token punctuation">{</span> $gt <span class="token operator">:</span> Date <span class="token punctuation">.</span> <span class="token function">now</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token operator">!</span> user <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> res <span class="token punctuation">.</span> <span class="token function">status</span> <span class="token punctuation">(</span> <span class="token number">401</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">json</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> error <span class="token operator">:</span> <span class="token string">'Invalid or expired password reset token'</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// Update the user's password and remove the reset token and its expiration date</span> user <span class="token punctuation">.</span> password <span class="token operator">=</span> req <span class="token punctuation">.</span> body <span class="token punctuation">.</span> password <span class="token punctuation">;</span> user <span class="token punctuation">.</span> passwordResetToken <span class="token operator">=</span> <span class="token keyword">undefined</span> <span class="token punctuation">;</span> user <span class="token punctuation">.</span> passwordResetExpires <span class="token operator">=</span> <span class="token keyword">undefined</span> <span class="token punctuation">;</span> <span class="token keyword">await</span> user <span class="token punctuation">.</span> <span class="token function">save</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// Send a confirmation email</span> <span class="token keyword">const</span> mailOptions <span class="token operator">=</span> <span class="token punctuation">{</span> from <span class="token operator">:</span> process <span class="token punctuation">.</span> env <span class="token punctuation">.</span> <span class="token constant">EMAIL_USER</span> <span class="token punctuation">,</span> to <span class="token operator">:</span> user <span class="token punctuation">.</span> email <span class="token punctuation">,</span> subject <span class="token operator">:</span> <span class="token string">'Password Reset Confirmation'</span> <span class="token punctuation">,</span> html <span class="token operator">:</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string"> <p>Your password has been successfully reset. If you did not initiate this request, please contact us immediately.</p> </span><span class="token template-punctuation string">`</span></span> <span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token punctuation">;</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token keyword">await</span> transporter <span class="token punctuation">.</span> <span class="token function">sendMail</span> <span class="token punctuation">(</span> mailOptions <span class="token punctuation">)</span> <span class="token punctuation">;</span> res <span class="token punctuation">.</span> <span class="token function">json</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> message <span class="token operator">:</span> <span class="token string">'Password reset successful'</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span> err <span class="token punctuation">)</span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">error</span> <span class="token punctuation">(</span> <span class="token string">'Failed to send password reset confirmation email:'</span> <span class="token punctuation">,</span> err <span class="token punctuation">)</span> <span class="token punctuation">;</span> res <span class="token punctuation">.</span> <span class="token function">status</span> <span class="token punctuation">(</span> <span class="token number">500</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">json</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> error <span class="token operator">:</span> <span class="token string">'Failed to send password reset confirmation email'</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
6. Kiểm tra việc triển khai
Để kiểm tra chức năng đặt lại mật khẩu, bạn có thể sử dụng các công cụ như Postman hoặc curl để gửi yêu cầu HTTP tới các tuyến /auth/forgot-password
và /auth/reset-password
.
- Gửi yêu cầu
POST
tới/auth/forgot-password
bằng địa chỉ email hợp lệ trong phần thân yêu cầu. - Kiểm tra hộp thư đến email của bạn để tìm email đặt lại mật khẩu và nhấp vào liên kết đặt lại.
- Gửi yêu cầu
POST
tới/auth/reset-passwor
d với mã thông báo từ liên kết đặt lại và mật khẩu mới trong nội dung yêu cầu. - Kiểm tra hộp thư đến email của bạn để biết email xác nhận đặt lại mật khẩu.
Phần kết luận
Trong bài viết này, chúng tôi đã triển khai thành công chức năng đặt lại mật khẩu an toàn trong ứng dụng Node.js Express. Chúng ta đã xem qua toàn bộ quá trình, từ thiết lập môi trường và cơ sở dữ liệu đến xử lý các yêu cầu đặt lại mật khẩu và cập nhật mật khẩu của người dùng.
Bằng cách làm theo các bước này, bạn có thể đảm bảo rằng ứng dụng của mình cung cấp trải nghiệm đặt lại mật khẩu an toàn và thân thiện với người dùng. Luôn nhớ sử dụng các thư viện cập nhật và tuân theo các phương pháp hay nhất khi xử lý dữ liệu nhạy cảm, chẳng hạn như mật khẩu và mã thông báo.
Và cuối cùng
Như mọi khi, tôi hy vọng bạn thích bài viết này và có một cái gì đó mới. Xin cảm ơn và hẹn gặp lại các bạn trong những bài viết tiếp theo!
Nếu các bạn thích bài viết này thì hãy cho mình 1 like và subscribe để ủng hộ mình nhé. Cảm ơn.