I upgraded a 4 years old cloud function from gen 1 to gen 2 using defineSecret
instead of functions.config()
to get environment variables and using OAuth2
authentication instead of having my gmail setup to authorize "less secure apps".
The function deploys successfully but when I run it I get an "invalid user or password" error. The function is a simple one that takes the data from a contact form and emails it to me, using my gmail as sender. I have OAuth consent screen on testing mode with my email as a test user. I generated a refresh token with OAuth 2.0 Playground using this API: https://www.googleapis.com/auth/gmail.send
Here is the exact log:
Access Token obtained successfully
Error creating transport: Error: Invalid login: 535-5.7.8 Username and Password not accepted.
EDIT
Here is the function:
const clientId = defineSecret("CLIENT_ID");
const clientSecret = defineSecret("CLIENT_SECRET");
const refreshToken = defineSecret("REFRESH_TOKEN");
async function getAccessToken(oauth2Client) {
try {
const { token } = await oauth2Client.getAccessToken();
return token;
} catch (error) {
console.error('Error getting access token:', error);
throw new Error(`Failed to get access token: ${error.message}`);
}
}
const createTransport = async () => {
try {
const oauth2Client = new google.auth.OAuth2(
await clientId.value(),
await clientSecret.value(),
"https://developers.google.com/oauthplayground"
);
oauth2Client.setCredentials({
refresh_token: await refreshToken.value()
});
const accessToken = await getAccessToken(oauth2Client);
if (!accessToken) {
throw new Error('Failed to obtain access token');
}
console.log('Access Token obtained successfully');
const transportConfig = {
host: 'smtp.gmail.com',
port: 465,
secure: true,
auth: {
type: 'OAuth2',
user: await emailSender.value(),
clientId: await clientId.value(),
clientSecret: await clientSecret.value(),
refreshToken: await refreshToken.value(),
accessToken,
},
};
const transporter = nodemailer.createTransport(transportConfig);
await transporter.verify();
console.log('SMTP connection verified successfully');
return transporter;
} catch (error) {
console.error('Error creating transport:', error);
throw new Error(`Failed to create transport: ${error.message}`);
}
};
The problem was related to the API that I was using. Since my function does nothing else than sending an email with my gmail account, I thought that using https://www.googleapis.com/auth/gmail.send
in the OAuth 2.0 playground would be enough but no.
I had to authorize full access to my gmail account with https://mail.google.com/
and now the function is working.