Using JSON Web Tokens with Node.js
Front end frameworks and libraries such as Ember, Angular, and Backbone are part of a trend towards richer, more sophisticated web application clients. As a consequence of this, server-side components are unburdened from many of their traditional responsibilities, in essence becoming more like API’s. This API approach allows a greater decoupling of the traditional “front end” and “back end” parts of an application. One set of developers can build the back end independently from the front end engineers, with the additional benefit that testing becomes simpler. This approach also makes it much easier to build, say, a mobile application that shares the same back end as your web application.
One of the challenges when providing an API is authentication. In traditional web applications, the server responds to a successful authentication request by doing two things. First, it creates a session using some storage mechanism. Each session has its own identifier – usually a long, semi-random string – which is used to retrieve information about the session on future requests. Secondly, that information is sent to the client by way of headers instructing it to set a cookie. The browser automatically attaches the session ID cookie to all subsequent requests, allowing the server to identify the user by retrieving the appropriate session from storage. This is how traditional web applications get around the fact that HTTP is stateless.
APIs should be designed to be truly stateless. This means no login or logout methods and no sessions. API designers can’t rely on cookies either, as there is no guarantee that requests will be made via a web browser. Clearly, we need an alternative mechanism. This article looks at one possible mechanism designed to tackle the problem – JSON Web Tokens, or JWTs (pronounced jots). The examples in this article uses Node’s Express framework on the back end, and Backbone on the client.
Background
Let’s briefly look at a few common approaches to securing APIs.
One is to use HTTP Basic Authentication. Defined in the official HTTP specification, this essentially involves setting a header on the server response which indicates authentication is required. The client must respond by attaching their credentials, including their password, to every subsequent request. If the credentials match, the user information is made available to the server application as as variable.
The second approach is very similar, but using the application’s own authentication mechanism. This usually involves checking the supplied credentials against those in storage. As with HTTP Basic Authentication, this requires that the user’s credentials are supplied with each and every call.
The third approach is OAuth (or OAuth2). Designed to a large extent for authenticating against third-party services, it can be rather challenging to implement, at least on the server-side.
A fourth approach is using tokens. That’s what we’re going to look at in this article. We’ll look at an implementation that utilizes JavaScript on both the front and back ends.
The Token Approach
Instead of supplying credentials such as a username and password with every request, we can allow the client to exchange valid credentials for a token. This token gives the client access to resources on the server. Tokens are generally much longer and more obfuscated than a password. For example, the JWTs we’re going to be dealing with are on the order of ~150 characters. Once the token is obtained, it must be sent with every API call. However, this is still more secure than sending a username and password with every request, even over HTTPS.
Think of the token like a security pass. You identify yourself at the front desk of a restricted building on arrival (supply your username and password), and if you can be successfully identified you’re issued a security pass. As you move around the building (attempt to access resources by making calls to the API) you are required to show your pass, rather than go through the initial identification process all over again.
About JWTs
JWTs are a draft specification, although in essence they are really just a more concrete implementation of an authentication and authorization mechanism that is already commonplace; that of exchanging tokens. A JWT is split into three parts, separated by periods. JWTs are URL-safe, meaning they can be used in query string parameters.
The first part of a JWT is an encoded string representation of a simple JavaScript object which describes the token along with the hashing algorithm used. The example below illustrates a JWT using HMAC SHA-256.
{
"typ" : "JWT",
"alg" : "HS256"
}
After encoding, the object becomes this string:
eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9
The second part of the JWT forms the core of the token. It too represents a JavaScript object, which contains a few pieces of information. Some of these fields are required, and some are optional. An example, taken from the draft specification, is shown below.
{
"iss": "joe",
"exp": 1300819380,
"http://example.com/is_root": true
}
This is called a JWT Claims Set. For the purposes of this article, we’re going to ignore the third parameter, but you can read more in the specification. The iss
property is short for issuer
, and specifies the person or entity making the request. Typically, this would be the user accessing the API. The exp
field, short for expires
, is used to limit the lifetime of the token. Once encoded, the JSON token looks like this:
eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ
The third, and final, part of the JWT is a signature generated based on the header (part one) and the body (part two). The signature for our example JWT is shown below.
dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
The resulting complete JWT looks like this:
eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
There are a number of additional, optional properties supported in the specification. Among them are iat
representing the time at which the token was issued, nbf
(Not Before) to indicate the token should not be accepted before a certain time, and aud
(audience) to indicate the recipients the token is intended for.
Handling the Tokens
We’re going to use the JWT Simple module to handle the tokens, which saves us from having to delve into the nitty gritty of encoding and decoding them. If you’re really interested you can find more information in the specification, or read through the repo’s source code.
Begin by installing the library using the following command. Remember that you can automatically add it to your project’s package.json
file by including the --save
flag in the command.
npm install jwt-simple
In your application’s initialization section, add the following code. This code imports Express and JWT Simple, and creates a new Express application. The final line of the example sets an application variable named jwtTokenSecret
to the value YOUR_SECRET_STRING
(make sure to change this value to something else).
var express = require('express');
var jwt = require('jwt-simple');
var app = express();
app.set('jwtTokenSecret', 'YOUR_SECRET_STRING');
Getting a Token
The first thing we need to do is enable the client to exchange their username and password for a token. There are two possible approaches to this in a RESTful API. The first is by issuing a POST
request to an authentication endpoint, with the server responding to a successful request with a token. Alternatively, you could implement an endpoint from which a client can GET
a token, which requires that they provide their credentials either as query parameters or, better still, via the headers.
The purpose of this article is to explain authentication tokens rather than the basic username / password authentication mechanism, so let’s assume we already have the following and we’ve already obtained the username
and password
from the request:
User.findOne({ username: username }, function(err, user) {
if (err) {
// user not found
return res.send(401);
}
if (!user) {
// incorrect username
return res.send(401);
}
if (!user.validPassword(password)) {
// incorrect password
return res.send(401);
}
// User has authenticated OK
res.send(200);
});
Next, we need to respond to a successful authentication attempt with a JWT token:
var expires = moment().add('days', 7).valueOf();
var token = jwt.encode({
iss: user.id,
exp: expires
}, app.get('jwtTokenSecret'));
res.json({
token : token,
expires: expires,
user: user.toJSON()
});
You’ll notice the jwt.encode()
function takes two parameters. The first is an object which will form the body of the token. The second is the secret string we defined earlier. The token is constructed using the previously described iss
and exp
fields. Notice that Moment.js is used to set the expiration to 7 days from now. The res.json()
method is used to return a JSON representation of the token to the client.
Verifying the Token
In order to verify the JWT, we need to write some middleware which will:
- Check for an attached token.
- Attempt to decode it.
- Check the validity of the token.
- If the token is valid, retrieve the corresponding user record and attach it to the request object.
Let’s start by creating the bare bones of the middleware:
// @file jwtauth.js
var UserModel = require('../models/user');
var jwt = require('jwt-simple');
module.exports = function(req, res, next) {
// code goes here
};
For maximum flexibility, we’ll allow the client to attach a token in one of three ways – as a query string parameter, a form body parameter, or in an HTTP header. For the latter, we’ll use the header x-access-token
.
Here’s the code, which goes in our middleware, that attempts to retrieve the token:
var token = (req.body && req.body.access_token) || (req.query && req.query.access_token) || req.headers['x-access-token'];
Note that in order to access req.body
we need to have attached the express.bodyParser()
middleware first.
Next, let’s try to decode the JWT:
if (token) {
try {
var decoded = jwt.decode(token, app.get('jwtTokenSecret'));
// handle token here
} catch (err) {
return next();
}
} else {
next();
}
If the decoding process fails, the JWT Simple package will throw an exception. If this happens, or if no token has been provided, we simply call next()
to continue processing the request – it just means we haven’t identified the user. If a valid token exists and is decoded, we should end up with an object with two properties – iss
containing the user ID, and exp
with an expiration timestamp. Let’s check the latter first, and reject the token if it has expired:
if (decoded.exp <= Date.now()) {
res.end('Access token has expired', 400);
}
If the token is still valid, we can retrieve the user and attach it to the request object as shown below.
User.findOne({ _id: decoded.iss }, function(err, user) {
req.user = user;
});
Finally, attach the middleware to a route:
var jwtauth = require('./jwtauth.js');
app.get('/something', [express.bodyParser(), jwtauth], function(req, res){
// do something
});
Or, perhaps attach it to a bunch of routes:
app.all('/api/*', [express.bodyParser(), jwtauth]);
Our middleware now examines requests looking for a valid token, and if one exists, attaches a user object to the request. It should be fairly trivial now to build some simple middleware to deny a request without a valid token, though you may wish to build that into the same piece of middleware.
That’s the server side element of the token approach. In the next section, we’ll look at how tokens work on the client side.
The Client
We’ve provided a simple GET
endpoint for obtaining an access token. It’s straightforward enough that we probably don’t need to go over the details – just make a call, passing the username and password (from a form, perhaps) and if the request is successful, store the resulting token somewhere for later use.
What we will look at in more detail is attaching the token to subsequent calls. One way to do this is to use jQuery’s ajaxSetup()
method. This can be used for straightforward Ajax calls, or for front end frameworks which use Ajax under the hood to communicate with the server. For example, suppose we put our access tokens in local storage using window.localStorage.setItem('token', 'the-long-access-token')
; we can attach tokens to all calls via the headers like this:
var token = window.localStorage.getItem('token');
if (token) {
$.ajaxSetup({
headers: {
'x-access-token': token
}
});
}
Put simply, this will “hijack” all Ajax requests and, if there’s a token in local storage, it will attach it to the request using the x-access-token
header.
This doesn’t handle token expiration, but that ought to be relatively straightforward. You’ll remember that we returned an expiration timestamp with the token. Additionally, you may wish to have the server notify the client of an expired token using headers which indicate they must re-authenticate.
Using with Backbone
Let’s apply the approach in the previous section to a Backbone application. The simplest way to do this is to globally override Backbone.sync()
as shown below.
// Store "old" sync function
var backboneSync = Backbone.sync
// Now override
Backbone.sync = function (method, model, options) {
/*
* "options" represents the options passed to the underlying $.ajax call
*/
var token = window.localStorage.getItem('token');
if (token) {
options.headers = {
'x-access-token': token
}
}
// call the original function
backboneSync(method, model, options);
};
Additional Security
You could add an additional layer of security by storing a record of issued tokens on the server, then verifying them against that record on each subsequent request. This would prevent a third-party from “spoofing” a token, and also allows the server to invalidate a token. I won’t cover that here, but it ought to be relatively straightforward to implement.
Summary
In this article we’ve looked at some approaches to authentication on an API, looking specifically at JSON Web Tokens. We’ve used Node with Express to write a basic working implementation of the technique, and looked at how to use it client-side using Backbone as an example. The code for this article is available on GitHub.
There is more to the specification which we haven’t fully implemented, such as “claims” on resources, but what we have done is used the basic proposal to build a mechanism for exchanging credentials for an access token, in this case between the client and server of a JavaScript application.
Of course you could apply this approach to other technologies – for example a Ruby or PHP backend, or an Ember or AngularJS application. Alternatively, you could adopt it for mobile applications. For example, by using web technologies in conjunction with something like PhoneGap, using a tool such as Sencha or as a fully native application.