REST — Token based authentication (jwt)

Separation of client and server

In REST architecture, the client and the server can be implemented independently without knowing each other. This independence of implementation gives freedom to change the client code without affecting the server and vice versa. Both the sides (client and server) knows what format of messages to send to each other and hence the independence is achieved.

Statelessness

Systems that follow the REST paradigm are stateless, meaning that the server does not need to know anything about what state the client is in and vice versa. For example, a RESTful server does not maintain user session. It is completely unaware whether a user is logged in or not.

Access tokens

When a client sends the login information (email and password), the RESTful server sends back an encrypted access token with an expiration time to the client if the email and password are valid. Now, when the same user needs some resources, it sends a request to the server attaching the same access token. The server verifies the token and if valid, sends back the requested data. Thus, server does not need to keep track or store the logged in users and can support millions of users simultaneously with high performance.

Implementing token based authentication

We will be using Node.js with Express and MongoDB with Mongoose in the reference code. Also, we will be using JWT — jsonwebtoken package (https://www.npmjs.com/package/jsonwebtoken).

const express = require('express');
const router = express.Router();
const password_hash = require('password-hash');
const jwt = require('jsonwebtoken');
const app = express();router.post('/api/user/validate', (req, res) => {
let curPass = req.body.password;
var pass_hash = "";
User.findOne({email: req.body.email},
(err, users)=>{
if(err){
res.json({success: false, msg: "Invalid", data: []});
}
if(users){
pass_hash = users.password_hash;
verification = password_hash.verify(curPass, pass_hash);
if(verification){
// token
const payload = {
role: users.role,
user_id: users._id
};
var token = jwt.sign({
exp: Math.floor(Date.now() / 1000) + (60 * 60), // 1 hour
data: payload
}, 'top-secret');
res.json({success: true, msg: "Login Successful", token: token});
}
else{
res.json({success: false, msg: "Invalid password", data: []});
}
}
else{
res.json({success: false, msg: "Invalid email id", data: []});
}
});
});
{
"success": true,
"msg": "Login Successful",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAasdagdfxg"
}
https://www.xyz.com/api/userREQUEST HEADERS
token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAasdagdfxg
// jwt middleware to verify token
router.use((req, res, next)=>{
var token = req.body.token || req.headers['token'];
if(token){
jwt.verify(token, 'top-secret', (err, decoded)=>{
if(err){
return res.json({success: false, msg: 'Token not valid or expired', data: []});
}
else{
req.decoded = decoded;
next();
}
});
}
else{
return res.status(403).json({success: false, msg: 'No token provided', data: []});
}
})
// get logged user information
router.get('/user', (req, res) => {
User.find({_id: req.decoded.data.user_id}, (err, user)=>{
if(!err){
res.json({success: true, msg: "", data: user});
}
else{
res.json({success: false, msg: "Invalid user", data: []});
}
}).select('-password_hash');
});
 https://github.com/adipixel/expense-tracker/blob/master/routes/api_route.js

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store