firebasegoogle-cloud-platformoauth-2.0google-cloud-run

Google Cloud Function - Invalid login


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}`);
  }
};


Solution

  • 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.