in packages/fxa-auth-server/lib/email/bounces.js [53:179]
async function handleBounce(message) {
utils.logErrorIfHeadersAreWeirdOrMissing(log, message, 'bounce');
let recipients = [];
// According to the AWS SES docs, a notification will never
// include multiple types, so it's fine for us to check for
// EITHER bounce OR complaint here.
if (message.bounce) {
recipients = message.bounce.bouncedRecipients;
} else if (message.complaint) {
recipients = message.complaint.complainedRecipients;
}
// SES can now send custom headers if enabled on topic.
// Headers are stored as an array of name/value pairs.
// Log the `X-Template-Name` header to help track the email template that bounced.
// Ref: http://docs.aws.amazon.com/ses/latest/DeveloperGuide/notification-contents.html
const templateName = utils.getHeaderValue('X-Template-Name', message);
const language = utils.getHeaderValue('Content-Language', message);
for (const recipient of recipients) {
// The email address in the bounce message has been handled by an external
// system, and depending on the system it can have had some strange things
// done to it. Try to normalize as best we can.
let email;
let emailIsValid = true;
const parsedAddress = eaddrs.parseOneAddress(recipient.emailAddress);
if (parsedAddress !== null) {
email = parsedAddress.address;
} else {
email = recipient.emailAddress;
if (!isValidEmailAddress(email)) {
emailIsValid = false;
// We couldn't make the recipient address look like a valid email.
// Log a warning but don't error out because we still want to
// emit flow metrics etc.
log.warn('handleBounce.addressParseFailure', {
email: email,
action: recipient.action,
diagnosticCode: recipient.diagnosticCode,
});
}
}
const emailDomain = utils.getAnonymizedEmailDomain(email);
const logData = {
action: recipient.action,
email: email,
domain: emailDomain,
bounce: !!message.bounce,
diagnosticCode: recipient.diagnosticCode,
mailStatus: recipient.status,
};
const bounce = {
email: email,
diagnosticCode: recipient.diagnosticCode,
};
// Template name corresponds directly with the email template that was used
if (templateName) {
logData.template = templateName;
bounce.templateName = templateName;
}
if (language) {
logData.lang = language;
}
// Log the type of bounce that occurred
// Ref: http://docs.aws.amazon.com/ses/latest/DeveloperGuide/notification-contents.html#bounce-types
if (message.bounce && message.bounce.bounceType) {
bounce.bounceType = logData.bounceType = message.bounce.bounceType;
if (message.bounce.bounceSubType) {
bounce.bounceSubType = logData.bounceSubType =
message.bounce.bounceSubType;
}
} else if (message.complaint) {
// Log the type of complaint and userAgent reported
logData.complaint = !!message.complaint;
bounce.bounceType = 'Complaint';
if (message.complaint.userAgent) {
logData.complaintUserAgent = message.complaint.userAgent;
}
if (message.complaint.complaintFeedbackType) {
bounce.bounceSubType = logData.complaintFeedbackType =
message.complaint.complaintFeedbackType;
}
}
// Log the bounced flowEvent and emailEvent metrics
utils.logFlowEventFromMessage(log, message, 'bounced');
utils.logEmailEventFromMessage(log, message, 'bounced', emailDomain);
utils.logAccountEventFromMessage(message, 'emailBounced');
log.info('handleBounce', logData);
/**
* Docs: https://docs.aws.amazon.com/ses/latest/DeveloperGuide/notification-contents.html#bounce-types
* Bug: https://github.com/mozilla/fxa-content-server/issues/5629
*
* If there is any type of bounce then suggest account for deletion.
* Code below will fetch the email record and if it is an unverified new account then it will delete
* the account.
*/
const suggestAccountDeletion = !!bounce.bounceType;
const work = [];
if (emailIsValid) {
work.push(recordBounce(bounce).catch(gotError.bind(null, email)));
if (suggestAccountDeletion) {
work.push(
findEmailRecord(email).then(
deleteAccountIfUnverifiedNew,
gotError.bind(null, email)
)
);
}
}
await Promise.all(work);
}
// We always delete the message, even if handling some addrs failed.
message.del();
}