Sending Mail Via Nodemailer Using Your Gmail With OAuth2

Sending email out from your application is pretty much a requirement. Because you want consistency in such an important aspect of your functionality, you will want to choose a reliable mail service, so I chose to use Gmail as SMTP. It turns out, the simple way is to turn on a feature called “less secure apps” in your account settings, but I actually don’t want to present those kinds of options to my clients, so I was stuck with the OAuth2 method, which was a little more of a pain. Due to the very definition of OAuth2, user interaction is going to required, so prepare to get your hands a little dirty. We’re not only going to see where to put the information in your code, but how to get it. The official Google page[1] holds to official instructions for what to do, but here’s the short list:

  1. Set up your application credentials in your Google account
  2. Get the secret URL and code
  3. Redeem the code for access tokens
  4. Plug your tokens and credentials into Nodemailer

Sounds like OAuth2 to me. You can get your token by just going to the Google OAuth Playground[2], but I think it is better to code the entire process for a couple reasons:

  1. It’s fun and educational (yes it is, dammit!)
  2. I found it better to just write my own functions to do it for me, that way I can program in my settings and I don’t have to remember what I did or why later.

You can get the fully runnable TypeScript/Javascript examples I’m working from the GitHub repo I made here.

 

Authorize your App with Google

The very first thing you’re going to have to do is give your application access to your Gmail account by creating an OAuth client ID. Log into your Google account, and go to the Google Console. At the time of writing, there is a menu on the top left you can toggle by clicking the three-barred hamburger icon. Under APIs & Services, you can navigate to the credentials page. If you have not already created a project, you will be prompted to, otherwise, toggle the dropdown at the top and create a new project for your application. On the credentials page with your project selected, go ahead and create a OAuth client ID. After you create your ID, it should display your client ID and client secret for you. That is all well, but you actually want to grab the full JSON version of your information, so click okay to dismiss the dialog, and select Download JSON. You will paste that into your code as one of the first things you do.

In order to interface the Google services, go ahead and add Google’s officially supported node.js client library for OAuth2.

npm i google-auth-library

I will be using TypeScript for my examples because I think it’s easier to convert TypeScript to Javascript than vice-versa. Add the following snippet of code to the file where you will store your configuration (mine goes in config/google_auth.ts)


import * as googleAuth from 'google-auth-library';
import { Credentials } from 'google-auth-library/build/src/auth/credentials';

const scope: string = "https://mail.google.com/";

/**
 * Step 0: Create OAuth2 credentials at the Google Console (make sure to download JSON, not only just get key and secret)
 */

export const credentials = {
	"web": {
		"client_id": "############-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com",
		"project_id": "my-project",
		"auth_uri": "https://accounts.google.com/o/oauth2/auth",
		"token_uri": "https://accounts.google.com/o/oauth2/token",
		"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
		"client_secret": "xxxxxx-xxxxxxxx-xxxxxxx",
		"redirect_uris": ["http://localhost"],
		"javascript_origins": ["http://localhost"]
	}
};

Nice, so we have the first part of what we need. A small note, Nodemailer uses SMTP, and will need full scope access, despite the array of scopes Google provides[3][4].

 

Get Secret Browser Url

 

In order to actually get our access tokens for OAuth2, we need to visit a secret page in our browser, which will give us another secret code, which we will use to get our token. Go ahead and add this authorize function to your configuration file, and call out to it to see your secret url:


/**
 * Step 1: Authorize in the browser
 */

export function getAuthorizeUrl(callback: (err: any, url: string) => any): void {
	const oauth2Client = new googleAuth.OAuth2Client(credentials.web.client_id, credentials.web.client_secret, credentials.web.redirect_uris[0]);

	const authUrl = oauth2Client.generateAuthUrl({
		access_type: 'offline',
		scope: scope
	});

	callback(null, authUrl);
}

getAuthorizeUrl((err, url) => {
	if(err) return console.log(err);
	console.log("Auth url is: ", url);
});

After running this and going to the URL in the console output, you will give the application permission to access your account, and it will send you back to your redirect page with a GET parameter called code, and it will be a random jumble of letters and stuff that looks like this:

?code=4%2FAAAt_6Q8QhcsPxIbhr_k4KH102H4J1DNOzzS1QCe-hSIEyatB7HJOIoMK50nnyv2BD9VyMaFeXrQZJxf_wI7YXA#

Just remember that this is URL encoded, so decode the %2F so that it becomes a forward slash.

4/AAAt_6Q8QhcsPxIbhr_k4KH102H4J1DNOzzS1QCe-hSIEyatB7HJOIoMK50nnyv2BD9VyMaFeXrQZJxf_wI7YXA

 

Get Access and Refresh Tokens

 

We will save that single-use code value as a variable in our code to get the token. Go ahead and add the function to get the auth token in the configuration file and call out to it, just as before. You will want to comment out the call to authorize() from the last step so that it does not keep generating secret URLs.


/**
 * Step 2: Get auth token
 */

/**
 * Paste in your one-time use authorization code here
 */
const code: string = "4/AAAt_6Q8QhcsPxIbhr_k4KH102H4J1DNOzzS1QCe-hSIEyatB7HJOIoMK50nnyv2BD9VyMaFeXrQZJxf_wI7YXA";

export function getAccessToken(callback: (err: any, token?: Credentials | null) => any): void {
	const oauth2Client = new googleAuth.OAuth2Client(credentials.web.client_id, credentials.web.client_secret, credentials.web.redirect_uris[0]);

	oauth2Client.getToken(code, (err, token) => {
		if(err) return console.log(err);

		callback(null, token);
	});
}

getAccessToken((err, token) => {
	if(err) return console.log(err);
	console.log("Auth token is: ", token);
});

Sweet, after running that function with our secret code, we should see our tokens in the log window! If you don’t see a refresh token, you may have already gotten one, and Google doesn’t send it again[5]. If you lost your refresh token, go to the “Apps with access to your account” part of your Google account[6] and remove the application requesting access and try again. Let’s paste that in at the end of our configuration file, and we will set up Nodemailer.


/**
 * Step 3: Save access and refresh tokens as an export for Nodemailer
*/

/**
 * Paste your credentials here as this object.
 */

export const tokens: Credentials = {
	access_token: 'xxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxx',
	token_type: 'Bearer',
	refresh_token: 'x/xxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
	expiry_date: 1528250763439
};

 

Configure your Tokens with Nodemailer

 

The very last thing you need to do is plug it all into Nodemailer. You can see about 3 legged authentication for Nodemailer at on the Nodemailer website[7]. Go ahead and create a new configuration file for your nodemailer (which I usually put as config/nodemailer_config.ts).


import * as nodemailer from 'nodemailer';
import * as googleAuth from './google_auth';

const EMAIL_USERNAME: string = 'me@example.com';
const COMMON_NAME: string = 'John Doe';

const nodemailerSettings = {
	host: 'smtp.gmail.com',
	port: 465,
	secure: true,
	service: 'Gmail',
	from: `"${COMMON_NAME}" <${EMAIL_USERNAME}>`, // You actually dont have to do this because Gmail overwrites it with the authenticated user anyway, but it's semantic
	
	auth: {
		type: 'OAuth2',
		user: EMAIL_USERNAME,
		clientId: googleAuth.credentials.web.client_id,
		clientSecret: googleAuth.credentials.web.client_secret,
		refreshToken: googleAuth.tokens.refresh_token,
		accessToken: googleAuth.tokens.access_token,
		expires: googleAuth.tokens.expiry_date
	}
} as nodemailer.TransportOptions;

export const gmailTransport = nodemailer.createTransport(nodemailerSettings);

I hope this will help somebody get authentication working so that we can all do the cool things in life, like using Gmail as SMTP, or spending your weekend… Not configuring that. Let me know if you have any questions in the comments and happy coding!

Kyle Niemiec is Chief Development Officer at Digital Solutions by DesignInk

[1]https://developers.google.com/identity/protocols/OAuth2
[2]https://developers.google.com/oauthplayground/
[3]https://developers.google.com/identity/protocols/googlescopes#gmailv1
[4]https://github.com/andris9/xoauth2/issues/6
[5]https://stackoverflow.com/questions/10827920/not-receiving-google-oauth-refresh-token
[6]https://myaccount.google.com/permissions
[7]http://nodemailer.com/smtp/oauth2/#example-3

Leave a Reply

Your email address will not be published. Required fields are marked *