Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Basic Authentication for json-server

DZone's Guide to

Basic Authentication for json-server

This article will share an approach for adding authentication to a json-server using an Express middleware component called Passport.

· Security Zone ·
Free Resource

Protect your applications against today's increasingly sophisticated threat landscape.

Summary

In my last DZone article on Zero Code REST with json-server I showed you how to stand up a REST server with minimal effort and no coding.

While this approach may work well in many situations, you often find that you'll need to extend capabilities, so you'll soon roll up your sleeves and start bending some code. The fun stuff!

This article will share an approach for adding authentication to a json-server using a simple, unobtrusive Express middleware component called Passport.

Architecture

Most sites require users to register and then use their credentials for access. Passport provides the layers necessary for authenticating claims made by users logging in to the site.

User Registration

User Registration

Basic Authentication is the simplest form of authenticating users, consisting of a username and a secret password. Being the simplest, it's arguably also the weakest form and vulnerable to intercept and exploitation when not used with secure transports.

The intent of this article is to share how we can start moving from plain text unauthenticated methods of communicating with our json-server to more robust solutions. Passport provides an extensible set of plugins known as strategies which will help you to do just that. At the time of writing this article, there are nearly 500 authentication strategies to choose from including:

  • Basic Authentication

  • Facebook

  • Twitter

  • Google

  • LinkedIn

Strategies can range from verifying a username and password credentials, delegated authentication using OAuth (using Facebook or Twitter), or federated authentication using OpenID.

Passport Strategy

Passport strategies are generally of the following form:

passport.use(new Strategy(
  function(username, password, cb) {  // <1>
    db.users.findByUsername(username, function(err, user) {  // <2>
      if (err) { return cb(err); }  // <3>
      if (!user) { return cb(null, false); }  // <4>
      if (user.password != password) { return cb(null, false); } // <5>
      return cb(null, user);
    });
  }));

Example Passport Strategy

  1. Authentication credentials

  2. Callback to verify access

  3. Error handling

  4. User Verification

  5. Password authentication

We'll now integrate passport authentication with the json-server we created in the last article.

CAUTION: For the sake of brevity and focusing on authentication, this example doesn't use TLS as any real-world application should.

Configuring User Authentication

Passport author Jared Hanson provides us with many a good example of Strategy implementation; and, as good OO practitioners we'll follow the mantra to reuse rather than reinvent, leveraging his Basic Authentication example.

Our new folder hierarchy now includes a db folder, simulating a database containing authentication credentials.

Image title

Passport authentication support

index.js Listing

exports.users = require('./users');

users.js Listing

var records = [
    { id: 1, username: 'jack', password: 'secret', displayName: 'Jack', emails: [ { value: 'jack@example.com' } ] }
  , { id: 2, username: 'jill', password: 'birthday', displayName: 'Jill', emails: [ { value: 'jill@example.com' } ] }
];

exports.findByUsername = function(username, cb) {
  process.nextTick(function() {
    for (var i = 0, len = records.length; i < len; i++) {
      var record = records[i];
      if (record.username === username) {
        return cb(null, record);
      }
    }
    return cb(null, null);
  });
}

Configuring npm Packages

In our earlier Zero Code example, we were able to start the json-server with static
assets in our project hierarchy. As we add custom code, we'll need a way of letting npm
know about our project dependencies. This is accomplished by adding package.json.

{
  "name": "custom-json-server",
  "version": "1.0.0",
  "description": "Custom json-server",
  "main": "server.js",
  "dependencies": {
    "json-server": "^0.12.1",     // <1>
    "passport": "^0.2.2",         // <2>
    "passport-http": "^0.3.0"     <
  },
  "devDependencies": {
    "json-server": "^0.12.1"

  },
  "scripts": {
    "serve": "node server.js --watch ./json/db.json",// <3>
    "test":       "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "json",
    "api"
  ],
  "author": "M. Dresdner",
  "license": "MIT"
}

Our package.json includes:

  1. json-server dependencies.

  2. Passport dependencies.

  3. The script for starting our server.

After creating the package.json we'll need to install the required packages. This is accomplished by running the npm install command, or the short form, npm i.

npm install

npm install

You should see npm pull down all the required packages and save them into the folder, node_modules.

With json-server and passport packages installed we turn our attention toward creating our
custom code json-server.

Configuring Server.json

Our customized rendition of json-server allows for additional middleware products in our json-server implementation. To add the Basic Authentication support, we start with the sample code described in the Module section of the json-server website and add our references to Passport.

const jsonServer = require('json-server')
const server = jsonServer.create()
const path = require('path')
const router = jsonServer.router(path.join(__dirname, './json/db.json'))

const middlewares = jsonServer.defaults()

var passport = require('passport');   // <1>
var Strategy = require('passport-http').BasicStrategy;
var db = require('./db/index');


// Configure the Basic strategy for use by Passport.    // <2>
passport.use(new Strategy(
  function(username, password, cb) {
    db.users.findByUsername(username, function(err, user) {
      if (err) { return cb(err); }
      if (!user) { return cb(null, false); }
      if (user.password != password) { return cb(null, false); }
      return cb(null, user);
    });
  }));

// http -a jack:secret localhost:3000/email     // <3>
server.get('/email',
  passport.authenticate('basic', { session: false }),
  function(req, res) {
    res.json({ username: req.user.username, email: req.user.emails[0].value });
  });

server.use(middlewares)


// http localhost:3000/wines
server.use(router)
server.listen(3000, () => {
  console.log('JSON Server is running on 3000')
})

server.js

In the above code, we are:

  1. Bringing in Passport support.

  2. Configuring our Basic Authentication strategy.

  3. Adding a new Express handler for authenticating a new request.

With the integration of Passport with json-server complete, Passport should now be configured to validate a new route request to /email.

Let's start up the json-server and see if we broke anything in our latest refactorings.

npm run serve

Start json-server

Since we now have a custom json-server implementation, we start it using the script (above) that we added earlier in our package.json.

When the json-server starts you see the message JSON Server is running on 3000.

We can now send some test messages with httpie.

http localhost:3000/wines

Regression Testing

If all went well, you should see a familiar response to the all wines request from json-server.

http -a jack:secret localhost:3000/email

Authentication Test

If all went well, you should see the following response:

HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 55
Content-Type: application/json; charset=utf-8
Date: Mon, 30 Apr 2018 18:06:27 GMT
ETag: W/"37-lew270BaMoufxDP6PMJe/Pp2Pys"
X-Powered-By: Express

{
    "email": "jack@example.com",
    "username": "jack"
}

Authentication Response

Next, we'll mess with the password and try again.

http -a jack:Xsecret localhost:3000/email

Authentication Failure

As expected, we get an authentication failure.

HTTP/1.1 401 Unauthorized
Connection: keep-alive
Content-Length: 12
Date: Mon, 30 Apr 2018 18:08:45 GMT
WWW-Authenticate: Basic realm="Users"
X-Powered-By: Express

Unauthorized

Authentication Failure Response

That was useful and a bit of fun, for your next steps I expect you may want to try another Passport Strategy like Google or LinkedIn authentication!

Passport's pluggable authentication greatly simplifies a complex, arduous, and oft times error-prone process.

I hope you've enjoyed reading this article as much as I have writing it and am looking forward to your feedback.

Rapidly detect security vulnerabilities in your web, mobile and desktop applications with IBM Application Security on Cloud. Register Now

Topics:
json ,authentication ,json-server ,security ,web application security

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}