Build a Simple Node.js OAuth Server with JWT

Author: Shane Larson

In this article we will explain how to create a simple oauth token server using node.js.

In order for microservices to work, there needs to be a way for client applications to authenticate with the API. There are many methods for authenticating API's, but the Node.js community seems to have embraced JWT, or JSON Web Tokens. JSON web tokens is a natural choice for node because it is a JSON based standard (RFC 7519). With JWT, there is a common framework for creating access tokens that will store a small data payload. Using node.js, we can create a simple access token microservice that can be used by our other microservices as an authentication platform.

The OAuth pattern that I used was the Client Credentials Grant, which is a fairly simple workflow.

alt text

Authenticate with an API using a client_id and a client_secret

First, the client application needs to authenticate with the framework. It does this by passing a client_id and a client_secret. The client_id is essentially a user_id here, and the client_secret is the password. You will need to secure the secret within your application. In my example, the client_secret is not hashed within the database, but to add more security it would make sense to salt or hash the password in your authentication before you take the token server to production.

Create an access token and return the token to the client application

Once the clientid and clientsecret have been authenticated, and any authorization meta data has been returned from the database, we can create a JSON Web Token. Using the jsowebtoken package from NPM, we will sign the token with the client's authorization meta data payload and our private key. The token information is then passed back to the client application in the token server API response.

Verify the access token

When the client application wants to use a resource, we will need to verify that the client's token is valid. The token is passed to the token server to be verified. If it is successful, an appropriate response is relayed to the client app with the relevant authorization metadata for the client. At this point, the client application can make a decision within it's business logic to give access or not.

Use Cases

This was a very simple example implementation of an Access Token server. How can this be used to provide API security? The client application in the above example can be a microservice. Using this pattern you can actually serve authentication to multiple microservices simultaneously, providing a form of SSO for your API framework.

alt text

The Code

So this seems pretty awesome but also it must be a lot of coding right? Not at all. This is all very simple using Node.js, express middleware, a database, and JWT.

The git repo is located at the following location.
https://github.com/grizzlypeaksoftware/access_token_server. Feel free to use the software as you need for your applications. I am happy to accept pull requests from the open source community.

Before you start, it's important to know that this application is written to serve up as HTTPS. In order to run the applicaiton you will need to obtain an SSL certificate. I based this project off of the code from my previous blog post about creating a secure SSL server in node.js. Please feel free to review that project here… https://grizzlypeaksoftware.com/blog?id=JDcsPW2raSic6oc6MCYaM

Below we can see the code that was used to accomplish the task.

// dependencies
var express = require('express');
var https = require('https');
var fs = require("fs");
const bodyParser = require('body-parser');

// require the jsonwebtoken library for creating tokens
var jwt = require('jsonwebtoken');

// you need to have a .env file with your database 
// connection string and encryption key
// see .env_sample for a starting point.
require('dotenv').config();

// the authentication model class, uses mongo db 
// to authenticate a client
var model = require("./models/auth_model.js");

// Initialize express and use the bodyparser for 
// getting http post body data.
var app = express();
app.use(bodyParser.urlencoded());

// just a generic home page for a web portal if desired.
app.get('/', function(req,res){
    res.send("TokenServer");
});

// an API route for the authorization/authentication step
app.post('/authorize', function (req, res) {

    // get the client_id and secret from the client application
    var client_id = req.body.client_id;
    var client_secret = req.body.client_secret;  

    // authorize with the model, using promises
    model.authorize(client_id, client_secret).then(function(docs){

        var token_response = {};
        token_response.success = false;

        // if we have a user with the passed client_id and client_secret
        // then create a token
        if(docs && docs.length > 0){
            token_response.client_id = docs[0].client_id;
            token_response.success = true;

            // sign the token and add it to the response object
            var token = jwt.sign(token_response, process.env.SECRET);
            token_response.token = token;
        }

        // return the token back to the client application
        res.send(token_response);
    }).catch(function(err){

        // error handling goes here
        res.send(err);
    });
});

// this is an API call to verify the access_token when it's passed
app.post('/verify', function (req, res) {

    // get the token that was passed in the app.
    // might make more sense to put this in the header
    var token = req.header.authorization;

    // decode the token to verify
    var decoded = jwt.verify(token, process.env.SECRET, function(err, decoded){
        if(err){
            // if there is an error, the token is not valid!
            res.send({success:false});
        } else {
            // if no error, send the decoded token to the client with
            // authorization metadata payload
            res.send(decoded);
        }
    });
});

// file location of private key
var privateKey = fs.readFileSync( 'private.key' );

// file location of SSL cert
var certificate = fs.readFileSync( 'SSL.crt' );

// set up a config object
var server_config = {
    key : privateKey,
    cert: certificate
};

// create the server
var https_server = https.createServer(server_config, app).listen(2600, function(err){
    console.log("Node.js Express HTTPS Server Listening on Port 2600");
});

This node.js express application provides two api calls, Authorize and Verify, Using this code we can provide a reliable source of access tokens for our api framework with a minimum of overhead. It uses SSL to ensure that data transmitted over the wire is encrypted. The only thing that should be added at this time is a way to salt or hash the passwords, and perhaps a web admin portal for creating and managing client_id's, secrets and manage other aspects of the server.

I chose to run this server on port 2600 by default, but you could move it to port 443 or any other port that you need.

Thank you for reading this blog post! I hope that you found this code example helpful and useful for your applications.

Recent Articles

Shane Larson

Software Engineer - Solutions Architect

Builder of Tiny Cabins in Alaska


I'm a software engineer with years of professional experience in NodeJS, Solidity, React, C#, Python, JavaScript, Postgres, SQL Server, MongoDb.

My current focus is in the areas of Microservices, API Frameworks, Cloud Native Architecture, Robotics, and DevOps.

I work as a Solutions Architect in the financial industry.

I also build tiny cabins in Alaska.


Author of Building Microservices with NodeJs

Packt Publishing

Microservices enable us to develop software in small pieces that work together but can be developed separately, one of the reasons why enterprises have started embracing them. For the past few years, Node.js has emerged as a strong candidate for developing these microservices because of its ability to increase developers' productivity and applications performance. This video is an end-to-end course on how to dismantle your monolith applications and embrace the microservice architecture.

Powered by Contentful