Newby Coder header banner

Adding Api to NodeJs Jwt Authentication Server

Adding an api in NodeJs express can be simple like declaring another route and assigning a function

Following is continuation of React Native Jwt Http Authentication

Since user registration is not implemented, following example provides user data by calling another service

These calls can be replaced with something like - calls to a database

NodeJs Jwt Authentication for Mobile applications

This has following parts:


Dependency

get-json package is used to get json data from Rest Apis

Run following command from project directory

npm install get-json --save

Project structure

No new files are added, so project structure is same as that of React Native Jwt Http Authentication

Implementation

User service

getUserInfo is modified to add a service call which returns random user data

This can be changed to a custom service call or database call if implemented

User data from the external service is not stored so it can return different data on subsequent calls for a user

Another method getMockPosts is added which also makes a service call to get some data as filler for user posts

Changes made from Nodejs Jwt Http Authentication is highlighted

var getJSON = require('get-json')
const jwt = require('jsonwebtoken')
const config = require('../config/config.json')

const users = [
  { id: 81623, username: 'test', password: 'test', firstName: 'Test', lastName: 'User', phone: '5235234132' },
  { id: 26858, username: 'ncuser', password: 'ncpassword', firstName: 'En', lastName: 'Cee', phone: '7685496767' }];

module.exports = {
    authenticate,
    getUserInfo
};

async function authenticate(username, password) {
    const user = users.find(u => u.username === username && u.password === password);
    if (user) {
        // use jwt module to sign a object containing id & username of a user and get a corresponding token
        // alternatively, whole user object can be passed, but some specific fields can be selected to make it lightweight
        const token = jwt.sign({id: user.id, username: username}, config.privateKey, { expiresIn: config.tokenExpirySla})
        // return token to be forwarded to client
        return {token:token};
    }
}

async function getUserInfo(req, res) {
  await getJSON(`https://randomuser.me/api/`, function(error, response){
    if(error != undefined || error != null) {
      res.status(400).json({ message: JSON.parse(error) });
    }
    else{
      var userdata = response.results[0];
      userdata.id = parseInt(Math.random() * 90000 + 10000);
      userdata.username = req.decoded.username;
      res.json({userdata : userdata});
    }
  });
}

async function getMockPosts(count, res) {
  await getJSON(`https://baconipsum.com/api/?type=meat-and-filler¶s=${count}&start-with-lorem=1`, function(error, response){
    if(error != undefined || error != null) {
      res.status(400).json({ message: JSON.parse(error) });
    }
    else{
      res.json({posts : response});
    }
  });
}

Controller

A route /initialPosts is added which is bound to getInitialPosts() methodwhich makes a service call to get 10 user posts with mock data

Additional routes also require authentication due to the authentication middleware (with no changes)

const express = require('express');
// get Router instance
const router = express.Router();
// variable to access exported methods of user_service.js
const userService = require('./user_service');

// set controller methods for routes
router.post('/authenticate', authenticate);
router.get('/getInfo', getInfo);
router.get('/initialPosts', getInitialPosts);

module.exports = router;

function authenticate(req, res, next) {
  console.log("authenticate ", req.body)
  // password is expected to be in base64 encoded form, which is decoded to send raw password to service
  var password = Buffer.from(req.body.password, 'base64').toString('ascii')
  userService.authenticate(req.body.username, password)
      // here 'user' is the object returned from authenticate() if not null, which contains 'id' of user
      .then(user => user ? res.json(user) :
          res.status(400).json({ message: 'Username or password is incorrect' }))
      // error is handled by global handler
      .catch(err => next(err));
}

// since this method is called after authentication middleware,
// the req (request) object has a 'decoded' field containing id of a user
function getInfo(req, res, next) {
  userService.getUserInfo(req, res)
}

// get 10 mock paragraphs as user posts
function getInitialPosts(req, res, next) {
  userService.getMockPosts(10, res);
}

Error Handler

Same as Nodejs Jwt Http Authentication

Authentication Middleware

Same as Nodejs Jwt Http Authentication

index.js - Server entry point

Same as Nodejs Jwt Http Authentication


Run instructions

Run following command from project directory

nodejs index.js

To test running in production

sudo NODE_ENV='production' nodejs index.js

To make it as accessible over a local network such as wifi, enter local ip address with command

nodejs index.js 192.168.43.34

Here, 192.168.43.34 is ip address

Testing using html file

Save following code in a file named test.html (or some other name with .html extension) in a directory

Replace url in case server is run in some ip address other that localhost

Open the file in a browser (double-click should do that)

<!doctype html>
<html>
  <meta charset="utf-8">
  <body>
    <div>
      <div>
      <h3>Login Form</h3>
      <table>
        <tr>
          <td><label for="id_username">Enter username</label></td>
          <td><input type="text" id="id_username"></td>
        </tr>
        <tr>
          <td><label for="id_username">Enter password</label></td>
          <td><input type="password" id="id_password"></td>
        </tr>
        <tr>
          <td><button id='btn'>Authenticate</button></td>
          <td><button id='infoBtn'>Get user info</button></td>
        </tr>
      </table>
      <pre><code id="output"></code></pre>
      </div>
    </div>
  </body>
  <script>
    output = document.getElementById('output')
    token = null
    function authReq() {
      output.innerHTML = ""
      username = document.getElementById('id_username').value
      password = document.getElementById('id_password').value
      const encoded =  btoa(password);
      fetch('http://127.0.0.1:4000/users/authenticate', {
        method: 'POST',
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({username: username, password: encoded})
      })
      .then(response => response.json())
      .then((responseJson) => {
        output.innerHTML =  JSON.stringify(responseJson)
        token = responseJson.token
      });
    }
    function getInfo() {
      output.innerHTML = ""
      const encoded = 'Bearer ' + token
      fetch('http://127.0.0.1:4000/users/getInfo', {
        method: 'GET',
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json',
          'Authorization': encoded
        },
      })
      .then(response => response.json())
      .then((value) => {
        output.innerHTML =  JSON.stringify(value, null, 4)
      });
    }
    document.getElementById('btn').onclick = authReq
    document.getElementById('infoBtn').onclick = getInfo
  </script>
</html>

Test with username and password set in server

cl-nodejs-server-jwt-auth-mock-data

Client Mobile Applications

Check how to connect the server with mobile applications