Token based authentication in node.js using passport

We recently implemented token based authentication for our express api. I was searching the web for information and found quite some good articles about parts of the topic, but nothing, which suited our problem as a whole.

We needed to be able to authenticate via several services, Google and Facebook for the most part. As we already had a session based setup for this, we would liked to use passport and it's strategies to save us some work.

This article is part one of two related posts:

A fully configured example can be found on bitbucket.

There are quite some articles out there, the most helpful ones i found are:

Authenticate with passport

Authentication via passport has different steps to it:

  • Authenticate via passport and its strategies.
  • Serialize user (find or create a user in our database).
  • Generate token.
  • Send everything back.

Setting up passport for a token based authentication service is quite easy.

First, we setup our Strategies as usual. In this example we use a local strategy with a dummy database:

'use strict';

const passport = require('passport');  
const Strategy = require('passport-local');

passport.use(new Strategy(  
  function(username, password, done) {
    // database dummy - find user and verify password
    if(username === 'devils name' && password === '666'){
      done(null, {
        id: 666,
        firstname: 'devils',
        lastname: 'name',
        email: 'devil@he.ll',
        verified: true
      });
    }
    else {
      done(null, false);
    }
  }
));

module.exports = passport;  

Next step is to integrate passport into our express server.

const express = require('express');  
const http = require('http'); // you really want https here!

const app = express();

app.use(passport.initialize());  
app.post('/auth', passport.authenticate(  
  'local', {
    session: false
  }), serialize, generateToken, respond);

http.createServer(app).listen(1337);  

As token based authentication doesn't need session cookies, we need to make sure to disable passports store by setting the session option to false.
This way passport won't create session cookies.

After authentication is done, we call three middlewares: serialize, generateToken and respond.

As we may use passport Strategies other than our local one, we need to create unknown but authenticated users.
The middleware does pretty much the same as the usual passport.serialize function. We have to move it out of passport as passport won't call it without session set to true.

function serialize(req, res, next) {  
  db.updateOrCreate(req.user, function(err, user){
    if(err) {return next(err);}
    // we store the updated information in req.user again
    req.user = {
      id: user.id
    };
    next();
  });
}

const db = {  
  updateOrCreate: function(user, cb){
    // db dummy, we just cb the user
    cb(null, user);
  }
};

We now have our user authenticated via passport and stored in our database as well as in req.user.

The next step is to generate the access-token.

const jwt = require('jsonwebtoken');

function generateToken(req, res, next) {  
  req.token = jwt.sign({
    id: req.user.id,
  }, 'server secret', {
    expiresInMinutes: 120
  });
  next();
}

We generate our token with the module jsonwebtoken. Inside the token we store the id of the authenticated user.

Last but not least we send everything back to the user:

function respond(req, res) {  
  res.status(200).json({
    user: req.user,
    token: req.token
  });
}

Let's try to authenticate using curl:

curl -X POST -H 'Content-Type: application/json' -d '{ "username": "devils name", "password": "666" }' localhost:1337/auth

=> {"token": "myToken", "user": {"id":666}}

As expected a token is created and returned to the user.

Protect a route

To protect our api we use the module express-jwt. It checks, if an incoming token (set in Authorization-Header) is valid and stores the token data in req.user (just as passport would, ... sweet).

const expressJwt = require('express-jwt');  
const authenticate = expressJwt({secret : 'server secret'});

app.get('/me', authenticate, function(req, res) {  
  res.status(200).json(req.user);
});

Check if it works (make sure you replace 'mytoken' with the access token you received earlier):

curl -H 'Authorization: Bearer mytoken' localhost:1337/me

=> {"id": 666, "iat": timestamp, "exp": timestamp}

That's all we need! We now have a protected route.

Conclusion

In this post we had a look at how to implement a token based authentication in node.js. We are now able to use passport and it's strategies for login and the express-jwt middleware to protect our api.
One great advantage of tokens is that we don't have to lookup the token in a database on every api call as it contains all needed information in itself. This should help keeping the authentication process small. The biggest downside of that is the inability of revoking a token without having a whitelist or blacklist somewhere. This is the reason we keep the lifetime of the token small (120 minutes in this example).
In the next post we will discuss how to upgrade this basic setup to use refresh tokens to be able to stay logged in permanently as well as creating multiple user sessions with different devices.

That's it, thanks for reading :). Feel free to leave suggestions and questions in the comment section below.

Next post: using refresh tokens in node.js to stay authenticated

comments powered by Disqus