Để có thể gửi được gmail từ API, bạn cần yêu cầu quyền của chủ tài khoản gmail. Ngay cả khi mail đó là của bạn thì bạn cũng cần phải yêu cầu cấp quyền từ chính mình và trao quyền cho chính mình thì mới dùng mail đó để gửi được.

Để gửi được gmail dùng oauth2 thì các bước cấu hình và chuẩn bị khá lằng nhằng, cần tập trung chú ý những điểm sau:

 

1. Tạo credentials trên console của google

         Tạo Gmail api trên google console

 

  • Enable gmail api xong thì chọn Credentials và tạo với các thông số sau:

Chọn loại OAuth client ID

        Tạo gmail oauth2 api

 

Chọn Application type là Other, và đặt tên cho credential:

    Tạo credential cho gmail api

 

  • Sau khi tạo xong credential, download file về và đặt vào thư mục ~/.credentials/your_credential.json 

           Tạo credential cho gmail api

 

2. Lấy access_token

Để có thể gửi được mail từ một tài khoản gmail nào đó, bạn phải lấy được quyền gửi mail của email đó. Bước lấy được quyền này chính là lấy được access_token trong oauth2. access_token này sẽ được lưu vào file configs/token.json.

Các bước làm như sau:

  • Khi khởi động project (nodejs), check xem đã có dữ liệu trong file configs/token.json chưa, nếu có rồi thì lấy access_token, refresh_token expires từ file đó ra, nếu chưa có dữ liệu trong file đó (hoặc chưa có file đó) thì yêu cầu lấy access_token từ người dùng (là chính bạn nếu bạn muốn gửi mail từ email của chính mình).
  • Để yêu cầu lấy được access_token cần đưa người dùng tới một url yêu cầu cấp quyền. Nếu chỉ cần lấy access_token của email của chính bạn thì có thể output cái url đó ra terminal và làm thủ công bằng tay.

 

Check access_token mỗi khi khởi động project:

// app.js

require('dotenv').config()  // using dotenv
require('./configs/folder_path') // setup CONFIG_DIR = 'configs/'
require(CONFIG_DIR + 'gmail_auth') // = require('configs/gmail_auth.js')
// other code ...
'use strict';
const fs = require('fs')
const readline = require('readline');
const {google} = require('googleapis')
const CREDENTIAL_FILE = (process.env.HOME || process.env.HOMEPATH ||
    process.env.USERPROFILE) + '/.credentials/your_credential.json';

// If modifying these scopes, delete token.json.
const SCOPES = [
  'https://mail.google.com/'
];
const TOKEN_PATH = CONFIG_DIR + 'token.json'

fs.readFile(CREDENTIAL_FILE, (err, content) => {
  if(err) throw err
  checkToken(JSON.parse(content))
})

// check token exist in token file
function checkToken(credential) {
  let clientSecret = credential.installed.client_secret;
  let clientId = credential.installed.client_id;
  let redirectUrl = credential.installed.redirect_uris[0];
  let oauth2Client = new google.auth.OAuth2(clientId, clientSecret, redirectUrl);

  fs.readFile(TOKEN_PATH, (err, token) => {
    if(err) return getNewToken(oauth2Client)
    oauth2Client.setCredentials(JSON.parse(token));
  })
}

function getNewToken(oauth2Client) {
  const authUrl = oauth2Client.generateAuthUrl({
    access_type: 'offline',
    scope: SCOPES,
  });
  console.log('Authorize this app by visiting this url:', authUrl);
  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
  });
  rl.question('Enter the code from that page here: ', (code) => {
    rl.close();
    oauth2Client.getToken(code, (err, token) => {
      if (err) return console.error('Error retrieving access token', err);
      oauth2Client.setCredentials(token);
      // Store the token to disk for later program executions
      fs.writeFile(TOKEN_PATH, JSON.stringify(token), (err) => {
        if (err) return console.error(err);
        console.log('Token stored to', TOKEN_PATH);
      });
    });
  });
}

Chú ý ở đoạn code trên scopes phải chính xác là https://mail.google.com/ thì mới gửi được bằng smtp.

 

Khi khởi động project, terminal sẽ hiển thị url, copy url này vào trình duyệt và cấp quyền cho app, khi cấp quyền trên trìn duyệt thì copy đoạn text trả về dán vào terminal

Authorize this app by visiting this url: https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&scope=https%3A%2F%2Fmail.google.com%2F&response_type=code&client_id=***&redirect_uri=***

Enter the code from that page here: ***

Token stored to .../configs/token.json

 

        cấp quyền cho gmail trong nodejs

          Cấp quyền cho gmail trong express

Vào https://myaccount.google.com/lesssecureapps và cho phép các ứng dụng kém bảo mật thao tác với tài khoản của bạn (Bước này mình không cần làm vẫn gửi được bình thường).

 

3. Gửi mail bằng nodemailer

'use strict';
const fs = require('fs')
const nodemailer = require('nodemailer');
const TOKEN_PATH = CONFIG_DIR + 'token.json'
const CREDENTIAL_FILE = (process.env.HOME || process.env.HOMEPATH ||
    process.env.USERPROFILE) + '/.credentials/your_credential.json';

class Mailer {
  static async send(data) {
    var mailOptions = {
        from: 'from_your_gmail',
        to: 'to_mail', // list of receivers
        subject: `Your Subject`, // Subject line
        text: `your text`, // plain text body
        html: `<div>your html</div>`, // html body
    }

    var credentials = JSON.parse(fs.readFileSync(CREDENTIAL_FILE))
    var tokens = JSON.parse(fs.readFileSync(TOKEN_PATH))

    // create reusable transporter object using the default SMTP transport
    const transporter = nodemailer.createTransport({
            service: 'Gmail',
            host: 'smtp.gmail.com',
            port: 465,
            secure: true, // true for 465, false for other ports
            auth: {
                type: 'OAuth2',
                user: 'your_gmail',
                clientId: credentials.installed.client_id.toString(),
                clientSecret: credentials.installed.client_secret.toString(),
                refreshToken: tokens.refresh_token.toString(),
                accessToken: tokens.access_token.toString(),
                expires: tokens.expiry_date,
            }
          })

    transporter.sendMail(mailOptions, (error, info) => {
        if (error) {
            return console.log(error);
        }
        console.log('Message sent: %s', info.messageId);
    })
  }
}

module.exports = Mailer

 

Gọi Mailer.send('test data') là mail sẽ gửi cho bạn.

 

Link tham khảo:

https://developers.google.com/gmail/api/quickstart/nodejs