I have a mern application on DigitalOean's app platform. If a user registers they are sent a welcome email asking them to verify their email address. If they do not, when they login they see a button on the profile page to resent verification email.
I'm using nodemailer to send the email, currently through my personal account (that will change later). It works on the localhost and also on Render.com but on digital ocean I am getting the following error:
Error: Missing credentials for "PLAIN"
[mat-scout] [2023-11-08 02:05:49] at SMTPConnection._formatError (/workspace/node_modules/nodemailer/lib/smtp-connection/index.js:790:19)
[mat-scout] [2023-11-08 02:05:49] at SMTPConnection.login (/workspace/node_modules/nodemailer/lib/smtp-connection/index.js:444:38)
[mat-scout] [2023-11-08 02:05:49] at /workspace/node_modules/nodemailer/lib/smtp-transport/index.js:272:32
[mat-scout] [2023-11-08 02:05:49] at SMTPConnection.<anonymous> (/workspace/node_modules/nodemailer/lib/smtp-connection/index.js:213:17)
[mat-scout] [2023-11-08 02:05:49] at Object.onceWrapper (node:events:627:28)
[mat-scout] [2023-11-08 02:05:49] at SMTPConnection.emit (node:events:513:28)
[mat-scout] [2023-11-08 02:05:49] at SMTPConnection._actionEHLO (/workspace/node_modules/nodemailer/lib/smtp-connection/index.js:1347:14)
[mat-scout] [2023-11-08 02:05:49] at SMTPConnection._processResponse (/workspace/node_modules/nodemailer/lib/smtp-connection/index.js:969:20)
[mat-scout] [2023-11-08 02:05:49] at SMTPConnection._onData (/workspace/node_modules/nodemailer/lib/smtp-connection/index.js:755:14)
[mat-scout] [2023-11-08 02:05:49] at TLSSocket.SMTPConnection._onSocketData (/workspace/node_modules/nodemailer/lib/smtp-connection/index.js:193:44) {
[mat-scout] [2023-11-08 02:05:49] code: 'EAUTH',
[mat-scout] [2023-11-08 02:05:49] command: 'API'
[mat-scout] [2023-11-08 02:05:49] }
I tried adding text: "Hello World" for now but I still get the error. Here is the mail.js file:
import nodemailer from "nodemailer";
import path from "path";
import { fileURLToPath } from "url";
import { generateTemplate } from "../mail/template.js";
const generateMailTransporter = () => {
let transport = nodemailer.createTransport({
host: "smtp.comcast.net",
port: 587,
secure: false,
auth: {
user: process.env.EMAIL,
pass: process.env.EMAIL_PASS,
},
});
return transport;
};
export const sendVerificationEmail = async (token, link, profile) => {
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const transport = generateMailTransporter();
const { firstName, email } = profile;
const welcomeMessage = `Hi ${firstName}, welcome to ! There are so many features available to verified users. Use the given OTP (One-Time-Password) to verify your email.`;
transport.sendMail({
to: email,
from: process.env.VERIFICATION_EMAIL,
subject: "Welcome to MatScout",
text: "hello world",
html: generateTemplate({
title: "Welcome to MatScout",
message: welcomeMessage,
logo: "cid:logo",
banner: "cid:welcome",
link,
btnTitle: "Verify Your Email",
}),
attachments: [
{
fileName: "logo.png",
path: path.join(__dirname, "../mail/logo.png"),
cid: "logo",
},
{
fileName: "welcome.png",
path: path.join(__dirname, "../mail/welcome.png"),
cid: "welcome",
},
],
});
};
export const sendForgotPasswordLink = async (link, profile) => {
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const transport = generateMailTransporter();
const { email } = profile;
const message = `We just received a request to reset your password. Use the link beloow to create a new password. <br><br> If you did not send this request, it means someone is trying to access your account and you can disregard this email.`;
async function main() {
let info = await transport.sendMail({
to: email,
from: process.env.VERIFICATION_EMAIL,
subject: "Reset Password Link",
text: "hello world",
html: generateTemplate({
title: "Forgot Password",
message,
logo: "cid:logo",
banner: "cid:forget_password",
link,
btnTitle: "ResetPassword",
}),
attachments: [
{
fileName: "logo.png",
path: path.join(__dirname, "../mail/logo.png"),
cid: "logo",
},
{
filename: "forget_password.png",
path: path.join(__dirname, "../mail/forget_password.png"),
cid: "forget_password",
},
],
});
}
main().catch(console.error);
};
export const sendPassResetSuccessEmail = async (firstName, email) => {
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const transport = generateMailTransporter();
const message = `Dear ${firstName}, your password has just been reset successfuly. You can now sign in with your new password.`;
transport.sendMail({
to: email,
from: process.env.VERIFICATION_EMAIL,
subject: "Password Reset Successfully!",
text: "hello world",
html: generateTemplate({
title: "Password Reset Successfully",
message,
logo: "cid:logo",
banner: "cid:forget_password",
link: process.env.SIGN_IN_URL,
btnTitle: "Sign In",
}),
attachments: [
{
fileName: "logo.png",
path: path.join(__dirname, "../mail/logo.png"),
cid: "logo",
},
{
filename: "forget_password.png",
path: path.join(__dirname, "../mail/forget_password.png"),
cid: "forgot_password",
},
],
});
};
The error message suggests that the authentication details are not being provided correctly to the SMTP server. Can you log them to the console correctly inside the generateMailTransporter
function?
E.g.:
const generateMailTransporter = () => {
// What does the following output?
console.log('Email:', process.env.EMAIL);
console.log('Email Pass:', process.env.EMAIL_PASS);
let transport = nodemailer.createTransport({
host: "smtp.comcast.net",
port: 587,
secure: false,
auth: {
user: process.env.EMAIL,
pass: process.env.EMAIL_PASS,
},
});
return transport;
};
If that is logged correctly, the only other things that occur to me are:
a) that some email providers restrict SMTP access from cloud platforms to prevent abuse. You may need to use a dedicated transactional email service such as SendGrid.
b) On DigitalOcean's App Platform, you can set environment variables (secrets) in the App Spec or through the Settings tab of your app. Verify that these are set up correctly and nothing is conflicting here.
HTH