Brian Bancroft

Setting up oauth in NodeJS for the Impatient

December 18,'19 | Tutorial

Sometimes making that first step into a well-documented process is intimidating. There's a lot of theory involved, and what you want to do is build a live example so that you can work backwards to understand how the thing works. If this is your way of thinking, this tutorial is for you.

oAuth allows you to authenticate individuals in your app using one or more external providers of your choice. If you don't want passwords, you can have them use their Facebook, Github, Google or other accounts instead.

In this tutorial you can expect to get past the first mental logjam towards integrating an oAuth-compliant Github authentication routed into a bare-bones express API. Once there, you should be in a place to Stackoverflow all the other incoming problems you encounter.

oauth-icon

Let's do it

For the setup, we're going to use a command line process. I will be using a unix terminal with Node version 12 (LTS). I do not suggest using this tutorial with anything older than Node 10. First, let's create a directory:

mkdir express-api-oauth

Now, like any new node project we want to setup npm, git and add the .gitignore file:

git init && npm init && npx gitignore node

You're going to have to run through the new project syntax. Here, I just smash the enter key:

install package json

Once you've done that, the next step is to add dependencies to the mix:

npm i dotenv express passport passport-github

We also want to add nodemon to the mix as a dev dependency. This allows us to reset the application on each code change.

npm i -D nodemon

These dependencies make up the bare minimum of what we're going to need for the purposes of this tutorial. We're now ready to configure the surrounding application.

Configure the Express App

Before we start to use passport, let's set everything else up first. Create an index.js, and add the following script in package.json:

"start": "nodemon index.js"

Now in index.js, let's add the structure to the app. Here, the hello world example from express (https://expressjs.com/en/starter/hello-world.html) is up for the task. We're just going to sprinkle in dotenv, allowing us to use environment variables:

require('dotenv').config();
const express = require('express')
const app = express()
const port = 3000
app.get('/', (req, res) => res.send('Hello World!'))
app.listen(port, () => console.log(`Example app listening on port ${port}!`))

At this point, you should be able to run npm start in the command line.

Start

Running http://localhost:3000 also works well at this point. You should see "Hello world!" in the browser. If you have, you've created the minimal express js app with one addition. We can now tack on our Github authentication.

Configure the Github oAuth Provider

For this step, we're going to take a step aside to check out Github. Go here:

https://github.com/settings/applications/new

If you're already signed in, you should see something like this:

github_auth

We're going to want to fill in the fields in the following manner:

Name: Call this whatever you want.

Homepage URL: http://localhost:3000/

Application Description: Example application using oAuth

Authorization callback URL: http://localhost:3000/auth/github/callback

Once you register the application, find the client ID and secret. Go back to the codebase, and create a .env file in the root directory. Fill it with the following:

# .env
OAUTH_GITHUB_TOKEN=<Client ID>
OAUTH_GITHUB_SECRET=<Client Secret>

Once you have saved the file, quit the server manually and restart it. Doing this ensures that the environment variables are accessible within the server. Now we are ready to build those authentication routes.

Configure Passport and Build those authentication routes!

The first part of the configuration involves copying and pasting content from the passport-github dependency's Github repo (https://github.com/jaredhanson/passport-github/#configure-strategy). First, we're going to import the dependencies at the top of the file:

const passport = require("passport");
const GitHubStrategy = require("passport-github").Strategy;

Now, before you declare the first route (app.get('/', ...), paste in the following block:

app.use(passport.initialize());
passport.use(
new GitHubStrategy(
{
clientID: process.env.OAUTH_GITHUB_TOKEN,
clientSecret: process.env.OAUTH_GITHUB_SECRET,
callbackURL: "http://localhost:3000/auth/github/callback"
},
function(accessToken, refreshToken, profile, cb) {
// User.findOrCreate({ githubId: profile.id }, function(err, user) {
// return cb(err, user);
// });
console.log("Access Token ", accessToken);
console.log("Refresh Token ", refreshToken);
console.log("Profile ", profile);
return cb(null, (req, res) => res.send("Hello auth user"));
}
)
);

This is the base configuration. It's worth noting that we have omitted session or cookies. This will come to bite us in the butt, but for the purposes of demoing a working oAuth signin, this is fine. You'll notice where I've commented out what appears to be a sequelize ORM command. This is the part where you'd want to find the user, and set up some form of session.

Once you have the configuration, it's time to make the routes:

app.get('/auth/github', passport.authenticate('github'))
app.get(
'/auth/github/callback',
passport.authenticate('github', { failureRedirect: '/nope' }),
function(req, res) {
// Successful authentication, redirect home.
res.redirect('/')
}
)
app.get('/nope', (req, res) => {
res.send('nope')
})

Once you have entered this information, make sure the server is still running. If it is, go to http://localhost:3000/auth/github and you should see something like this:

working

Congrats! At this point if you Authorize, you should be redirected.

The bad news is that the redirect will you will get the error:

Error: passport.initialize() middleware not in use

But that's fine. What you have is the minimal example of single sign on that you can use as a basis of understanding oauth. From here, it's up to you to figure out the answer to problems such as:

  1. Setting up session or cookies (which is the crux of the problem you've run into),

  2. What to do when a person's oAuth has expired, and

  3. What to do when the person is already signed in.

Also look into the console. You now will see all manner of things that Github collects from you, which can now be stored into your own application. You can pull avatars, as well as email addresses. This will help in the event that the user loses their github account, or to match with other oauth providers.

Below contains the entire application if this is something you'd want:

require("dotenv").config();
const express = require("express");
const passport = require("passport");
const GitHubStrategy = require("passport-github").Strategy;
const app = express();
const port = 3000;
app.use(passport.initialize());
app.use(passport.session());
passport.use(
new GitHubStrategy(
{
clientID: process.env.OAUTH_GITHUB_TOKEN,
clientSecret: process.env.OAUTH_GITHUB_SECRET,
callbackURL: "http://localhost:3000/auth/github/callback"
},
function(accessToken, refreshToken, profile, cb) {
// User.findOrCreate({ githubId: profile.id }, function(err, user) {
// return cb(err, user);
// });
console.log("Access Token ", accessToken);
console.log("Refresh Token ", refreshToken);
console.log("Profile ", profile);
return cb(null, (req, res) => res.send("Hello auth user"));
}
)
);
app.get("/", (req, res) => res.send("Hello World!"));
app.get("/auth/github", passport.authenticate("github"));
app.get(
"/auth/github/callback",
passport.authenticate("github", { failureRedirect: "/nope" }),
function(req, res) {
// Successful authentication, redirect home.
res.redirect("/");
}
);
app.get("/nope", (req, res) => {
res.send("nope");
});
app.listen(port, () => console.log(`Example app listening on port ${port}!`));