Error: Invalid login: Application-specific password required - node.js

i want send Welcome notification when user sign in using Cloud-Function with firebase auth
so i m using nodejs CLI and run the code
my index.js file
'use strict';
const functions = require('firebase-functions');
const nodemailer = require('nodemailer');
// Configure the email transport using the default SMTP transport and a GMail account.
// For Gmail, enable these:
// 1. https://www.google.com/settings/security/lesssecureapps
// 2. https://accounts.google.com/DisplayUnlockCaptcha
// For other types of transports such as Sendgrid see https://nodemailer.com/transports/
// TODO: Configure the `gmail.email` and `gmail.password` Google Cloud environment variables.
const gmailEmail = functions.config().gmail.email;
const gmailPassword = functions.config().gmail.password;
const mailTransport = nodemailer.createTransport({
service: 'gmail',
auth: {
user: gmailEmail,
pass: gmailPassword,
},
});
// Your company name to include in the emails
// TODO: Change this to your app or company name to customize the email sent.
const APP_NAME = 'Cloud Storage for Firebase quickstart';
// [START sendWelcomeEmail]
/**
* Sends a welcome email to new user.
*/
// [START onCreateTrigger]
exports.sendWelcomeEmail = functions.auth.user().onCreate((user) => {
// [END onCreateTrigger]
// [START eventAttributes]
const email = user.email; // The email of the user.
const displayName = user.displayName; // The display name of the user.
// [END eventAttributes]
return sendWelcomeEmail(email, displayName);
});
// [END sendWelcomeEmail]
// [START sendByeEmail]
/**
* Send an account deleted email confirmation to users who delete their accounts.
*/
// [START onDeleteTrigger]
exports.sendByeEmail = functions.auth.user().onDelete((user) => {
// [END onDeleteTrigger]
const email = user.email;
const displayName = user.displayName;
return sendGoodbyeEmail(email, displayName);
});
// [END sendByeEmail]
// Sends a welcome email to the given user.
async function sendWelcomeEmail(email, displayName) {
const mailOptions = {
from: `${APP_NAME} <noreply#firebase.com>`,
to: email,
};
// The user subscribed to the newsletter.
mailOptions.subject = `Welcome to ${APP_NAME}!`;
mailOptions.text = `Hey ${displayName || ''}! Welcome to ${APP_NAME}. I hope you will enjoy our service.`;
await mailTransport.sendMail(mailOptions);
console.log('New welcome email sent to:', email);
return null;
}
// Sends a goodbye email to the given user.
async function sendGoodbyeEmail(email, displayName) {
const mailOptions = {
from: `${APP_NAME} <noreply#firebase.com>`,
to: email,
};
// The user unsubscribed to the newsletter.
mailOptions.subject = `Bye!`;
mailOptions.text = `Hey ${displayName || ''}!, We confirm that we have deleted your ${APP_NAME} account.`;
await mailTransport.sendMail(mailOptions);
console.log('Account deletion confirmation email sent to:', email);
return null;
}
i refer this code https://github.com/firebase/functions-samples/blob/master/quickstarts/email-users/functions/index.js
but after i ran the code i got error
Error: Invalid login: 534-5.7.9 Application-specific password required. Learn more at
534 5.7.9 https://support.google.com/mail/?p=InvalidSecondFactor i82sm13686303ilf.32 - gsmtp
at SMTPConnection._formatError (/srv/node_modules/nodemailer/lib/smtp-connection/index.js:784:19)
at SMTPConnection._actionAUTHComplete (/srv/node_modules/nodemailer/lib/smtp-connection/index.js:1523:34)
at SMTPConnection._responseActions.push.str (/srv/node_modules/nodemailer/lib/smtp-connection/index.js:550:26)
at SMTPConnection._processResponse (/srv/node_modules/nodemailer/lib/smtp-connection/index.js:942:20)
at SMTPConnection._onData (/srv/node_modules/nodemailer/lib/smtp-connection/index.js:749:14)
at TLSSocket.SMTPConnection._onSocketData.chunk (/srv/node_modules/nodemailer/lib/smtp-connection/index.js:195:44)
at emitOne (events.js:116:13)
at TLSSocket.emit (events.js:211:7)
at addChunk (_stream_readable.js:263:12)
at readableAddChunk (_stream_readable.js:250:11)
i also Allow less secure apps From your Google Account
and also done 2 step-verification
but still got an error
I read all "Similar Questions" here in stackoverflow and I don't know if I need anything else or if I'm doing anything bad

If you have enabled 2-factor authentication on your Google account you can't use your regular password to access Gmail programmatically. You need to generate an app-specific password and use that in place of your actual password.
Steps:
Log in to your Google account
Go to My Account > Sign-in & Security > App Passwords
(Sign in again to confirm it's you)
Scroll down to Select App (in the Password & sign-in method box) and choose Other (custom name)
Give this app password a name, e.g. "nodemailer"
Choose Generate
Copy the long generated password and paste it into your Node.js script instead of your actual Gmail password.

You need to use an application password for this purpose. This issue will arise when 2 Step verification is turned-on for your Gmail account. You can bypass it by using app password. here is how to generate an app password.
Select your profile icon in the upper-right corner of Gmail, then select Manage Google Account.
Select Security in the left sidebar.
Select App passwords under the Signing into Google section. You're then asked to confirm your Gmail login credentials.
Under Select app, choose Mail or Other (Custom name), then select a device.
Select Generate.
Your password appears in a new window. Follow the on-screen instructions to complete the process, then select Done.
google doc : https://support.google.com/mail/answer/185833?hl=en#zippy=%2Cwhy-you-may-need-an-app-password

Generate a password from https://security.google.com/settings/security/apppasswords and use that password instead.

in Gmail, Enable 2-Step-Verification.
Then create App password & use it on SMTP

UPD: You can't do this on localhost. I try. Because it's not a secure connection.

you may still get error if you are doing this on localhost,Because it's not a secure connection(http not https).here is how to solve
for that remove secure: true in the configuration when you create the transporter or comment out it or set it equal to false.

Related

Unable to use SendInBlue with Gmail and Nodemailer Properly

Before I go into this, I know two very common issues with Gmail with Nodemailer is that Allow Less Secure Apps is disabled by default, and I know two factor authentication can provide similar issues. Neither of these is the case.
I am attempting to make a system wherein many user accounts can be created en masse. When they are created, each account will receive an email with a greeting and their password. The issue was that gmail does not like spam, so the emails wouldn't send. I tried adding a 1 second wait period between each mutation thus between each email, but that didn't work either.
I now have a SendInBlue Account, using that same gmail. My code looks like this now...
////////////////////////////////
/// ///
/// EMAIL STUFF ///
/// ///
////////////////////////////////
// Creates the Transporter
const transporter = nodemailer.createTransport({
service: "SendinBlue",
auth: {
user: `${process.env.EMAIL_ADDRESS}`,
pass: `${process.env.EMAIL_PASSWORD}`
}
})
// Creates the Mail Object
const mailOptions = {
from: `${process.env.EMAIL_ADDRESS}`,
to: `${actualEmail}`,
subject: `Thank you for joining the TOM Team!`,
text: `We have recieved your Account Signup and are please to welcome you to the TOM Experience!`
}
// Sends the mail
transporter.sendMail(mailOptions, (error, response) => {
if (error){
throw new Error('Something went wrong, please try again \n' + error)
}
})
Wherein the only thing in the code above that is different than when it was sending (or at least it was trying to send before it got hit with the spam block) is the line service: "SendinBlue", was changed from service: "Gmail",
Is this an issue with SendInBlue, or does this look more like a Gmail / Nodemailer issue? Could anyone assist

ServerError: AADSTS50058: A silent sign-in request was sent but none of the currently signed in user(s) match the requested login hint"

SSO fails "ServerError: AADSTS50058: A silent sign-in request was sent but none of the currently signed in user(s) match the requested login hint"
when I use same account for both work and personal azure account.
I have 2 AAD accounts (one is with my work account and the other one is personal account but both attached with same email and both are using same credentials). When I use msal.js library for single sign on application. It takes me to my work account where it asks me to validate the credentials (using standard pop up dialog) by giving full email address and does not authenticate properly even if give right credentials. As I need to login using my personal account
I expect this should validate using my ad alias#company.com credentials. I tried with different account option in the dialog, but it fails and shows up same full email account.
How can I use my adalias#company.com as a default user id?
Here are the piece of the code I am trying to use.
var msalConfig = {
auth: {
clientId: 'xxxxxxxxxx', // This is your client ID
authority: "https://login.microsoftonline.com/{tenantid}" // This is tenant info
},
cache: {
cacheLocation: "localStorage",
storeAuthStateInCookie: true
}
};
var graphConfig = {
graphMeEndpoint: "https://graph.microsoft.com/v1.0/me"
};
var requestObj = {scopes: ["user.read", "email"]};
// Is there a way to change here to get the required user id?
var myMSALObj = new Msal.UserAgentApplication(msalConfig);
// Register Callbacks for redirect flow
myMSALObj.handleRedirectCallbacks(acquireTokenRedirectCallBack,
acquireTokenErrorRedirectCallBack);
myMSALObj.handleRedirectCallback(authRedirectCallBack);
function signIn() {
myMSALObj.loginRedirect(requestObj).then(function (loginResponse) {
// Successful login
acquireTokenPopupAndCallMSGraph();
}).catch(function (error) {
// Please check the console for errors
console.log(error);
});
}
Here is the error message I get:
ServerError: AADSTS50058: A silent sign-in request was sent but none of the
currently signed in user(s) match the requested login hint
The expected result is seamless login to other application.
If you want to provide a login_hint to indicate the user you are trying to authenticate try:
var requestObj = {scopes: ["user.read", "email"], loginHint: "adalias#company.com"};
Reference https://github.com/AzureAD/microsoft-authentication-library-for-js/wiki/FAQs#q10-how-to-pass-custom-state-parameter-value-in-msaljs-authentication-request-for-example-when-you-want-to-pass-the-page-the-user-is-on-or-custom-info-to-your-redirect-uri

Create and Return Firebase Email Verification Link inside a Firebase Cloud Function

I have a firebase cloud function trigger an send a Welcome email when someone signs up. I would like to include my email verification link in that same email to reduce the amount of emails users get upon signup and improve the onboarding experience (rather than sending two separate emails).
exports.sendWelcomeEmail = functions.auth.user().onCreate(event => {
// Get user that signed up
const user = event.data; // The Firebase user.
// get the email of the user that signed up
const email = user.email; // The email of the user.
// Create email verification link
var emailVerificationLink = user.createEmailVerificationLink() // NEED HELP HERE: ideally, I would like to create/call a function to create an email verification link for the user here
// send email
mailgun.messages().send({
from: 'support#example.com',
to: email,
subject: 'Welcome & Get Started',
text: 'Welcome! Here are some resources to help you get started, but first verify your email: ' + emailVerificationLink + '!',
html: // some nice formatted version of the text above
}, function (error, response) {
console.log("Email response");
console.log(response);
console.log("Email error");
console.log(error);
});
})
I have carefully looked through the documentation on custom email handlers, but it doesn't seem like they return the email verification link, so I do not see how to use that approach for my purposes here (although I hope I'm wrong).
Is there a way to create the email verification link inside a Firebase Cloud Function in such a way that I could then use resulting link as I please (like in my Welcome email)?
There is no public API to get the OOB verification code, or the link that contains that code.
But you can implement this yourself with a few steps:
Generate your own verification code, that you store somewhere securely (e.g. in a protected section of your Firebase Database).
Embed that code in your message in a link.
Create a Cloud Function at that link.
Handle the request, check the verification code in the database
Set emailVerified to true.
This isn't all that much different from what Firebase Authentication does when you call sendEmailVerification().

Username and Password not accepted when using nodemailer?

This is my settingController:
var sendSmtpMail = function (req,res) {
var transport = nodemailer.createTransport({
service:'gmail',
auth: {
user: "asdfqweerrccb#limitlesscircle.com",
pass: "qwerr#wee"
}
});
var mailOptions = {
from: "transactions#limitlesscircle.com",
to:'umaraja1124#gmail.com',
subject: req.body.subject+"nodejs working ?",
text: "Hello world ?",
}
transport.sendMail(mailOptions, function(error, response){
if(error){
res.send("Email could not sent due to error: "+error);
console.log('Error');
}else{
res.send("Email has been sent successfully");
console.log('mail sent');
}
});
in postman I got the error like that:
Email could not sent due to error:
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 g7sm64435626pfj.29 - gsmtp
Update (2022-05-02)
As mentioned in the comments and directly quoted from Google:
On May 30 2022, you may lose access to apps that are using less secure
sign-in technology
So the bottom code will probably stop working with Gmail. The solution is to enable 2-Step Verification and generate Application password, then you can use the generated password to send emails using nodemailer.To do so you need to do the following:
Go to your Google account at https://myaccount.google.com/
Go to Security
In "Signing in to Google" section choose 2-Step Verification - here you have to verify yourself, in my case it was with phone number and a confirmation code send as text message. After that you will be able to enabled 2-Step Verification
Back to Security in "Signing in to Google" section choose App passwords
From the Select app drop down choose Other (Custom name) and put a name e.g. nodemailer
A modal dialog will appear with the password. Get that password and use it in your code.
If there is still a problem, try clearing captcha by visiting https://accounts.google.com/DisplayUnlockCaptcha from your Google account.
Sample usege
const nodemailer = require('nodemailer');
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: 'YOUR-USERNAME',
pass: 'THE-GENERATED-APP-PASSWORD'
}
});
send();
async function send() {
const result = await transporter.sendMail({
from: 'YOUR-USERNAME',
to: 'RECEIVERS',
subject: 'Hello World',
text: 'Hello World'
});
console.log(JSON.stringify(result, null, 4));
}
Old Answer (before 2022-05-02)
I think that first you need to Allow less secure apps to access account setting in your Google account - by default this settings is off and you simply turn it on. Also you need to make sure that 2 factor authentication for the account is disabled. You can check how to disable it here.
Then I use the following script to send emails from a gmail account, also tested with yahoo and hotmail accounts.
const nodemailer = require('nodemailer');
let transporter = nodemailer.createTransport({
host: 'smtp.gmail.com',
port: 587,
secure: false,
requireTLS: true,
auth: {
user: 'your.gmail.account#gmail.com',
pass: 'your.password'
}
});
let mailOptions = {
from: 'your.gmail.account#gmail.com',
to: 'receivers.email#domain.example',
subject: 'Test',
text: 'Hello World!'
};
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
return console.log(error.message);
}
console.log('success');
});
If you put the previous code in send-email.js for example, open terminal and write:
node send-email
You should see in the console - success, if the email was send successfully or the error message returned by nodemailer
Don't forget to first do the setting - Allow less secure apps to access account.
I hope this code will be useful for you. Good Luck!
If you have enabled 2-factor authentication on your Google account you can't use your regular password to access Gmail programmatically. You need to generate an app-specific password and use that in place of your actual password.
Steps:
Log in to your Google account
Go to My Account > Sign-in & Security > App Passwords
(Sign in again to confirm it's you)
Scroll down to Select App (in the Password & sign-in method box)
and choose Other (custom name)
Give this app password a name, e.g. "nodemailer"
Choose Generate
Copy the long generated password and paste it into your Node.js script instead of your actual Gmail password. (You don't need the spaces.)
Your script will now look like this:
var transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: 'YOUR-GMAIL-USERNAME#gmail.com',
pass: 'YOUR-GENERATED-APP-PASSWORD'
}
});
I hope this helps someone.
https://myaccount.google.com/lesssecureapps if it is OFF, turn it ON to enable lesssecureapps.
(i had same issue turn it on resolve my issue)
May 2022, google has remove this below the link of option ( https://myaccount.google.com/lesssecureapps if it is OFF, turn it ON to enable lesssecureapps. )
because it's risky to connect third party in google..
so, gmail has provide app password. where you can use into app(application)
> go to google > my account > security > 2-two steps verification(just verified your number by otp or call) >
> Scroll down to Select App (in the Password & sign-in method box) and choose Other (custom name)
> Give this app password a name, e.g. "nodemailer"
> Copy Generate password and paste it into your Node.js script instead of your actual Gmail password. (You don't need the spaces.)
once you copied app p/w, just regsiter for smtp on https://www.smtper.net/ (tick all option and don't left any field to fill up )
then click on submit (check gmail acc)
nodemailer code
const nodemailer = require('nodemailer');
const smtpTransport = require('nodemailer-smtp-transport');
var transporter = nodemailer.createTransport(smtpTransport({
service: 'gmail',
host: 'smtp.gmail.com',
port: 587,
secure: false,
auth: {
user: 'your.gmail.account#gmail.com',
pass: 'app password'
}
}));
let mailOptions = {
from: 'your.gmail.account#gmail.com',
to: 'receivers.email#domain.example',
subject: 'Test',
text: 'Hello World!'
};
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
return console.log(error.message);
}
console.log('success');
});
for more better understanding follow this 2links are given in below
offical smtp : https://nodemailer.com/smtp/
setup free google smtp server : https://www.youtube.com/watch?v=ql5Dex4m40w
Make sure you are using the right port: ie port: 587 as of now.
Visit this link to allow Less secure App access: https://myaccount.google.com/lesssecureapps
Enable the recaptcha at this link: https://accounts.google.com/b/0/DisplayUnlockCaptcha
Wait for a few minutes and try to send an email.
Alternatively use sendgrid. It has a free version.
If you are not seeing the emails in your inbox, check the Spam folder.
I encountered the same problem, i solved it as follows:
GOTO https://admin.google.com and Login with the main account you used for setting up the business account and create users. Remember to use the main Email ID.
Click on the Security Icon and you'll be taken to this page where you'll see Less secure apps section, click on it.
Now You'll see this, allow users or give permission here.
And you're not done yet, Not Go to the below link:
https://myaccount.google.com/u/1/lesssecureapps
Now you'll see the switch. Enable it and try it'll definitely work.
Peace :)
You have to allow Less-Secure-Apps to access account settings in your google account - by default, this setting is off and you can simply turn it on.
Image example
There are some account settings that are necessary for you to send through it using SMTP.
If you have two-step verification enabled on the account, you will need to use an application specific password (created in the Gmail account) in the device settings: Signing in using application-specific passwords
If not, please try this: sign into the Gmail account using a web browser at https://mail.google.com, then go to Settings > Accounts and Import > Other Google Account settings. Under Security, scroll down and enable access for less secure apps. This setting is required to enable SMTP, POP or IMAP access.
If there is still a problem, try clearing Captcha: visit https://accounts.google.com/DisplayUnlockCaptcha and sign in with the Gmail username and password. If necessary (it's usually not), enter the letters in the distorted picture then press Continue. This will allow ten minutes for the device to register as an approved connection. Note that you must use the account you are trying to add to the device - if the browser is already signed into another account, you must sign out first. Also, you must trigger the device to make a connection within ten minutes of pressing Continue.
Explanation from Google Support team
You are using gmail service so your mail must be in gmail:
var transport = nodemailer.createTransport({
service:'gmail',
auth: {
user: "asdfqweerrccb#gmail.com",
pass: "qwerr#wee"
}
});
var mailOptions = {
from: "transactions#gmail.com",
to:'umaraja1124#gmail.com',
subject: req.body.subject+"nodejs working ?",
text: "Hello world ?",
}
transport.sendMail(mailOptions, function(error, response){
if(error){
res.send("Email could not sent due to error: "+error);
console.log('Error');
}else{
res.send("Email has been sent successfully");
console.log('mail sent');
}
});

Cloud Function for Firebase: Email Duplicates

Im trying to implement some code that sends an email to anyone who would like to sign up to my newsletter. The code is actually working, but it sends multiple duplicates. Im using Firebase' samplecode, like this.
I think the problem is that it listensens for every change on the {uid} and I'm setting 4 values. If I manually change anything at the database from the dashboard, it triggers the event and sends a new mail. My code:
'use strict';
const functions = require('firebase-functions');
const nodemailer = require('nodemailer');
// Configure the email transport using the default SMTP transport and a GMail account.
// For other types of transports such as Sendgrid see https://nodemailer.com/transports/
// TODO: Configure the `gmail.email` and `gmail.password` Google Cloud environment variables.
const gmailEmail = encodeURIComponent(functions.config().gmail.email);
const gmailPassword = encodeURIComponent(functions.config().gmail.password);
const mailTransport = nodemailer.createTransport(
`smtps://${gmailEmail}:${gmailPassword}#smtp.gmail.com`);
// Sends an email confirmation when a user changes his mailing list subscription.
exports.sendEmailConfirmation = functions.database.ref('/mailingList/{uid}').onWrite(event => {
const snapshot = event.data;
const val = snapshot.val();
if (!snapshot.changed('subscribed')) {
return;
}
const mailOptions = {
from: '"Spammy Corp." <noreply#firebase.com>',
to: val.email
};
// The user just subscribed to our newsletter.
if (val.subscribed == true) {
mailOptions.subject = 'Thanks and Welcome!';
mailOptions.text = 'Thanks you for subscribing to our newsletter. You will receive our next weekly newsletter.';
return mailTransport.sendMail(mailOptions).then(() => {
console.log('New subscription confirmation email sent to:', val.email);
});
}
});
A database trigger will run for each change made to the path it's monitoring, and you need to plan for that. In your function, you need a way to figure out if the email has already been sent. The typical solution is to write a boolean or some other flag value back into the node that triggered the change, then check for that value every time and return early if it's set.

Resources