I'm trying to use nodemailer in my contact form to receive feedback and send them directly to an email. This is the form below.
<form method="post" action="/contact">
<label for="name">Name:</label>
<input type="text" name="name" placeholder="Enter Your Name" required><br>
<label for="email">Email:</label>
<input type="email" name="email" placeholder="Enter Your Email" required><br>
<label for="feedback">Feedback:</label>
<textarea name="feedback" placeholder="Enter Feedback Here"></textarea><br>
<input type="submit" name="sumbit" value="Submit">
</form>
This is what the request in the server side looks like
app.post('/contact',(req,res)=>{
let transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: 'user#gmail.com',
password: 'password'
}
});
var mailOptions = {
from: req.body.name + '<' + req.body.email + '>',
to: 'bantspl#gmail.com',
subject: 'Plbants Feedback',
text: req.body.feedback
};
transporter.sendMail(mailOptions,(err,res)=>{
if(err){
console.log(err);
}
else {
}
});
I'm getting the error Missing credentials for "PLAIN". Any help is appreciated, thank you very much.
You have
auth: {
user: 'user#gmail.com',
password: 'password'
}
But you should write this
auth: {
user: 'user#gmail.com',
pass: 'password'
}
Just rename password to pass.
I was able to solve this problem by using number 3, Set up 3LO authentication, example from the nodemailer documentation (link: https://nodemailer.com/smtp/oauth2/). My code looks like this:
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'
}
});
If you looked at the example in the link that I stated above, you can see there that there is a 'expires' property but in my code i didn't include it and it still works fine.
To get the clientId, clientSecret, refreshToken, and accessToken, I just watched this video https://www.youtube.com/watch?v=JJ44WA_eV8E .
I don't know if this is still helpful to you tho.
Gmail / Google app email service requires OAuth2 for authentication. PLAIN text password will require disabling security features manually on the google account.
To use OAuth2 in Nodemailer, refer: https://nodemailer.com/smtp/oauth2/
Sample code:
var email_smtp = nodemailer.createTransport({
host: "smtp.gmail.com",
auth: {
type: "OAuth2",
user: "youremail#gmail.com",
clientId: "CLIENT_ID_HERE",
clientSecret: "CLIENT_SECRET_HERE",
refreshToken: "REFRESH_TOKEN_HERE"
}
});
And if you still want to use just plain text password, disable secure login on your google account and use as follows:
var email_smtp = nodemailer.createTransport({
host: "smtp.gmail.com",
auth: {
type: "login", // default
user: "youremail#gmail.com",
pass: "PASSWORD_HERE"
}
});
We don't need to lower our Google Account Security for this. This works for me on localhost and live server. Versions: node 12.18.4, nodemailer ^6.4.11.
STEP 1:
Follow setting up your Google Api Access in this video AND IGNORE his code (it didn't work for me): https://www.youtube.com/watch?v=JJ44WA_eV8E
STEP 2:
Try this code in your main app file after you install nodemailer and dotenv via npm i nodemailer dotenv:
require('dotenv').config(); //import and config dotenv to use .env file for secrets
const nodemailer = require('nodemailer');
function sendMessage() {
try {
// mail options
const mailOptions = {
from: "MySite#mysite.com",
to: "my_gmail#gmail.com",
subject: "Hey there!",
text: "Whoa! It freakin works now."
};
// here we actually send it
transporter.sendMail(mailOptions, function(err, info) {
if (err) {
console.log("Error sending message: " + err);
} else {
// no errors, it worked
console.log("Message sent succesfully.");
}
});
} catch (error) {
console.log("Other error sending message: " + error);
}
}
// thats the key part, without all these it didn't work for me
let transporter = nodemailer.createTransport({
host: 'smtp.gmail.com',
port: 465,
secure: true,
service: 'gmail',
auth: {
type: "OAUTH2",
user: process.env.GMAIL_USERNAME, //set these in your .env file
clientId: process.env.OAUTH_CLIENT_ID,
clientSecret: process.env.OAUTH_CLIENT_SECRET,
refreshToken: process.env.OAUTH_REFRESH_TOKEN,
accessToken: process.env.OAUTH_ACCESS_TOKEN,
expires: 3599
}
});
// invoke sending function
sendMessage();
Your .env file for the above code should look similar to this:
GMAIL_USERNAME=your_mail#gmail.com
GMAIL_PASSWORD=lakjrfnk;wrh2poir2039r
OAUTH_CLIENT_ID=vfo9u2o435uk2jjfvlfdkpg284u3.apps.googleusercontent.com
OAUTH_CLIENT_SECRET=og029503irgier0oifwori
OAUTH_REFRESH_TOKEN=2093402i3jflj;geijgp039485puihsg[-9a[3;wjenjk,ucv[3485p0o485uyr;ifasjsdo283wefwf345w]fw2984329oshfsh
OAUTH_ACCESS_TOKEN=owiejfw84u92873598yiuhvsldiis9er0235983isudhfdosudv3k798qlk3j4094too283982fs
For me the issue was that I wasn't accessing the .env file variables properly (I assume you're storing your email and password in a .env file too). I had to add this to the top of the file:
const dotenv = require('dotenv');
dotenv.config();
Once I did that I could fill out the "auth" portion of the credentials like this:
auth: {
user: process.env.EMAIL_USERNAME,
pass: process.env.EMAIL_PASSWORD
}
Of course you need to replace EMAIL_USERNAME and EMAIL_PASSWORD with whatever you called those variables in your .env file.
For me it happened because I forgot to npm install dotenv and require('dotenv').config();
I came across this issue when deploying my app to Heroku. I was sending emails fine locally, but when I pushed a new build to Heroku, I got the dreaded Missing credentials for "PLAIN" error. My issue was that I hadn't separately setup the .env variables in Heroku.
If you go to your dashboard in Heroku, you can manually set up the config variables and that solved the problem for me.
Or you can do via Heroku CLI - good tutorial here
I was running ts-node in a folder that didn't have the .env file.
So my process.env.GMAIL_EMAIL and process.env.GMAIL_PASS weren't defined.
When I ran it in the directory with the .env, it worked
If you are going to use the basic authentication (your current configuration) you will need to activate less secure app access from the following link to your google account which was stated in node mailer site here.
Also for more secure way, I recommend to take a look on the two following links:
Sending Emails Securely Using Node.js, Nodemailer, SMTP, Gmail, and OAuth2 This will explain both simple/Basic and the secure methods to you.
Using OAuth2 in nodemailer official docs, which contain a more simplified version of the secure method (but you will need to follow the steps mentioned in the first link till you get the client_ID,client_Secret, Refresh_Token, and Access Token before proceeding with it).
Note: if you are going to use the secure way from the first link steps you should modify the auth attributes to add the type option like the following:
auth: {
type: 'OAuth2',
user: process.env.EMAIL,
accessToken,
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
refresh_token: process.env.REFRESH_TOKEN
}
because without stating explicitly that you are using OAuth2 method it will print the same error to you (based on my recent trials using the written code in that link)
This happened to me when I tried to send mail to MailHog, a development local mailserver which runs on port 1025, and uses no username/password by default.
My code was providing empty username/password, but in fact you should provide no credentials at all.
This does not work (results in 'Missing credentials for "PLAIN"'):
const transport = require('nodemailer').createTransport({
host: mailhog,
port: 1025,
secure: false,
auth: {
user: '',
pass: '',
},
});
But this works:
const transport = require('nodemailer').createTransport({
host: mailhog,
port: 1025,
secure: false,
});
For me it was because I forgot to add my Gmail password to my .env file.
Google disabled less secure apps, to resolve the issue one need to setup "Login with app password" and to allow the app password "setup two factor authentication"
when 2-Step-Verification is on and one get a "password incorrect" error, sign in to your account and go to security, try to use an App Password.
transport: {
host: 'smtp.gmail.com',
port: 465,
secure: true,
auth: {
user: 'contact#gmail.com',
pass: 'app password',
},
},
If you are expecting this (ERROR Send Error: Missing credentials for "PLAIN") error, it's probably because you are testing on localhost.
For example, if you are using Heroku, simply try to deploy your project and test it in production.
Regards.
I encountered the same problem, and that was caused by Google automatically blocked your logging-in form untruthful third-party software. Then you can just turn this feature off in your Google account management.
Regards,
Related
I am trying to send email from my Nodejs, Express API. But it is showing
message: "connect ECONNREFUSED 74.125.137.109:465"
name: "Error
when I am using configuration like
service: 'Gmail'
or 'smtp.gmail.com', or host: 'mail.gmail.com'
port: 465,
secure: true, // true for 465, false for other ports
auth: {
user: process.env.EMAIL_USER, // generated ethereal user
pass: process.env.EMAIL_PASS, // generated ethereal password
}
.
Can someone please explain why this is happening and how I can resolve it? TIA
I am trying to setup nodemail, but run into a strange issue where I cannot wrap my head around. Whenever I fill in the credentials as a string, everything works fine (but of course I don't want this). If I start using env variables, then I get the following error message:
UnhandledPromiseRejectionWarning: Error: Missing credentials for "PLAIN"
It's using a gmail account to send an email. The account has the bypass for unsafe accounts enabled. When I add a console.log() statement, the password is clearly visible. Does anyone have any idea why this is happening and how this can be resolved. Below you'll find a code snippet which sends the email.
import nodemailer from 'nodemailer';
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: 'some-account#gmail.com',
pass: process.env.PWD,
},
});
export const sendUserCreateEmail = (to, firstName) => {
const mailOptions = {
from: 'someone',
to: to,
subject: 'It works',
html: `
<p>It works!</p>
`,
};
return transporter.sendMail(mailOptions);
};
You need to install dotenv
npm install dotenv
then import it at your file
const dotenv = require('dotenv');
dotenv.config();
then it should works
I figured it out, although it is more of a work around. Apparently, the variables which where set by a dotenv file are not working there. I modified my npm command to include the variables and then it works as expected.
PWD=password node index.js
I can set these without committing them locally, so I can live with this solution for the moment. Still not sure why the dotenv variables are not accepted though.
I have a web app that is provided by an express.js server (web app server). The web app server handles the user login via passport.js and the passport-azure-ad npm package. I am using the OIDCStrategy for this.
I am also hosting a REST api via another server (rest backend). I want to secure this backend via passport.js and the passport-azure-ad npm package using the BearerStrategy. For that I want to define a scope in the web app server passport configuration so that the access_token of the web app server can be passed via a cookie to my web app and from there be used to access the rest backend.
My issue: with my current configuration, I receive 401 access denied while trying to access my backend api with the access_token. access_token invalid is the error message: {"name":"AzureAD: Bearer Strategy", "msg":"authentication failed due to: error: invalid_token","v":0}
I think I should be redirected to a permission page while signing in but it does not. So I guess my access token is actually not valid.
Web app server passport configuration:
passport.use(
new OIDCStrategy(
{
clientID: credsAzureAD.clientID,
identityMetadata: credsAzureAD.identityMetadata, // here is use the web app tenant id
clientSecret: credsAzureAD.clientSecret,
callbackURL: credsAzureAD.returnURL,
redirectUrl: credsAzureAD.returnURL,
skipUserProfile: true,
responseType: 'code',
responseMode: 'query',
scope: 'api://test/Write.Contributor',
useCookieInsteadOfSession: false,
passReqToCallback: false
},
(issuer, sub, profile, accessToken, refreshToken, done) => {
user.accessToken = accessToken;
return done(null, user);
}
)
);
I try to use the scope where api://test is my REST API application ID uri and /Write.Contributor is the scope that I defined in azure active directory.
My REST backend server passport configuration:
const options = {
identityMetadata: azureAD.identityMetadata, // here I use the backend server tenant id
clientID: azureAD.clientID,
issuer: azureAD.issuer, // here I use the backend server tenant id
passReqToCallback: false,
};
const bearerStrategy = new BearerStrategy(options, function(token, done) {
done(null, {}, token);
});
I have created my backend server in azure active directory via application registration and created the named scope and application id above. I also have whitelisted my web app clientId there as a authorized client applications.
I try to call following route and receive 401:
app.get(
'/testtest',
cors(),
passport.authenticate('oauth-bearer', { session: false }),
function(req, res) {
var claims = req.authInfo;
console.log('User info: ', req.user);
console.log('Validated claims: ', claims);
res.status(200).json({ name: claims['name'] });
}
);
This is my rest call from my vue web app:
let headers = {
'Content-Type': 'application/json',
Authorization: `Bearer ${this.user.accessToken}`,
'cache-control': 'no-cache'
};
const apiClient = axios.create({
baseURL,
headers
});
apiClient.get('/testtest').then( resp => console.log( resp));
I am taken the access token as is no decoding/encoding.
Any support would be very much appreciated. Thank you!
As #Jim Xu suggested in the comments, adding the application scope of the REST api to the client application via Azure Portal helped solving the issue. But I was also using the wrong token.
instead of using the accessToken from the verify function parameter list, I now use the param from the verify function parameter list.
(iss, sub, profile, access_token, refresh_token, params, done) => {
// access_token did not work
// id_token can be used as an accessToken
user.accessToken = params.id_token;
...
}
When attempting to connect to my local SQL Server instance I am receiving an error stating Authentication failed for login. However I am able to login directly to the server in SQL using the provided login.
Here is my code that is attempting to connect to the server.
var Sequelize = require('sequelize');
const sequelize = new Sequelize('GraphQLTests', 'gql', 'Password1', {
dialect: 'mssql',
host:'localhost'
});
sequelize
.authenticate()
.then(() => {
console.log('Connection has been established successfully.');
})
.catch(err => {
console.error('Unable to connect to the database:', err);
});
I have printed to the console in the Sequelize code to verify that the correct credentials are getting passed but still receive this error.
name: 'SequelizeAccessDeniedError',
parent:
{ ConnectionError: Login failed for user ''.}
Please let me know if there is any other info I can provide.
try this
const sequelize = new Sequelize('DB Name', 'Username', 'Password', {
host: 'Host',
dialect: 'mssql',
dialectOptions: {
options: {
encrypt: true,
}
}
});
sequelize.authenticate().then((err) => {
console.log('Connection successful', err);
})
.catch((err) => {
console.log('Unable to connect to database', err);
});
Try this link as reference Connecting to MSSQL server with Sequelize
This is what worked for me, where I have put the server details in an YAML file. (this way, you can switch servers on the fly).
this is the sequelize code
//get the configure from the YAML file
const YAML = await fs.readFile(process.env.SEQUELIZE_CONNECT,'utf8');
//load the database parameters into our system
const params = jsyaml.safeLoad(YAML, 'utf8');
//initiate our database server details to connect to our underlying database system
//as described in the YAML file.
sequlz = new Sequelize(params.dbname, params.username, params.password, params.params);
Here is how my YAML file looks. (I have left my code comments as it is)
#this should work to whatever you are using anywhere.
#as per the YAML file title, I am using a MS SQL server hosted on Azure.
# you can edit values. as per your requirement.
# check the dialect help file of your server on the sequelize documentation
# https://sequelize.org/v5/file/lib/dialects/mssql/connection-manager.js.html
#change the values as per your server.
dbname: databasenamehere
username: usernamehere
password: passwordhere
params:
host: servernamehere.database.windows.net
dialect: mssql
dialectOptions:
{
options: {
encrypt: true,
requestTimeout: 10000
}
}
So, that worked for me. (I have remixed answers from above post, and also from the textbook and multiple online resources I was referring).
Note : The server is running on Azure with the Firewall IP address set to ALL IP address.
I have the following code in a file called knexfile.js
module.exports = {
development: {
client: 'mysql',
connection: {
database: 'myDatabase',
timezone: 'Z',
user: 'root',
password: 'myPassword',
host: '127.0.0.1'
},
pool: {
min: 2,
max: 10
},
migrations: {
tableName: 'myMigrationTable'
}
}
};
myPassword from the code above is in plaintext. On my production server, I definitely don't want my password in plaintext in my code that my application uses to authenticate with my database. I also wouldn't want it laying around in a file in plaintext on my server.
Is there a way in knex or node to easily handle securely logging into my database? Should I just simply encrypt my password, leave it in a file on my server, and decrypt it using my webapp when it's going to log in?
Best practice would be using environment variable.
knex = require('knex')({
client: 'mysql',
connection: process.env.DATABASE_URL
})