Skip to main content

OAuth2

OAuth2 lets your application use short‑lived access tokens instead of passwords. Tokens are scoped, revocable, and regenerable, so a leaked token causes far less harm than a leaked password.

  1. Provider‑agnostic OAuth2 authentication
  2. Gmail‑specific helpers
tip

Managing OAuth2 app credentials is painful. Let EmailEngine handle them for you. Once an account is registered with EmailEngine, you can point Nodemailer to EmailEngine and skip all authentication completely. Read more here.

Provider‑agnostic OAuth2 authentication

Use this method when the SMTP server accepts a plain username + access token pair. No client secrets or refresh tokens are involved.

  • auth – authentication object

    • type'OAuth2'
    • user – e‑mail address (required)
    • accessToken – access token (required)
    • expires – UNIX timestamp when accessToken expires (optional)

Token scopes • Gmail – request the token with the https://mail.google.com/ scope • Outlook – request the token with the https://outlook.office.com/SMTP.Send scope

let transporter = nodemailer.createTransport({
host: "smtp.gmail.com",
port: 465,
secure: true,
auth: {
type: "OAuth2",
user: "user@example.com",
accessToken: "ya29.Xx_XX0xxxxx-xX0X0XxXXxXxXXXxX0x",
},
});
tip

Normal (non‑pooled) transports can override auth per‑message. Create the transport once and pass different tokens in sendMail options as needed.

Gmail‑specific helpers

The sections below cover Gmail‑only flows that Nodemailer can automate for you.

3‑legged OAuth2 authentication

Your app requests consent from the user and receives a refreshToken. Nodemailer uses this token to generate fresh accessTokens when needed.

  • auth – authentication object

    • type'OAuth2'
    • user – e‑mail address (required)
    • clientId – OAuth2 client ID (required)
    • clientSecret – OAuth2 client secret (required)
    • refreshToken – refresh token (required)
    • accessToken – access token (optional; Nodemailer auto‑refreshes if missing or expired)
    • expires – UNIX expiration timestamp for accessToken (optional)
    • accessUrl – custom token endpoint (optional; defaults to Gmail)

2LO authentication (service accounts)

Use a Google service account to impersonate a user. No interactive consent is required.

  • auth – authentication object

    • type'OAuth2'
    • user – e‑mail address to send as (required)
    • serviceClient – service account client_id (required)
    • privateKey – service account private key (required)

Using custom token handling

Register an oauth2_provision_cb callback that returns a token whenever Nodemailer needs one.

transporter.set("oauth2_provision_cb", (user, renew, cb) => {
const token = userTokens[user];
if (!token) return cb(new Error("Unknown user"));
cb(null, token);
});

Token update notifications

Listen for the token event to persist newly generated tokens.

transporter.on("token", (t) => {
console.log("User:", t.user);
console.log("New access token:", t.accessToken);
console.log("Expires at:", new Date(t.expires));
});

Examples

  1. Authenticate with an existing token
let transporter = nodemailer.createTransport({
host: "smtp.gmail.com",
port: 465,
secure: true,
auth: {
type: "OAuth2",
user: "user@example.com",
accessToken: "ya29.Xx_XX0xxxxx-xX0X0XxXXxXxXXXxX0x",
},
});
  1. Custom handler – token returned by your own service
let transporter = nodemailer.createTransport({
host: "smtp.gmail.com",
port: 465,
secure: true,
auth: { type: "OAuth2", user: "user@example.com" },
});

transporter.set("oauth2_provision_cb", (user, renew, cb) => {
cb(null, userTokens[user]);
});
  1. Full 3‑legged setup – Nodemailer refreshes tokens automatically
let transporter = nodemailer.createTransport({
host: "smtp.gmail.com",
port: 465,
secure: true,
auth: {
type: "OAuth2",
user: "user@example.com",
clientId: "000000000000-xxx.apps.googleusercontent.com",
clientSecret: "XxxxxXXxX0xxxxxxxx0XXxX0",
refreshToken: "1/XXxXxsss-xxxXXXXXxXxx0XXXxxXXx0x00xxx",
accessToken: "ya29.Xx_XX0xxxxx-xX0X0XxXXxXxXXXxX0x",
expires: 1484314697598,
},
});
  1. Service account – token re‑generated via 2LO
let transporter = nodemailer.createTransport({
host: "smtp.gmail.com",
port: 465,
secure: true,
auth: {
type: "OAuth2",
user: "user@example.com",
serviceClient: "113600000000000000000",
privateKey: "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBg...",
accessToken: "ya29.Xx_XX0xxxxx-xX0X0XxXXxXxXXXxX0x",
expires: 1484314697598,
},
});
  1. Per‑message auth – single transport, many users
let transporter = nodemailer.createTransport({
host: "smtp.gmail.com",
port: 465,
secure: true,
auth: {
type: "OAuth2",
clientId: "000000000000-xxx.apps.googleusercontent.com",
clientSecret: "XxxxxXXxX0xxxxxxxx0XXxX0",
},
});

transporter.sendMail({
from: "sender@example.com",
to: "recipient@example.com",
subject: "Message",
text: "I hope this message gets through!",
auth: {
user: "user@example.com",
refreshToken: "1/XXxXxsss-xxxXXXXXxXxx0XXXxxXXx0x00xxx",
accessToken: "ya29.Xx_XX0xxxxx-xX0X0XxXXxXxXXXxX0x",
expires: 1484314697598,
},
});
info

Per‑message auth does not work with pooled transports.


Troubleshooting

  • Gmail SMTP requires the https://mail.google.com/ scope – ensure your token has it.
  • Gmail API access must be enabled for your Client ID in Google API Manager.