JWT Authentication with micro

These past few days, I've been working on two NPM modules. They are micro-http-router and micro-http-router-jwt. The former, is a router library for micro that lets you more clearly define your microservice's routes. The latter is a middleware library for the router that allows you to protect your endpoints with JWT authentication.

Today, I'll be showing you how to wire it all together to make a simple microservice that generates and validates your tokens. This won't be an effective or good microservice - I'll simply be showing you how to make the tools work together.

Don't know anything about JWTs? The JWT.io introduction is a good place to start.

You should have Node.js 8.X.X installed if you plan on following along, as these libraries use new ES6 features.

Let's get started!

Step 1: Install dependencies and start building the microservice

Create a directory and enter it. Initialize your project with npm init. Follow the steps, and once you've filled everything out, start installing dependencies:

npm install --save micro
npm install --save micro-http-router
npm install --save micro-http-router-jwt
npm install --save jsonwebtoken

Let's install micro-dev as a dev dependency:

npm install --save-dev micro-dev

Update your package.json so that you have the following two scripts:

"scripts": {
  "start": "micro",
  "dev": "micro-dev"

Let's add an index.js file to the root project directory next, and copy the following inside of it:

const {send, json} = require('micro');
const jwt = require('jsonwebtoken');
const Router = require('micro-http-router');

const router = new Router();
router.get('/', async (req, res) => {
    send(res, 200, 'Hello, World');

module.exports = async (req, res) => router.handle(req, res);

Awesome! Now, you should be able to start micro-dev by executing npm run dev. Make sure that it runs successfully and you see "Hello, World" when submitting a GET request to the service.

Step 2: Add a JWT

Let's set up our secret key first:

const secret = 'I am super secret, yes I am';

Nice. Let's set up a route to generate tokens.

Add a new POST handler to the router:

router.post('/login', async (req, res) => {
    const body = await json(req);

    if (!('username' in body) || !('password' in body)) {
        send(res, 400, 'Please provide a username and password.');
    } else {
        send(res, 200, `Hi, ${ body.username }!`);

This route will parse the JSON request body, validate that the object contains a username and password, and if all is good, will reply 'Hi, [username]!' Give it a test and you should see similar results.

Now that we have a good POST request and we know we're getting our username and password as parameters, let's generate a token and send that instead of a friendly greeting.

Replace your send call that sends the greeting with the following:

const token = jwt.sign({ username: body.username }, secret);
send(res, 200, { token: token });

If all is well, now you should receive a JSON blob like the following from your microservice:

    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InRlc3R1c2VyIiwiaWF0IjoxNTA1MTYzMzY3fQ.7pzIV2VgIhqwAoblQPYdub1JPGPoGZpXkiXTSNQW6c8"

And that's your token. Yay! Now, let's add a protected route.

Step 3: Adding a protected route

Let's add micro-http-router-jwt to the file. Add the following to your require block at the top of the file:

const { setupJWT, verifyToken } = require('micro-http-router-jwt');

Below your secret key variable, add the following:

setupJWT({ secret: secret });

Boom. That's all the setup you have to do for that. Let's add a new protected GET request:

router.get('/protected', verifyToken, async (req, res) => {
    send(res, 200, `Hello, ${ req.token.username }`);

Notice the verifyToken in between the route and the request handler. Micro-http-router supports a single level of middleware for this exact purpose. It will automatically parse your claims from the token and stick them in the req.token object. Since we added the username as a claim, it will be accessible on our token.

If you send a standard GET request with no headers to /protected you should receive a 401 Unauthorized status code. However, add the following header (replacing [token] with your token, of course):

Authorization: Bearer [token]

And then send a GET request to the service, and now, you should receive a successful request, and a personalized greeting to boot.

Step 4: That's it

Really. That's it. You're done. 30 lines of code later and you have 3 routes defined. One generates tokens, one accepts and parses the token, and the other just says 'Hello, World.'

You can view and download the example code on GitHub here: https://github.com/protocol114/micro-jwt-example