I am using aws-sdk using Node to send AWS SES emails and I was able to successfully send emails using AWS CLI. However, from my Node script, verification for my email fails for some reason.
Below is the code:
const aws = require('aws-sdk')
const ses = new aws.SES()
const message = {
Destination: {
ToAddresses: ['example#example.com']
},
Message: {
Body: {
Text: {
Charset: 'UTF-8',
Data: 'Test body'
}
},
Subject: {
Charset: 'UTF-8',
Data: 'Test subject'
}
},
Source: 'example#example.com'
}
ses.sendEmail(message, function (err, data) {
if (err) console.log(err);
else console.log(data);
});
Below is the error:
message: 'Email address is not verified. The following identities failed the check in region US-EAST-1: example#example.com',
code: 'MessageRejected',
time: 2017-12-15T15:37:26.312Z,
requestId: 'random-id',
statusCode: 400,
retryable: false,
retryDelay: 15.030260565173382
Please help! Thank you!
Per AWS troubleshooting documentation:
Email address is not verified. The following identities failed the check in region (region): (identity1), (identity2), (identity3) — You are trying to send email from an email address or domain that you have not verified with Amazon SES. This error could apply to the "From", "Source", "Sender", or "Return-Path" address.
If your account is still in the sandbox, you also must verify every recipient email address except for the recipients provided by the Amazon SES mailbox simulator. If Amazon SES is not able to show all of the failed identities, the error message ends with an ellipsis.
Note: Amazon SES has endpoints in multiple AWS regions, and email address verification status is separate for each AWS region. You must complete the verification process for each sender in the AWS region(s) you want to use.
I strongly suspect that your application's configuration does not 100% match the configuration you used to successfully send your test email via the CLI.
Check the following configuration:
The 'Source' address must be an SES verified sender in the us-east-1 region. Check that the source address that you expect to send email from is a verified sender in each region that you intend to send email from.
If SES sandbox mode is enabled, email recipients (the 'ToAddresses' value) must also be SES verified senders in the us-east-1 region. See 'Moving Out of the Amazon SES Sandbox' for instructions on how to remove this restriction.
Make sure all clients you're testing with are being tested in the same region, since configuration needs to be distinct per-region. The error message mentioned the application attempted to hit SES in us-east-1, so explicitly perform your CLI test in the us-east-1 region again by using the --region option. It is possible that the initial CLI test was flawed if the CLI default region was used, and that region happened to not be us-east-1.
If all of the above looks correct, carefully review your node application. Make sure the SES client is configured for the region you expect to use, and that the client is correctly writing the emails that you expect to the SES request.
Further Reading
AWS Documentation - Verifying Email Addresses and Domains in Amazon SES
AWS Documentation - Sending Email Using Amazon SES
Here is my code using node.js with express, ejs and npm package node-ses
The solution is mentioned above, but is easy to miss. It's the third part, after key and secret, the callback to amazon - the url needs to be there and it needs to be customised for your region, in my case eu-west-1, the other two choices are us-east-1 or us-west-2.
The key and the secret was found in IAM - User. Once you have set up a user with Programmatic access and AmazonSESFullAccess permissions. Select your User and then select the "Security credentials" tab. Scoot down and click on Create Access Key.
You will only be given access to your Secret Password once. When you get access, highlight the Secret Password, copy it and paste it somewhere safely along with your Key.
Your from email must be related to the domain registered on the AWS SES page. If you don't host with Route 53, you will need to register and verify the email on the AWS SES page as well.
Here's the router code:
app.post('/email', function(req, res) {
var ses = require('node-ses'),
client = ses.createClient({
key: 'xxx',
secret: 'xxx',
amazon: 'https://email.eu-west-1.amazonaws.com'});
client.sendEmail({
to: 'xxx'
, from: 'xxx'
, cc: ''
, bcc: ''
, subject: 'greetings'
, message: 'your <b>message</b> goes here'
, altText: 'plain text'
}, function (err, data, res) {
if (err) {
console.log('Email send Error: ',JSON.stringify(err, null, 2));
} else {
console.log('Email send Success: ', JSON.stringify(data,null,2));
}
});
});
Related
Goal: I am working on a side project that uses React for frontend and Express.js for backend. My goal is to send acknowledgement emails to logged in users via nodemailer using express api.
Progress:
I have set up HTTPS for my react app that lives in Amazon s3 via Cloudfront and route53. I did this by attaching the domain name(https://www.example.com) to the cloudfront distribution via Route53. I used AWS ACM to create the cert.
My backend code lives in an EC2 instance, I have set up HTTPS for the backend api api.example.com. I used certbot to configure lets encrypt cert and pointed nginx server name to api.example.com to configure HTTPS.
I have setup a backend api to notify logged in users. I send emails using nodemailer.
const nodemailer = require("nodemailer");
const transporter = nodemailer.createTransport({
service: "gmail",
auth: {
user: process.env.USERNAME,
pass: process.env.PWD
},
});
exports.emailAck = function (req, res) {
const mailOptions = {
from: 'Testing email',
to: req.body.email,
subject: "Thank you for submitting the request for : " + req.body.id,
text: "TBD",
};
transporter.sendMail(mailOptions, function (error, info) {
if (error) {
res.json({status: "error", message: error});
}
else {
res.json({ status: "success", message: "Email sent" });
}
});
};
Problem:
My emails are being sent only when I turn on allow less secure apps in gmail.. Even though my app is secured, I am not sure why this is happening
When I try to store my transporter auth credentials in .env file to protect the secrets, I see a Error: Missing credentials for "PLAIN" error but if I try to add plaintext in the given file below, it works.
I use https://www.npmjs.com/package/react-google-login in the react side to setup login functionality.
I am wondering if I should restrict my backend api in EC2 to just allow access to the react app so it wouldn't be considered less secure application...
I would appreciate any direction with the above two issues,If there is a better solution other than node mailer, I am open to trying it.
Thanks in advance. I can clarify any questions in the edits if anything is not clear, so please let me know.
For issue number 2, if you are using dotenv package for environment variables,you should load the config like this in your js file
require('dotenv').config()
Good day! I’m trying to implement a Passwordless login using auth0 node package. Basically I’m trying to send magic link through email without getting the email value in the browser, so I’m getting the email from an API.
Note: The email that has been pulled from other API is already registered in Auth0
The problem was when I receive the link in my Inbox and click it, I’m getting the Opt-in that I should allow the app to access the profile, which is not the path that I’m expected to see. So here’s my code:
const AuthenticationClient = require('auth0').AuthenticationClient
app.get('/sendmagiclink', function(req, res) {
let auth0 = new AuthenticationClient({
domain: [Auth0 Domain],
clientId: [Auth0 Client ID],
clientSecret: [Auth0 Client Secret]
})
var data = {
email: 'myemail#gmail.com',
send: 'link',
authParams: {
connection: [My Connection]
} // Optional auth params.
};
auth0.passwordless.sendEmail(data, function (err) {
if (err) {
// Handle error.
}
});
})
Also, another problem with my code is the connection name which automatically sets to email rather than the custom connection name I created.
Your thoughts would be greatly appreciated.
I assume you are getting the Consent screen here. Are you using a localhost URL? In that case it's not possible to skip it, but it won't appear when your app is in production, or if you set up a domain in /etc/hosts file. More info here: https://auth0.com/docs/api-auth/user-consent#skipping-consent-for-first-party-clients
The name of the passwordless email connection is usually email.
Email address is not verified. The following identities failed the check in region US-WEST-2
{
message: 'Email address is not verified. The following identities failed the check in region US-WEST-2: xxx#gmai.com, xxx#gmail.com',
code: 'MessageRejected',
time: Tue Sep 12 2017 13:01:12 GMT+0000 (UTC),
requestId: '73dddb05-97ba-11e7-9847-c5dd9b1b6fa5',
statusCode: 400,
retryable: false,
retryDelay: 94.04094410128891
}
You need to request that Amazon support take your SES account out of sandbox mode. Until you do that you will only be able to send emails to verified email addresses.
You may send email to only verified emails using AWS SES. To add email id to the verified email list, add email using SES (https://console.aws.amazon.com/ses/home?region=us-east-1#verified-senders-email:) and click to verification email send by AWS. After that you may able to send email using AWS SES.
Also, you need to add IAM role to the lambda to get access of SES.
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 trying to send email to my other email from amazon SES verified email, but the programs gives an error that email address in to field is not verified. I am making a web app which allows user to log in using AWS Cognito so I dont have their email addresses in database. I need to send email to them on an event(I cannot use SNS because I need to send emails to selective persons which I have figured out.) So my questions are:
a)Do we need to verify SES email of the recipient also?
b)If yes, how can we use the cognito identity pool to verify their email addresses for SES.
code:
var aws = require("aws-sdk");
aws.config.update({
region: "us-west-2",
});
var ses = new aws.SES({"accessKeyId": "Mykey", "secretAccessKey":"YY","region":"us-west-2"})
var to = ['xyz#gmail.com']
var from='abc#gmail.com'
ses.sendEmail( {
Source: from,
Destination: { ToAddresses: to },
Message: {
Subject:{
Data:"Sending emails through SES"
},
Body: {
Text: {
Data: 'Stop your messing around',
}
}
}
}
, function(err, data) {
if(err) throw err
console.log('Email sent:');
console.log(data);
}
Error:
MessageRejected: Email address is not verified. The following identities failed the check in region US-WEST-2: xyz#gmail.com
If you're testing this inside your SES sandbox, you need to manually verify the recipient email addresses before it will allow you to send.
This step isn't required after leaving the sandbox, but it's a reasonable default safety setting when testing email-related functionality during development and not wanting bogus emails to go our for real.
Amazon SES Email Sending Errors (relevant portion in bold):
Email address is not verified. The following identities failed the
check in region : , , —You
are trying to send email from an email address or domain that you have
not verified with Amazon SES. This error could apply to the "From",
"Source", "Sender", or "Return-Path" address. If your account is still
in the sandbox, you also must verify every recipient email address
An easier way to test your email sending in AWS without needing to send actual emails would be to use their mailbox simulator:
The Amazon SES mailbox simulator is a set of test email addresses.
Each email address represents a specific scenario. You can send emails
to the mailbox simulator when you want to:
Test your application without having to create test "To" addresses.