Xin chào tất cả các bạn, mình là Trung Quân aka trungquandev :D.
Như tiêu đề bài viết thì hôm nay mình sẽ hướng dẫn các bạn viết một API để gửi email, cụ thể là dùng gmail của google nha, kỹ thuật trong bài sẽ sử dụng OAuth2 kết hợp module Nodemailer trong NodeJS nhé.
“Bài này nằm trong loạt bài Lập Trình NodeJS từ cơ bản đến nâng cao trên trang blog chính thức trungquandev.com“
Những nội dung có trong bài:
- OAuth2 là gì và tại sao chúng ta cần sử dụng tới nó
- Triển khai Code ứng dụng gửi email với OAuth2 kết hợp cùng Nodemailer
- Chia sẻ full toàn bộ Source Code của bài hôm nay trên Github
1. OAuth2 là gì và tại sao chúng ta cần sử dụng tới nó
Nếu bạn nào đã theo dõi blog mình từ trước hoặc có tìm kiếm trên google với từ khóa “Gửi nhận email nodejs” sẽ thấy mình đã có một bài viết hướng dẫn các bạn gửi email với Nodemailer, dùng giao thức SMTP, đó là: “Gửi nhận email trong NodeJS cực kỳ đơn giản với nodemailer”
Tuy nhiên với cách này có một hạn chế đó là các bạn phải sử dụng địa chỉ email cũng như mật khẩu tài khoản google trong code để xác thực. Và để làm như vậy thì đồng nghĩa với việc trong phần Cấu hình bảo mật tài khoản google, các bạn phải “Bật chế độ ứng dụng kém an toàn”. Nhưng từ ngày 30-5-2022 Google sẽ không cho phép chúng ta sử dụng tính năng này nữa, như hình bên dưới:
Vì vậy mình viết bài này để cập nhật luôn cho các bạn một cách khác tối ưu hơn trong việc gửi email bằng NodeJS nhé. Cụ thể là OAuth2, vậy OAuth2 là gì? Cùng xem định nghĩa chính thức của nó ở đây nha:
The OAuth 2.0 authorization framework enables a third-party application to obtain limited access to an HTTP service, either on behalf of a resource owner by orchestrating an approval interaction between the resource owner and the HTTP service, or by allowing the third-party application to obtain access on its own behalf. This specification replaces and obsoletes the OAuth 1.0 protocol described in RFC 5849.
https://datatracker.ietf.org/doc/html/rfc6749
Mình sẽ không dịch chi tiết đoạn trên như mọi lần mà sẽ giải thích đơn giản theo ý hiểu của mình nha: “OAuth2 là một khung ủy quyền xác thực cho phép một ứng dụng của bên thứ ba có quyền truy cập và sử dụng dịch vụ HTTP của một ứng dụng gốc, từ đó thay mặt ứng dụng gốc để thực hiện một số tính năng mà chúng ta cần.“
Trong trường hợp hôm nay thì ứng dụng gốc chính là Gmail của google, còn ứng dụng bên thứ ba chính là ứng dụng mà chúng ta sẽ Code trong phần 2 nhé.
Dĩ nhiên để sử dụng được OAuth 2 thì ứng dụng gốc cũng phải cho phép nó thì chúng ta mới có thể sử dụng được, và Google cũng không làm chúng ta thất vọng về khoản này :D, các bạn có thể đọc ở đây:
Google APIs use the OAuth 2.0 protocol for authentication and authorization. Google supports common OAuth 2.0 scenarios such as those for web server, client-side, installed, and limited-input device applications.
https://developers.google.com/identity/protocols/oauth2
(Ngoài lề quen thuộc: Cảnh báo này dành cho bất kể trang nào khác mà có ý định copy bài không phải của các bạn thì hãy tôn trọng người viết bài chân chính, tuyệt đối không được xào nấu, chỉnh sửa linh tinh bài viết của mình cụ thể là không được xóa những liên kết (link) cũng như tự ý xóa các câu thoại của mình trong toàn bộ bài viết rồi post lại lên trang của các bạn như kiểu đây là bài của các bạn vậy, nếu tham khảo thì hãy để lại liên kết nguồn rõ ràng từ trang trungquandev, mình sẽ thường xuyên dùng tool để check, và nếu phát hiện ra thì cứ đơn giản là chắc chắn sẽ ăn report DMCA nhé.)
2. Triển khai Code ứng dụng gửi email với OAuth2 kết hợp cùng Nodemailer
Trước khi vào code thì chúng ta cần phải tạo ứng dụng trên Google Cloud Console cho Developer để sử dụng được OAuth2 nhé:
Truy cập vào https://console.cloud.google.com và đăng nhập tài khoản google của các bạn, tài khoản nào cũng được.
Đối với tài khoản mới chưa tạo ứng dụng nào thì các bạn sẽ thấy phần Select a project, click vào đó và chọn New Project, còn tài khoản đã từng có project rồi thì nó sẽ tự select cái ứng dụng cũ của các bạn, cũng click vào đó và chọn New Project như bình thường nhé.
– Tiếp theo là nhập tên Project và nhấn Create:
– Sau khi tạo xong Project, các bạn sẽ được redirect về trang Dashboard, bây giờ mở Menu (góc trên cùng bên trái) > Chọn APIs & Services > Credentials:
– Chọn tiếp Create Credentials > OAuth client ID
– Nếu các bạn thấy màn hình tiếp theo thông báo là: “To create an OAuth client ID, you must first configure your consent screen“, thì cứ nhấn cái button Configure Consent Screen để cấu hình nó trước nhé, ví dụ như hình dưới:
– Chọn User Type là External > rồi nhấn Create
– Tiếp theo sẽ có 4 bước, ở bước đầu tiên OAuth Consent screen thì các bạn nhập tên app cũng như vài trường require trên đầu, còn lại những trường không require thì cứ để trống đã nhé.
Bước 2(Scopes) – 3(Test Users) – 4(Summary) thì không cần điền gì, cứ nhấn Save rồi cuối cùng là nút Back To Dashboard.
– Như vậy là xong cái Configure Consent Screen, bây giờ các bạn quay lại từ bước Create Credentials > OAuth client ID, lúc này màn hình sẽ không còn hiển thị cái cấu hình Consent Screen như trên nữa, và chúng ta tiếp tục tạo Credential OAuth Client ID:
- Application type chọn Web application
- Name thì tùy các bạn nhé
- Authorized JavaScript Origins thì các bạn để trống cho mình (vì đây là ứng dụng test, mình cũng sẽ test bằng Postman chứ không dùng Web app, trong thực tế các bạn có thể set url trang web mà các bạn dùng để call api vào đây)
- Authorized Redirect URIs thì các bạn điền link sau vào: https://developers.google.com/oauthplayground (mục đích là để lát nữa chúng ta sẽ dùng oauthplayground của google để tạo Refresh Token.)
– Tạo xong thì nó sẽ redirect bạn về Credentials Dashboard, lúc này một popup sẽ xuất hiện, các bạn lưu lại 2 cái Client ID và Client Secret để lát chúng ta dùng nhé (Chú ý quan trọng: 2 cái Client ID và Client Secret phải được giữ bảo mật tuyệt đối, trong dự án thực tế thường sẽ cho vào biến môi trường ENV)
Gần xong rồi, chỉ còn một bước nữa để tạo mã Refresh Token cũng như chỉ định tài khoản google mà chúng ta sẽ dùng để gửi email:
– Các bạn truy cập vào https://developers.google.com/oauthplayground
– Click vào icon Setting ở góc trên bên phải màn hình.
– Tích vào: Use your own OAuth credenticals
– Copy và Paste đúng 2 cái Client ID và Client Secret vừa tạo ở trên vào sau đó nhấn close.
– Quay sang phần bên trái màn hình trong ô Input your own scopes các bạn nhập link gmail vào như sau: https://mail.google.com rồi nhấn Authorize APIs và chọn địa chỉ email của các bạn, (lưu ý: đây sẽ là địa chỉ email lát chúng ta dùng trong code để gửi email đi nhé, gọi là Admin Email)
– Sau khi chọn account ở trên thì khả năng rất cao các bạn sẽ gặp lỗi này:
– Lý do bởi vì cái OAuth consent screen ban nãy chúng ta đang để trạng thái Testing và chưa có chỉ định user nào được phép test, các bạn quay lại phần APIs & Services > OAuth consent screen và thêm user để test như hình dưới:
Lưu ý: Nếu bạn nào không để Testing mà chọn Public app thì lúc đó user nào cũng sử dụng được nhé, và các bạn sẽ phải Verify cái app với Google, còn hiện tại chúng ta chỉ cần để Testing và cho phép một vài user cụ thể được phép dùng app gửi email thôi nha.
– Quay lại phần bị lỗi ban nãy, các bạn chọn đúng account mà đã thêm trong phần Test users ở trên, lúc này sẽ thấy những màn hình sau: (vì App chúng ta để Testing nên nó cảnh báo là chưa xác minh (Verify) ứng dụng thôi, cứ kệ nó mà chọn Tiếp Tục hết nhé.)
Tiếp tục quay lại trang playground, lúc này ở Step 2: Exchange authorization code for tokens, làm lần lượt:
– Tích vào nút checkbox Auto-refresh the token before it expires
– Nhấn button: Exchange authorization code for tokens
– Chúng ta chỉ cần quan tâm cái Refresh Token để lát nữa sử dụng trong code thôi nhé, Access Token thì cứ kệ nó đi vì nó sẽ hết hạn sau một khoảng thời gian.
– Riêng với Refresh Token thì cái expiry date của nó sẽ là 7 ngày nếu app của bạn để chế độ testing, còn public thì nó sẽ không có hạn, nhưng mà vẫn có trường hợp refreshToken không sử dụng được nữa nếu như nó không hoạt động trong vòng 60 ngày…vv Cụ thể hơn mình sẽ để link docs của google ở đấy cho các bạn đọc thêm phần này nha:
https://developers.google.com/identity/protocols/oauth2#expiration
– Step 3 là Configure request to API cũng không cần quan tâm luôn, đến đây là xong phần cấu hình khá phức tạp rồi, tiếp theo là bắt tay vào code ứng dụng gửi email của chúng ta nhé.
Sau phần cấu hình vất vả ở trên thì bây giờ vào code nhẹ nhàng hơn này 😀
- Cấu trúc thư mục dự án của chúng ta sẽ trông như sau:
Lưu ý: Cấu trúc demo này mình sẽ làm đơn giản nhất có thể với một file server.js để các bạn có thể dễ dàng hiểu được, còn trong thực tế đi làm thì sẽ cần tách việc xử lý gửi Mail ra một Service hoặc Provider riêng, cũng như những cái OAuth Client ID, OAuth Client Secret hay Refresh Token cần bảo mật thì sẽ được đưa vào biến môi trường thay vì để luôn trong code, nếu bạn nào quan tâm tới phần Coding, tổ chức Code nâng cao này thì có thể tham khảo 2 khóa Lập Trình MERN Stack (NodeJS, ReactJS, ExpressJS, MongoDB) nâng cao của mình nhé.
– Khóa Start MERN Stack – Xây dựng ứng dụng kéo thả tương tự Trello
– Khóa MERN Stack nâng cao: (đang cập nhật, sẽ sớm có trong tháng 7/2022)
src
server.js
package.json
Việc khởi tạo ứng dụng nodejs thì mình không làm lại, các bạn có thể xem cách làm ở các bài viết trước của mình tại link bên dưới đây:
https://trungquandev.com/series-lap-trinh-nodejs/
Tiếp theo, trong ví dụ ngày hôm nay, chúng ta sẽ cài đặt 3 module lần lượt là:
express, nodemailer và google-auth-library
npm install --save express nodemailer
google-auth-library
Hoặc:
yarn add express nodemailer google-auth-library
File: src/server.js
Mình đã tối giản code cũng như comment giải thích rất chi tiết trong code luôn rồi, bạn nào còn thắc mắc hay gặp vấn đề thì cứ Inbox cho mình trên page facebook Trung Quân DEV nhé.
/** * Created by trungquandev.com's author on 05/16/2022 * NodeJS send email by OAuth2 and Nodemailer * Tutorial here: https://trungquandev.com/nodejs-viet-api-gui-email-voi-oauth2-va-nodemailer */ // Import 3 thư viện cần thiết import express from 'express' import nodemailer from 'nodemailer' import { OAuth2Client } from 'google-auth-library' // Khởi tạo NodeJS App bằng Express const app = express() // Cho phép nhận data thông qua req.body của API gửi lên. app.use(express.json()) /** * Những biến sau trong thực tế nên đưa vào biến môi trường ENV vì mục đích bảo mật hơn. * Các bạn có thể tham khảo khóa Lập Trình MERN Stack nâng cao trên kênh YouTube: * Trungquandev Official của mình để học cách triển khai & tổ chức code như đi làm thực tế nhé. * Link: https://www.youtube.com/c/TrungquandevOfficial/ */ const APP_PORT = 3000 const APP_HOST = 'localhost' const GOOGLE_MAILER_CLIENT_ID = 'Nhập OAuth Client ID của bạn vào đây.' const GOOGLE_MAILER_CLIENT_SECRET = 'Nhập OAuth Client Secret của bạn vào đây.' const GOOGLE_MAILER_REFRESH_TOKEN = 'Nhập Refresh Token của bạn vào đây.' const ADMIN_EMAIL_ADDRESS = 'Nhập Email của bạn vào đây. (Chính là cái email mà mình đã hướng dẫn thêm vào trong phần Test Users)' // Khởi tạo OAuth2Client với Client ID và Client Secret const myOAuth2Client = new OAuth2Client( GOOGLE_MAILER_CLIENT_ID, GOOGLE_MAILER_CLIENT_SECRET ) // Set Refresh Token vào OAuth2Client Credentials myOAuth2Client.setCredentials({ refresh_token: GOOGLE_MAILER_REFRESH_TOKEN }) // Tạo API /email/send với method POST app.post('/email/send', async (req, res) => { try { // Lấy thông tin gửi lên từ client qua body const { email, subject, content } = req.body if (!email || !subject || !content) throw new Error('Please provide email, subject and content!') /** * Lấy AccessToken từ RefreshToken (bởi vì Access Token cứ một khoảng thời gian ngắn sẽ bị hết hạn) * Vì vậy mỗi lần sử dụng Access Token, chúng ta sẽ generate ra một thằng mới là chắc chắn nhất. */ const myAccessTokenObject = await myOAuth2Client.getAccessToken() // Access Token sẽ nằm trong property 'token' trong Object mà chúng ta vừa get được ở trên const myAccessToken = myAccessTokenObject?.token // Tạo một biến Transport từ Nodemailer với đầy đủ cấu hình, dùng để gọi hành động gửi mail const transport = nodemailer.createTransport({ service: 'gmail', auth: { type: 'OAuth2', user: ADMIN_EMAIL_ADDRESS, clientId: GOOGLE_MAILER_CLIENT_ID, clientSecret: GOOGLE_MAILER_CLIENT_SECRET, refresh_token: GOOGLE_MAILER_REFRESH_TOKEN, accessToken: myAccessToken } }) // mailOption là những thông tin gửi từ phía client lên thông qua API const mailOptions = { to: email, // Gửi đến ai? subject: subject, // Tiêu đề email html: `<h3>${content}</h3>` // Nội dung email } // Gọi hành động gửi email await transport.sendMail(mailOptions) // Không có lỗi gì thì trả về success res.status(200).json({ message: 'Email sent successfully.' }) } catch (error) { // Có lỗi thì các bạn log ở đây cũng như gửi message lỗi về phía client console.log(error) res.status(500).json({ errors: error.message }) } }) // Run application app.listen(APP_PORT, APP_HOST, () => { console.log(`Hello Trungquandev Official, I'm running at ${APP_HOST}:${APP_PORT}/`) })
– Các bạn chạy ứng dụng lên bằng lệnh: node src/server.js
– Sau đó mở Postman, nhập đủ thông tin API và body của request rồi nhấn Send
– Nếu có lỗi thì các bạn sẽ thấy nó được trả về ở Postman cũng như log ra trong phần terminal của code.
– Còn nếu không có lỗi gì thì sẽ nhận message success cũng như Email đã được gửi thành công, các bạn có thể vào gmail để check nhé:
3. Chia sẻ full toàn bộ Source Code của bài hôm nay trên Github
Vậy là kết thúc bài hôm nay chúng ta đã cùng nhau hoàn thiện về ý tưởng và cách triển khai cho việc Gửi email trong nodejs với OAuth2 và Nodemailer rồi.
Mình có để full source code của bài hôm nay ở repo này (Thư mục số 6) cho các bạn tham khảo giống như mọi lần, các bạn có thể clone về và làm theo các bước hướng dẫn mà mình có để trong file README cũng như file code nhé.
https://github.com/trungquandev/trungquandev-public-utilities-algorithms
Và nếu thấy bài viết cũng như video bổ ích, hãy ủng hộ mình bằng cách Đăng ký kênh Youtube Trungquandev Official để mình có động lực tiếp tục viết những bài viết hay cũng như ra thêm nhiều video chất lượng hơn nữa nha, cảm ơn các bạn nhiều !!!
https://www.youtube.com/c/TrungquandevOfficial
Cảm ơn các bạn đã dành thời gian đọc bài viết.
Xin chào và hẹn gặp lại các bạn ở những bài viết tiếp theo.
Best Regards – 💻 Trungquandev Official ❤
Tham khảo kiến thức:
https://nodemailer.com/smtp/oauth2/
https://www.npmjs.com/package/google-auth-library
https://developers.google.com/oauthplayground
https://alexb72.medium.com/how-to-send-emails-using-a-nodemailer-gmail-and-oauth2-fe19d66451f9
“Thanks for awesome knowledges.”
“ From author: trungquandev ”