I'm running a Firebase cloud function whose purpose is to handle the backend of a "contact me" form. The Express framework is being used for middleware functions. When the submit button is clicked, a POST
request is made to the /submit
endpoint.
index.js
in the functions folder is as below:
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const bodyParser = require("body-parser");
const express = require("express");
const emailRoute = require("./routes/email");
// Initialize Firebase in order to access its services.
admin.initializeApp();
const app = express();
// Automatically allow cross-origin requests.
app.use(cors({ origin: true }));
app.use(bodyParser.urlencoded({extended: false}));
app.use(emailRoute);
// Expose Express API as a single Cloud Function.
exports.app = functions.https.onRequest(app);
The imported router from email.js
is as follows:
const nodemailer = require("nodemailer");
const express = require("express");
const router = express.Router();
router.post("/submit", (req, res) => {
const transporter = nodemailer.createTransport({
service: "gmail",
auth: {
user: "email@gmail.com",
pass: "password",
},
});
const myEmail = {
to: "myemail@gmail.com",
subject: `A new message from ${req.body.name}`,
text: `${req.body.name} sent the following message:
\n\n ${req.body.message}
\n\n Senders email: ${req.body.email}`,
};
const sendersEmail = {
to: req.body.email,
subject: "A copy of your message to me",
text: `You just sent me the following message:\n\n${req.body.message}`,
};
console.log("SUBMIT REQUEST PROCESSING");
transporter.sendMail(myEmail);
transporter.sendMail(sendersEmail);
res.redirect("/#contact");
console.log("PROCESSING COMPLETE");
});
module.exports = router;
There is no issue when running this in the local environment - the email gets sent to both parties. However, when run in the hosted environment as a Firebase function the following error is thrown: Error: Process exited with code 16
(as displayed in the function logs section of the Firebase console). A previous SO answer indicates an uncaughtException
or unhandledRejection
.
Before the error, both console.log()
statements are logged. Then the function finishes with a 302
status code (as it does when run locally and successfully). After that, there is the unhandled rejection followed by Error: Invalid login
and a link to my Google account and a statement "Please log in via your web browser and then try again".
Could this be a Firebase security measure against automated mailing that nodemailer
is attempting to execute?
I needed to both enable less secure apps and display unlock captcha on the gmail account, only the former of which I had previously done.
I also needed to set the gmail.email
and gmail.password
Google cloud environment variables. This can be done with the following shell command: firebase functions:config:set gmail.email="myusername@gmail.com" gmail.password="secretpassword"
A great resource I found on this is an official Firebase function sample that utilises Nodemailer, where the above is covered.