There are times when we need to authenticate the user before giving him access to specific pages on our website.This authentication ensures that user has access to only those data that he has privileges on.
An entry level programmer would just fetch username and password stored in database at the time of login and if they match would give him access , which is not wrong but only half a step in the process of authentication.
It is also necessary to ensure that of all the data stored in database only data related to user is shown.
This can be achieved in two ways:
1- Token based authentication(using jwt-jsonWebToken)
2- Session based authentication
Today we will talk and implement token based authentication in NodeJs.
1- Install following packages and dependencies which we are going to work with -
we will build our server with express , jsonWebToken is library used for creating and verifying tokens and dotenv for storing our secrets in .env file that will not be visible to others.
npm install --save express jsonWebToken dotenv
npm install -D nodemon
2- .env file contains two things:
1-SECRET_ACCESS_TOKEN
2-REFRESH_TOKEN
Secret access token is a secret code that we use to verify ourself as creator of tokens and same secret is used while verifying tokens too.
Refresh tokens are used to create new access token once they expire.
(We will not be implemeting refresh tokens for now)
e.g. -
These tokens can be created randomly using encrypt library in nodejs.
SECRET_ACCESS_TOKEN="9c2fa79645d6210a31e1cfb8435f06d5c10d9c7e5e80069e91a52fc870b05409"
SECRET_REFRESH_TOKEN="f1f0e9c17f296226431f4468ed329781b3b774583c86462247576c2d92f01900"
3-Create a basic server in app.js file containing following code and start the server with nodemon.
require("dotenv").config();
const express = require("express");
const app = express();
const jwt = require("jsonwebtoken");
app.get("/", (req, res) => {
res.status(200).send("This is homepage!");
})
app.listen(process.env.PORT, () => {
console.log("Server started!");
});
4- Now we will create a new route("/login) that will check for user id and password at the time of login and generate token for the same user that we will pass in headers with every request we make ever after.
After authentication is successful we go ahead and create a token using jwt.sign(user,token) , it signs the token with the user we enter and will return the same user when we will verify the token.
If authentication fails , we tell user to enter correct credentials.
const express = require("express");
const app = express();
const jwt = require("jsonwebtoken");
require("dotenv").config();
app.get("/", (req, res) => {
res.status(200).send("This is homepage!");
})
app.post("/login", (req, res) => {
const {username , password} = req.body; //get username and password that we passed client side.
//Authenticate with username and password stored in database.Do it yourself!
if(Authentication is successfull)
{
//Create a token signed by username
const user = {name : req.body.username}
const accessToken = jwt.sign(user , process.env.SECRET_ACCESS_TOKEN);
res.send({accessToken : accessToken});
}
else
{
res.send("Wrong Credentials!");
}
})
app.listen(process.env.PORT, () => {
console.log("Server started!");
});
5- Now we have created a token and sent it to client side , this token will be passed in headers with every request to authenticate for the user and show him data related to user only.
For verifying we will create a middleware(autenticateToken).It takes access token from the headers that is passed client side and verifies it using jwt.verify(token , secret-access-token , (error,user)=>{})
.The callback returns user info that is saved in res so that it is accessible in our route;
if(Authentication is successfull)
{
//Create a token signed by username
const user = {name : req.body.username}
const accessToken = jwt.sign(user , process.env.SECRET_ACCESS_TOKEN);
res.send({accessToken : accessToken});
}
else
{
res.send("Wrong Credentials!");
}
})
const authenticateToken = (req,res,next) =>{
// We will pass token in the following format => "token"
const accessToken = req.headers['authorization'];
if (accessToken == null)
return res.sendStatus(401);
jwt.verify(accessToken , process.env.SECRET_ACCESS_TOKEN,(err,data)=>{
if (err) return res.status(402).send(err);
req.user = data;
next();
})
}
app.listen(process.env.PORT, () => {
console.log("Server started!");
});
6 -It verifies the token and in callback return error and the user info that we can use to filter out contents from our database , since here we are not connected to a database we will create an array of posts to check if token works.
const posts = [{
{username : "Bob" , title:"superman" , serial : 1},
{username : "Allen" , title:"Batman" , serial : 2},
{username : "Ray" , title:"Iron Man" , serial : 3}
}];
7 - We create a new route("/posts") to test our tokens and add this middleware in our "/posts" route and then filter our content out with our username.
const posts = [{
{username : "Bob" , title:"superman" , serial : 1},
{username : "Allen" , title:"Batman" , serial : 2},
{username : "Ray" , title:"Iron Man" , serial : 3}
}];
app.get("/posts", authenticateToken , (req,res)=>{
res.json(posts.filter((post)=> post.username == req.user.name));
});
const authenticateToken = (req,res,next) =>{
// We will pass token in the following format => "token"
const accessToken = req.headers['authorization'];
if (accessToken == null)
return res.sendStatus(401);
jwt.verify(accessToken , process.env.SECRET_ACCESS_TOKEN,(err,data)=>{
if (err) return res.status(402).send(err);
req.user = data;
next();
})
}
app.listen(process.env.PORT, () => {
console.log("Server started!");
});
Output:
if we passed username as Bob we get :
{username : "Bob" , title:"superman" , serial : 1}
This is how we authenticate using tokens and filter out data of our user.
This token can also be set for automatic expiry of 1 min(or as we like) by passing in an expiry time jwt.sign(user,SECRET_ACCESS_TOKEN , 3600).
Complete Code :
const express = require("express");
const app = express();
const jwt = require("jsonwebtoken");
require("dotenv").config();
const posts = [{
{username : "Bob" , title:"superman" , serial : 1},
{username : "Allen" , title:"Batman" , serial : 2},
{username : "Ray" , title:"Iron Man" , serial : 3}
}];
app.get("/posts", authenticateToken , (req,res)=>{
res.json(posts.filter((post)=> post.username == req.user.name));
});
app.post("/login", (req, res) => {
const {username , password} = req.body; //get username and password that we passed client side.
//Authenticate with username and password stored in database.Do it yourself!
if(Authentication is successfull)
{
//Create a token signed by username
const user = {name : req.body.username}
const accessToken = jwt.sign(user , process.env.SECRET_ACCESS_TOKEN);
res.send({accessToken : accessToken});
}
else
{
res.send("Wrong Credentials!");
}
})
app.get("/", (req, res) => {
res.status(200).send("This is homepage!");
})
const authenticateToken = (req,res,next) =>{
// We will pass token in the following format => "token"
const accessToken = req.headers['authorization'];
if (accessToken == null)
return res.sendStatus(401);
jwt.verify(accessToken , process.env.SECRET_ACCESS_TOKEN,(err,data)=>{
if (err) return res.status(402).send(err);
req.user = data;
next();
})
}
app.listen(process.env.PORT, () => {
console.log("Server started!");
});
**Important -
We usually create an access token and refresh token seperately.Access token have an expiry that are refreshed by refresh token by creating a seperate function.**
Hope it helps!
Top comments (0)