Monday evening we had a particularly nasty outage: JWT authentication was broken, preventing anyone from using our HTTP API to publish data. The reason we didn’t catch this early on is because our manual test scripts turned out to be broken (reporting auth success when auth had failed.. yeesh!), and there was no authentication coverage in our external monitoring to fall back on.

In a perfect world, our external monitoring would test authentication. I’m happy to report that we are now doing this with Runscope! Getting this to work right was a little tricky since we use JWT, but it was made possible thanks to Runscope’s scripting feature.

First, a little about Runscope

Runscope makes API monitoring easy. You can create sophisticated tests, with multiple HTTP calls per test, variable extraction for subsequent calls, and response data validation. You can then set these tests to run on a regular interval, reporting failures via email or through several third-party integrations (e.g. Slack). Overall, Runscope is far more powerful than a typical “ping”-style monitoring system.

Initial Script

As if Runscope didn’t do enough already, it also supports executing arbitrary JavaScript prior to running tests. This can be used to create variables that should be used by the test calls. To use it, just click the Initial Script tab and fill in some code:

runscopejwt1

The catch is that the environment is very minimal and does not include Node.js foundation modules, so you’ll need to work mostly in pure JavaScript. A few libraries are provided though. See the docs for more info.

Implementing JWT

Thankfully, Runscope’s scripting environment includes CryptoJS, so implementing JWT doesn’t take too much code. Below is the code we’re using, inspired by node-jwt-simple:

var base64urlEscape = function (str) {
  return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
};

var jwt_encode = function (payload, key) {
    var header = { typ: 'JWT', alg: 'HS256' };
    var segments = [];
    segments.push(base64urlEscape(btoa(JSON.stringify(header))));
    segments.push(base64urlEscape(btoa(JSON.stringify(payload))));
    var hash = CryptoJS.HmacSHA256(segments.join('.'), key);
    segments.push(base64urlEscape(hash.toString(CryptoJS.enc.Base64)));
    return segments.join('.');
};

var token = jwt_encode({
    iss: '{fanout realm id}',
    exp: Math.floor(new Date().getTime() / 1000) + 3600
}, CryptoJS.enc.Base64.parse('{fanout realm key}'));

variables.set('token', token);

We define a jwt_encode() method that takes two arguments: a claim payload and a key. This interface is typical of JWT libraries, such as node-jwt-simple and PyJWT.

Fanout uses Base64-encoded binary keys, so we parse the key with CryptoJS’s Base64 parser (needed to produce a WordArray object, which is what CryptoJS uses for binary data). If you use a string key then you can just pass it directly.

The last line of code sets a Runscope variable called token that contains the token value. That allows us to refer to it using templating notation when configuring test calls:

runscopejwt2

Now that this is in place, we hope to find out about authentication issues well before our customers do!

Feel free to use the code in this post to support JWT in your own Runscope tests.