Simple OAuth2 with Passport and Express

dtroyano86

Daniel Troyano

Posted on August 9, 2020

Simple OAuth2 with Passport and Express

This tutorial is going to walk you through getting OAuth2 set up, without a database. It will just persist sessions and cookies locally using Passport and Express' built in functionality. This probably isn't ideal for production, but I found it helpful to learn, and hopefully you will too.

I'm going to use Google to log people in with OAuth2 so if you want to follow along you're going to need a Google Client ID, and Client Secret, to get that all set up you'll have to go to your Google Developer's Console.


Now before we get started, let's set up an empty package.json file with npm init -y, which will just auto select all the defaults for you. Then let's install a few modules that we're going to need.

npm install --save express express-session passport passport-google-oauth

We're using Express to write our server code, and the Express Sessions plugin will help us persist sessions. Passport is our authentication middleware, and it uses Strategies to define how it does authentication so we need Passport Google OAuth because we're using Google to log users in.

We're only going to need one file for this whole demo, so go ahead and make an index.js file. And at the top of it we're going to require all the packages we just installed.

If you want to know more about Strategies and why we're using this specific one you can check the Passport Documentation.

const express = require('express');
const session = require('express-session');
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;

const app = express();

Now that we have our app initialized, we want to set up the Session and Passport middleware. Let's do Session first.

app.use(session({
  secret: 'demo',
  resave: false,
  saveUninitialized: true,
}));

I've used a secret here of a string of demo, but this should really be a secret string of random characters, but for our purposes, this is fine. ReSave and saveUninitialized are being set to essentially their default values, but if you want to know more about them and why you might want different settings, it's going to depend on how your database works, check the Express Sessions docs.

Now let's set up Passport before using it as middleware as well. We have to tell Passport how to handle serializing and deserializing a user. In our case we're just passing information along, but in a real app these will probably be a little more complicated.

passport.serializeUser((user, done) => {
  done(null, user.id);
});

passport.deserializeUser((id, done) => {
  done(null, id);
})

SerializeUser is called whenever the user logs in, and in our case it just saves the user.id to the session. Then whenever a user visits a page, deserializeUser will be called and ideally, you would use the given id to find a matching user in your database. But in our case we're just returning the id. DeserializeUser will save whatever you return to the req.user object.

Now we have to set up Passport itself, with our secrets. We also need to define a callback route, which should match the one we told Google about when we got our Client Id and Client Secret. The callbackURL is where Google is going to bounce us back to after attempting to authenticate the user.

passport.use(new GoogleStrategy({
    clientID: GOOGLE_CLIENT_ID,
    clientSecret: GOOGLE_CLIENT_SECRET,
    callbackURL: "/callback"
  },
  (accessToken, refreshToken, profile, done) => {
    return done(null, {name: profile.displayName, id: profile.id});
}));

This function is called when the user is verified and is just going to return the profile's name and id. But this is where you would parse the data you are getting back from Google. And maybe add them to your database or check to see if they already exist.

Now we just need to tell our app to use Passport.

app.use(passport.initialize());
app.use(passport.session());

And now we can set up a few routes to hit to see our app work. First let's do our '/', here we'll just console log several variables so you can see your Session become populated.

app.get('/', (req, res) => {
  console.log('SESSION',req.session);
  console.log('sessionID', req.sessionID);
  console.log('USER', req.user);
  res.status(200).send('YAY!')
});

Then we'll set up our login route which is just a call to Passport's authenticate function. We have to specify that we want to use Google, and the scope is what information we want to get back. Google has a long list of scopes you can use in their docs.

app.get('/login',
  passport.authenticate('google', { scope: ['profile']}));

We also need that callback route we were talking about earlier. Here we use authenticate as middleware, specifying Google again, and if we failed to authenticate we will redirect to our failure route. But otherwise we will redirect to our home.

app.get('/callback',
  passport.authenticate('google', { failureRedirect: '/failure'}),
  (req, res) => {
    res.redirect('/');
});

Let's also just set up that failure route, it doesn't need to do much of anything for our purposes.

app.get('/failure', (req, res) => {
  res.status(200).send('FAILED');
});

Finally let's have a logout route. Passport exposes a logout function on the request object which you can call to log out a user. So it's just as simple as you see here.

app.get('/logout', (req, res) => {
  req.logout();
  res.redirect('/');
});

Finally we have to start our app listening

app.listen(8080, () => console.log('listening'));

And that's it. You now have a very simple demo app that will talk to Google to log in a user, and then persist them in a session. Granted it won't really do anything with that user, but the goal here was just to set up the simplest possible app with some kind of authentication using Passport. I hope going through this was as helpful to other people as it was to me.

💖 💪 🙅 🚩
dtroyano86
Daniel Troyano

Posted on August 9, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related