With the shut down of Less secure apps by Google on May 30, 2022, using Gmail with nodemailer now throws an error that says response: '535-5.7.8 Username and Password not accepted. Learn more at\n' + '535 5.7.8 https://support.google.com/mail/?p=BadCredentials. The Nodemailer docs appears to not be updated yet regarding this issue of Less secure apps but suggest to use another delivery provider. I used to just turn on LSA, store the credentials in an environment variable and let nodemailer do its thing, with this change, how can one still use gmail with nodemailer? There are no youtube tutorials to fix this yet and looking at the google documentation, it doesn't show nodemailer
Solved it by creating App password inside Google account. You must have 2-step verification actived.
Open Mail > Settings > See all Settings > Forwarding and POP/IMAP.
Enable POP download: & Enable IMAP access: (then save the settings). Mail Settings Image
Open Your Gmail Account > security > 2-step verification(enable it).
Go to App Passwords > select device > select app(you can create any custom app).
Copy App Password and use it in your application.
You should look into xoauth Nodemailer appears to support serval oauth options
let transporter = nodemailer.createTransport({
host: "smtp.gmail.com",
port: 465,
secure: true,
auth: {
type: "OAuth2",
user: "user#example.com",
clientId: "000000000000-xxx0.apps.googleusercontent.com",
clientSecret: "XxxxxXXxX0xxxxxxxx0XXxX0",
refreshToken: "1/XXxXxsss-xxxXXXXXxXxx0XXXxxXXx0x00xxx",
accessToken: "ya29.Xx_XX0xxxxx-xX0X0XxXXxXxXXXxX0x",
expires: 1484314697598,
},
});
Trying to get nodemailer to work with Outlook and to send emails from the outlook email. I need the private key, access key, and the serviceClient, preferably that don't expire. And I see no way of getting those for Azure, for google it was simple enough, I just got the service account but the Azure doesn't have that option, only has client secret and certificates.
I need something like this
let transporter = nodemailer.createTransport({
host: 'Outlook365',
auth: {
type: 'OAuth2',
user: 'MYOUTLOOKEMAIL#DOMAIN.COM',
serviceClient: '113600000000000000000',
privateKey: '-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBg...',
accessToken: 'ya29.Xx_XX0xxxxx-xX0X0XxXXxXxXXXxX0x',
expires: 1484314697598
}
});
This is the related question
What is necessary to obtain service account like information from Microsoft/ Azure for my usecase? I probably have to register an app with them but I found no way of getting a service account. I need it for SERVER-TO-SERVER communication so no human interaction.
I'm creating a Twitter bot and I'm implementing a method that sends me a email if there is an error. As I'm already using the google API to access Google Drive (have no problem here), I decided to use the service account to send the email (Google console says it could be used that way)
The method I've come up to send the email so far is:
var config = require('./config/mail');
var google = require('./config/google');
var nodemailer = require('nodemailer');
var send = function (args) {
let transporter = nodemailer.createTransport({
'service': 'gmail',
'auth': {
'type': 'OAuth2',
'user': google.client_email,
'serviceClient': google.client_id,
'privateKey': google.private_key
}
});
transporter.on('token', token => console.log(token));
let message = {
'from': `"${config.serverFromName}" <${config.serverFromMail}>`,
'to': args.to,
'subject': args.subject,
'text': args.text,
'html': `<p>${args.text}</p>`
};
transporter.sendMail(message, (err, info) => {
if (err) {
console.log('Mail couldn\'t be sent because: ' + err);
} else {
console.log('Mail sent');
}
});
};
The config/google file contains the data that Google generates for you when you create a service account. config.serverFromName and config.serverFromMail are the name and email of the sender (not the same as the service account id). args contains the recipent email and the content
When I test the send method, I got the following message in my console:
Mail couldn't be sent because: Error: Invalid login: 535-5.7.8 Username and Password not accepted. Learn more at
535 5.7.8 https://support.google.com/mail/?p=BadCredentials z123sm543690vkd.10 - gsmtp
I know the token is being created correctly because the listener I created is printing it:
{ user: 'name#project.iam.gserviceaccount.com',
accessToken: 'ya29.ElmIBLxzfU_kkuZeyISeuRBeljmAe7HNTlwuG4K12ysUNo46s-eJ8NkMYHQqD_JrqTlH3yheNc2Aopu9B5vw-ivEqvPR4sTDpWBOg3xUU_4XiJEBLno8FHsg',
expires: 1500151434603 }
Searching on the Internet I found that it may be a problem with the OAuth scope. However, all the info that talks about it refers to using Client IDs, not service accounts. I don't find that option in the Google developer console, either.
Any ideas of what I'm doing wrong?
Bottom Line: The specific way Google describes a service account is INCOMPATIBLE with nodemailer. BUT there is a way!
I have just spent countless hours myself up over this same issue! I have come to the conclusion, Google's Admin Console has removed half this capability indirectly. The console does not provide a way to authorize (a user accepting the consent screen) the desired scope the very first time with a service account.
First up, follow the Node.JS Quickstart instructions for Google Drive API to authorize a scope and receive a refresh token.
Go to console.developers.google.com, build a OAuth2.0 Client Id, and download the client_secret.json file.
Create a separate temporary module folder and use NPM to download google api modules
npm install googleapis
npm install google-auth-library
Create a quickstart.js file
Place your client_secret.json file next to quickstart.js
Line 7 in the quickstart.js is the array to define the scopes you intend to allow the application to access. Modify it as you see necessary. It is highly recommended to only provision access for what is intended. See Gmail API Scopes.
RUN node quickstart.js
Open the URL in a browser, authenticate, and copy the code from the browser back into the terminal window. This will download a nodejs-gmail-quickstart.json file which the location will be provided in stdout.
This is the part you are unable to accomplish for a Service Account. This action authorizes the scopes provided in the SCOPES array to the downloaded access_token & refresh token.
NOTE: access_token's have a lifespan of 1 hour. refresh_token's are immortal.
Now you have an authorized refresh_token!
Next is setting up your auth object with 3LO in Nodemailer. I would look more at the bottom examples because not all values are required. My auth looks like this:
const mailbot = nodemailer.createTransport({
host: 'smtp.gmail.com',
port: 587, // TLS (google requires this port for TLS)
secure: false, // Not SSL
requireTLS: true, // Uses STARTTLS command (nodemailer-ism)
auth: {
// **HIGHLY RECOMMEND** ALL values be
// read in from a file not placed directly in code.
// Make sure that file is locked down to only the server daemon
type : 'OAuth2',
user : config.client_email,
scope : "https://www.googleapis.com/auth/gmail.send",
clientId : config.client_id,
clientSecret: secret,
refreshToken: activeToken.refresh_token
// AT RUNTIME, it looks like this:
//type : 'OAuth2',
//user : 'user#gmail.com', // actual user being impersonated
//scope : "", //Optional, but recommend to define for the action intended
//clientId : '888888888998-9xx9x99xx9x99xx9xxxx9xx9xx9x88x8xxx.apps.googleusercontent.com',
//clientSecret: 'XxxxxXXxX0xxxxxxxx0XXxX0',
//refreshToken: '1/XXxXxsss-xxxXXXXXxXxx0XXXxxXXx0x00xxx'
}
});
TIP: Gmail will rewrite the FROM field from any email sent with the authorized user account (user impersonated). If you want to customize this slightly, use the syntax { FROM: '"Display NAME" <user email>' } and it will not overwrite your display name choice since the email matches.
NOTE: nodemailer will make a token request out to https://accounts.google.com/o/oauth2/token with the refresh token to automatically obtain an access_token.
Unfortunately, nodemailer lacks the functionality to save a received token out to a file directly but instead just uses this.emit(). If the server stays active it will not be an issue but as mine is only bursting, it will always incur a delay as a new access_token will be requested every time.
[SECURITY] Hopefully this works for you! It is disappointing to loose the private key encryption a service account with 2LO would bring but at least this Client ID way is very hard to spoof. I was concerned about security but reading more I am okay with this implementation. See Google Identity Platform (Nodemailer uses the HTTP/REST details) and given
[1] Google's OAuth 2.0 endpoint is at
https://accounts.google.com/o/oauth2/v2/auth. This endpoint is
accessible only over HTTPS. Plain HTTP connections are refused.
[5] After the web server receives the authorization code, it can exchange
the authorization code for an access token.
you are using TLS to connect initially for an authorization code, then matching it with your client ID data, and a refresh_token (you must go through the hassle we did above) then you can receive an access_token to actually interact with Google APIs.
As long as you increase your security posture with keeping the OAuth2.0 Client ID (highly random username), secret, and refresh token as separate, secure, and hidden as much as possible, you should be able to sleep soundly. GOOD LUCK!
After visiting the OAuth 2.0 Playground and experimenting with all possible variations of gmail-related sub-scopes, even selecting them altogether...
https://www.googleapis.com/auth/gmail.labels
https://www.googleapis.com/auth/gmail.send
https://www.googleapis.com/auth/gmail.readonly
https://www.googleapis.com/auth/gmail.compose
https://www.googleapis.com/auth/gmail.insert
https://www.googleapis.com/auth/gmail.modify
https://www.googleapis.com/auth/gmail.metadata
https://www.googleapis.com/auth/gmail.settings.basic
https://www.googleapis.com/auth/gmail.settings.sharing
...the error message described in the OP title still persist:
Error: Invalid login: 535-5.7.8 Username and Password not accepted
It seems that NodeMailer is not capable of connecting via the scopes mentioned above. In fact, it explicitly mentions in the "Troubleshooting" section of its OAuth2 SMTP transport docs
The correct OAuth2 scope for Gmail SMTP is https://mail.google.com/, make sure your client has this scope set when requesting permissions for an user
Although this gives access to more than just sending emails, it works!
The only alternative to reach a more fine grained scope solution seems to be to resort to google's own Gmail API, where you can pass scopes when generating the OAuth2 client (which should of course at least include the scopes granted at the time the OAuth consent screen was shown):
oAuth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES,
})
I was able to get service accounts working with Google & nodemailer:
these were the steps:
Log in to console.- https://console.cloud.google.com/
Create a service account under the project.
Click on the new service account, go to permissions and add a member. You will use this member's email address when sending the request.
Create keys for the service account. - keys -> add key. https://console.cloud.google.com/iam-admin/serviceaccounts
Download your key file. You will get something like service-account-name-accountid.json. It will have all the information you need to get the code below running.
Delegate authority to your service account https://developers.google.com/identity/protocols/oauth2/service-account#delegatingauthority. Addhttps://mail.google.com/ as the scope.
Write some code like below:
const nodemailer = require('nodemailer');
const json = require('./service-account-name-accountid.json');
const sendEmail = async (email, subject, text) => {
try {
const transporter = nodemailer.createTransport({
host: 'smtp.gmail.com',
port: 465,
secure: true,
auth: {
type: 'OAuth2',
user: email, //your permissioned service account member e-mail address
serviceClient: json.client_id,
privateKey: json.private_key
}
});
await transporter.verify();
await transporter.sendMail({
from: json.service_email,
to: email, //you can change this to any other e-mail address and it should work!
subject,
text
});
console.log('success!');
return {
status : 200
}
} catch (error) {
console.log(error);
return {
status : 500,
error
}
}
}
sendEmail('your_permissioned_service_account_email_address#some_place.com, 'testing 123', 'woohoo!');
I am playing with an Azure Mobile Apps backend (nodeJS), as discussed here. I have been using the default web setup configuration to develop my mobile app, but now I want to customise the cloud backend functionality, so I have created a local backend with the Azure-Mobile-Apps SDK.
I logged in with my mobile app (using the authorization aspect of the Azure client SDK) and then captured the AuthToken.
I then constructed a Postman HTTP POST request, with these headers:
ZUMO-API-VERSION = 2.0.0
x-zumo-auth = eyJ0eX000000000000000000000000000000.eyJ000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000000000-000000000_00000_00000
NB: my tokenm doesn't actually have all those zeros, it looks like a valid token.
However, the POST request's response is:
{
"name": "JsonWebTokenError",
"message": "invalid signature"
}
I thought this might be because the Auth token was generated by a different Service (the default backend rather than my project running on localhost). So I initialised a Client with localhost and tried to Authenticate with that, but I got:
JS: Error Logging in! Error: Logging in with the selected authentication provider is not enabled
chromium: [INFO:CONSOLE(12)] "Not allowed to load local resource: file:///android_asset/webkit/android-weberror.png", source: data:text/html,chromewebdata (12)
Update:
I have found my WEBSITE_AUTH_SIGNING_KEY from https://myApp.scm.azurewebsites.net/Env.cshtml and added it to my azureMobile.js file, which is in the same directory as my app.js file. It looks like this:
console.log("Test");
module.exports = {
cors: {
origins: ['localhost']
},
data: {
provider: 'mssql',
server: '127.0.0.1',
database: 'mytestdatabase',
user: 'localDemo',
password: 'myPassword'
},
logging: {
level: 'verbose'
},
auth: { secret: 'xzy0000000000000000000000000000000000' },
};
However, I still get the same result. Is there a way of telling whether my azureMobile file is being correctly referenced, or whether something else is wrong?
To validate JWT tokens locally that were created by a hosted service, you need to obtain the signing key that is used. You can obtain this by opening a browser to https://mobile-service-name.scm.azurewebsites.net/Env.cshtml and finding the value for WEBSITE_AUTH_SIGNING_KEY. Take this value and configure your local server by creating (or updating) a file called azureMobile.js in the root of your project with the following content:
module.exports = {
auth: { secret: 'value from WEBSITE_AUTH_SIGNING_KEY' }
};
It's recommended to exclude this file from deployment by adding azureMobile.js to your .gitignore file.
I am trying to generate a Gmail authentication token to use in my NodeJS server for sending email notifications.
Right now I have something like this (I'm not the one who did that) but I need to change the email address and I'm having issues with this:
exports.mail =
{
service: 'Gmail',
auth: {
user: 'gmail_address_here',
pass: '16_characters_token_here'
}
}
I created a few projects in Google Console and tried messing around and I also followed the guide provided by Google but no luck. I also checked this: Generating valid oauth token and secret for gmail imap? as well as https://scotch.io/tutorials/easy-node-authentication-google and other guides and I always end up with a client ID and a client secret as well as the JSON file with the info inside but it doesn't work in my app.
Any ideas?