Nodejs Express OAuth2 Guide

API Discussion
OAuth2 can be kind of confusing at times, especially in the development environment. Please, allow me to try to alleviate some of your frustration setting up and testing Bnet OAuth2 in your app. This guide is mainly going to focus on how to get a development environment running. As a result, the production side will come to life as well.

I used this guide by Billi to get all set up myself. https://us.battle.net/forums/en/bnet/topic/14881218054
I am only going to cover bnet-us, please refer to Billi's guide to have more than just the US region.

A little disclaimer is that I don't claim for this to be the best method but it is one that worked very well for me in testing and production.

I work in Windows and use the Windows Subsystem for Linux. https://docs.microsoft.com/en-us/windows/wsl/install-win10
I will be writing Linux commands because I am assuming you will also be using this tool. I will also be assuming that you have a front end client running on a different port than your backend.

Let's start with what packages you need (after you already have node and express running).

  • https://www.npmjs.com/package/passport
    npm install --save passport
  • https://www.npmjs.com/package/passport-bnet
    npm install --save passport-bnet
  • https://www.npmjs.com/package/dotenv
    npm install --save dotenv
  • https://www.npmjs.com/package/localtunnel
    npm install -g localtunnel


I will only be going over how to connect passport to bnet and authentication routes. For more information on hooking up passport to your app please refer to the passport docs. http://www.passportjs.org/docs/

Let's start by setting up your environment. Go ahead and create your .env file in your root directory and as early as possible in your application, require and configure dotenv.
require('dotenv').config()
Inside of your .env file create these variables. Remember to never push this up to your repository to protect your keys.
BNET_ID='insertKeyHere'
BNET_SECRET='insertSecretHere'

Create a constants file (constants.js) and let's define your domains. The Callback URL is https (localtunnel allows this) because that's what Blizzard requires.
let constants = {};
if (process.env.NODE_ENV === 'production') {
constants.DOMAIN = '';
constants.CALLBACKURL = '/auth/bnet/callback'
} else {
constants.DOMAIN = 'http://localhost:' + frontEndPort;
constants.CALLBACKURL = 'https://myapp.localtunnel.me/auth/bnet/callback'
}

module.exports = constants;

Ok, your domains are setup. Let's now look at our routes. The first route is going to be what your application redirects to when a user clicks the login button. It will handle the redirect to Bnet. The second route (callback) is what Bnet is looking for after the user has entered their credentials. In this callback route you will decide where the user is redirected to after they login through Bnet.
const passport = require('passport');
const constants = require('/path/to/constants.js');

/*=========================
Bnet OAuth2 Navigate to
===========================*/
router.get('/auth/bnet', (req, res, next) => {
passport.authenticate('bnet',
(err, user, info) => {
if (err) {
return next(err)
}
})(req, res, next);
});

/*============================
Bnet OAuth2 Callback Route
==============================*/
router.get('/auth/bnet/callback', (req, res, next) => {
passport.authenticate('bnet', { failureRedirect: constants.DOMAIN + '/loginFailure' }, (err, user, info) => {
if (err) {
return next(err);
}

console.log(user);
res.redirect(constants.DOMAIN + '/loginSuccess' );
})(req, res, next);
});

Now that the routes are all setup, we need to configure passport-bnet. After you have setup your initial passport (look at passport guides) we will configure the BnetStrategy.
const constants = require('/path/to/constants.js');
const passport = require('passport');
const BnetStrategy = require('passport-bnet').Strategy;

passport.use(new BnetStrategy({
clientID: process.env.BNET_ID,
clientSecret: process.env.BNET_SECRET,
callbackURL: constants.CALLBACKURL,
region: 'us'
}, (accessToken, refreshToken, profile, done) => {
// Profile returns the Bnet Id, Battletag
return done(null, profile);
}));

Alright, good work. On your website you will create a button or an a tag with an href pointing to your '/auth/bnet' route. However, in your development build, this needs to point to the URL that localtunnel gives you. I will show you how to run localtunnel after you create this button. In production, it will just point to your normal URL with the /auth/bnet route.
<a href="https://myapp.localtunnel.me/auth/bnet/">Login with Bnet</a>
Let's startup localtunnel. Open your bash shell (using the Windows Subsystem for Linux) and run
lt --port 8080 --subdomain myapp
A few things to keep in mind. This port needs to be your backend port. The subdomain also must be unique throughout the world. If you all use 'myapp', only one person is actually going to get the myapp subdomain.

Now open up another bash shell and startup your node server with this command.
NODE_ENV=development node app.js
When it's production you will use this command instead.
NODE_ENV=production node app.js
When you click your button, it doesn't work. Wtf, Uglyer I followed your guide to the T! Don't panic, we just need to tell Bnet what our Callback URL is. You need to login to your dev.battle.net portal and follow these steps.

Click on My Account -> Applications -> Edit -> scrolldown and enter your Callback URL (the same from the constants file depending on whether you are in development or production) into that textbox and hit save. It might take a few minutes to update on Bnet's side.

A few things to remember is that if you do not use a constants file on your front end you will need to change the link on your button to be your Production URL when you deploy. The other place that is not dynamic will be the Callback URL on dev.battle.net, this will also need to be manually changed as you deploy your app.

I hope this made sense and actually helps a few of you! Let me know what questions you have or if I've just messed up completely :P

EXTRA: Get a user's WoW characters.

Let's take a look back at the passport-bnet configuration. It gives us an Access Token to be used to gain information about the user. Let's use it to get the user's WoW characters.

To make http requests from the server I use the package axios. https://www.npmjs.com/package/axios
npm install --save axios
I'm going to rewrite the passport-bnet config code here. Notice in the options portion of the BnetStrategy that I add a 'scope' parameter. Also, if you have any information coming from the request, you can pass it into your callback function with the 'passReqToCallback' parameter.
const constants = require('/path/to/constants.js');
const passport = require('passport');
const BnetStrategy = require('passport-bnet').Strategy;
const axios = require('axios');

passport.use(new BnetStrategy({
clientID: process.env.BNET_ID,
clientSecret: process.env.BNET_SECRET,
scope: 'wow.profile',
callbackURL: constants.CALLBACKURL,
region: 'us',
passReqToCallback: true,
}, (req, accessToken, refreshToken, profile, done) => {
axios({
method: 'get',
url: 'https://us.api.battle.net/wow/user/characters',
headers: { 'Authorization': 'Bearer ' + accessToken }
})
.then(response => {
// Info about the user
console.log(response.data);
// User's characters
console.log(response.data.characters);
})
.catch(err => {
return done(err);
});
}));
I needed this like 4 weeks ago, but hey. Awesome job!
Thank you!

Join the Conversation

Return to Forum