Token Based Authentication using Credentials in NodeJS
Tags: authentication, node, passportjs, jwt
Published: 2023-12-23 06:58:22

Hello coders,
I am going to talk about the modern way of token-based authentication using NodeJS, so if you are looking for a start or want to build your own then you have come to right place.

In this post I am going to implement the basic token-based authentication system using ExpressJS, PassportJS and other helpful  libraries.

So let's start

First run the command
`npm init -y`
It will create a package.json file for you with all the necessary defaults

Now we need to install the following packages:
1. ExpressJS
2. PassportJS
3. HelmetJS
4. Nodemon
5. Passport-JWT
6. Passport-Local
7. JsonWebToken
8. Cookie-Parser

to install these packages run the following command
`npm i express helmet jsonwebtoken passport passport-jwt passport-local nodemon cookie-parser`

After this we need to make some changes in the package.json file so that we don't have to run the script manually everytime to see the changes. That's exactly where `nodemon` comes into the picture. It looks for the changes into the specific file and if there is any change it restarts the server. 

Let's add the following script into the scripts section in the `package.json` file

`"start": "nodemon server.js"
`
Now let's move ahead. Following is the file & folder structure we are going to follow to organize our code


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qfgubritfmbnkhsamse9.png)

So far so good. We have organized our code. Now it's time to get our hands dirty by writing some code.

We will start with `server.js` file

```
const express = require("express");
// create app instance from express
const app = express();

// specify the port number for your app
const PORT = 3000;

// home page route
app.get("/", (req, res) => res.send("Hi"));

app.listen(PORT, (req, res) => {
	console.log(`Connected on http://localhost:${PORT}`);
});
```

Now if you run `npm run start` in the terminal then your server will start and run on the port 3000.


> **Note**: We are not using database at this point to save ourselves from additional database setup. We are using a simple array of objects user model.
If you need this with database then comment down. I will create another post with the database setup.


So let's create a user with array of objects in `users.js` file in the models folder

```
const users = [
	{ username: "Prince", password: "password1" },
	{ username: "Kathan", password: "password2" },
	{ username: "Srivinay", password: "password3" },
	{ username: "Siddharth", password: "password4" },
];
module.exports = users;
```
Add below html page for login route in the `login.html` file in public folder

```
<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>NodeJS Authentication</title>
	</head>
	<body>
		<h1>This is the HtML sent by the server</h1>
		<a href="/secret">Show Me!</a>
		<a href="/auth/google">Google Login</a>
		<a href="/auth/logout">Logout</a>
		<!-- <img src="./logo.png" alt="logo img" /> -->
	</body>
</html>

```



After this we are going to edit our `passport.js` file to add the passportjs with  Token based authentication to our app.

```
const passport = require("passport");
const JwtStrategy = require("passport-jwt").Strategy;
const ExtractJwt = require("passport-jwt").ExtractJwt;

const users = require("./model/users");

// function to extract the cookie from the response header
var cookieExtractor = function (req) {
	var token = null;
	if (req && req.cookies) {
		token = req.cookies["secure_token"];
	}
	return token;
};

// function to use the jwt as strategy to authenticate the users
function passportAuth() {
	try {
		passport.use(
			new JwtStrategy(
				{
					jwtFromRequest: ExtractJwt.fromExtractors([cookieExtractor]),
					secretOrKey: "my_secret_key",
				},
				function (jwt_payload, done) {
					const user = users.find((user) => jwt_payload.username === user.username);
					if (!user) {
						return done(null, false);
					}

					return done(null, user);
				}
			)
		);
	} catch (error) {
		throw error;
	}
}

module.exports = {
	passportAuth,
};

```

In above code we are first importing the necessary packages then I have create a `cookieExtractor` function which is used to extract the cookies from the headers and pass the value of the cookies to passport. Please note that we have used cookies specially to save the authentication tokens so that we don't have to include them  seperately in each request if which is the case if we save them in the localStorage or sessionStorage. Sure storing the tokens in cookies have some downside and is susceptible to xsrf but it can be  avoided by setting the cookies to httponly, secure and maxAge properties.

After that we using the `passportjs` middleware which runs on every request that is beign made to our server and extract the cookies from header. It then decodes the token extracted from cookie using a secretKey which you have to set in `.env` file and extracts the user information from tokens. We are verifying here based on the username only for now to keep things simple. If it founds the user then it returns the user's properties or else returns false.

Now let's create the login routes

Add following code to `loginRoutes.js` file in the routes folder

```
const express = require("express");
const loginRouter = express.Router();

//importing the getLoginForm and postLoginDetails from controllers/loginController.js file
const { getLoginForm, postLoginDetails } = require("../controllers/loginController");

// router for the GET method of the login form
loginRouter.get("/", getLoginForm);

// route for the POST method of the login form
loginRouter.post("/", postLoginDetails);

module.exports = loginRouter;
```


After this we are going to edit the `loginController.js` file in the controllers folder

```
const path = require("path");
const jwt = require("jsonwebtoken");

const users = require("../model/users");

// function to serve the login page to the user
const getLoginForm = (req, res) => {
	res.sendFile(path.join(__dirname, "..", "public", "login.html"));
};

// function to handle the login request
const postLoginDetails = (req, res) => {
	// extracting the username and password from the body of the request
	const { username, password } = req.body;
	const user = users.find((user) => user.username === username);

	// sending 404 error along with the message if user is not found in the database
	if (!user) {
		return res.status(404).json({ message: "INCORRECT_USERNAME" });
	} else {
		// generating the token and with the information and sending it to the user on the client end
		const token = jwt.sign({ username: username }, "my_secret_key");

		// setting the token in the cookie so that we don't need to include it manually in each request
		res.cookie("secure_token", token, {
			httpOnly: true, // Ensures cookie cannot be accessed by JavaScript
			maxAge: 1000 * 60 * 60, // Expire in 1 hour,
		});

		// returning the response containing the token and the message
		return res.json({
			message: "sucessfully authentiacted",
			token,
		});
	}
};

// function to add the authentication on the selective routes using the jwt strategy
function authenticate(req, res, next) {
	// Use Passport to authenticate the request using the "jwt" strategy
	passport.authenticate("jwt", { session: false }, (err, user) => {
		console.log(user);
		if (err) next(err); // If there's an error, pass it on to the next middleware
		if (!user) {
			// If the user is not authenticated, send a 401 Unauthorized response
			return res.status(401).json({
				message: "Unauthorized access. No token provided.",
			});
		}
		// If the user is authenticated, attach the user object to the request and move on to the next middleware
		req.user = user;
		next();
	})(req, res, next);
}

module.exports = {
	passportAuth,
	authenticate,
};
```


In above code we are importing the necessary packages. After that we are serving the login.html file which is served to the user after he visits the `/login` route in the `getLoginForm` function.

Now moving on we have `postLoginDetails` function which first extracts the user's username and password from the request, then looks for the that user in the users model. If it founds the user then it creates a token using the users information and then sets that token in the cookie. We have already talked about why we are using cookies approach. And finally send the successful message to the client side. In case if user is not found then also we are sending `"INCORRECT_USERNAME"` message to the client side.

After that we have added the `authenticate` function middleware which you can use to implement the authentication on selective routes. It first extracts the JWT tokens with the help of passportjs and looks for user. If user is found then it returns the user or returns the error message.


Now going back to `server.js` file and adding the passportjs middleware and login routes

Modifying our existing file like this

```
const express = require("express");
const helmet = require("helmet");
const passport = require("passport");
const cookieParser = require("cookie-parser");

const { passportAuth } = require("./passport");
const loginRouter = require("./routes/loginRouter");

// create app instance from express
const app = express();

// specify the port number for your app
const PORT = 3000;

// using this helmet middleware to set extra attributes on the request headers
app.use(helmet());

// middleware to parse the cookies in the incoming requests
app.use(cookieParser());

// including the passportjs middleware
passportAuth();

app.use(express.urlencoded({ extended: false }));

// initialising the passportjs middleware
app.use(passport.initialize());

// home page route
app.get("/", (req, res) => res.send("Hi"));

// login route
app.use("/login", loginRouter);


// secret page route which requires the user to be authenticated
app.get("/secret", authenticate, (req, res) => {
	res.send("secret value is 19");
});
```

Here we have made couple of changes. We have added the helmet middleware so set extra necessary headers, cookieParser middleware to parse the cookies from the requests and added the passportjs middleware. After that we are intializing the passportjs middleware. Lastly we are creating the routes for the /login route.

At this point we have made all the heavy lifting. 
Now if you go the `http://localhost:3000/` route you should see "Hi".

If you go to `http://localhost:3000/secret` endpoint then you will get the error message saying "Unauthorized access. No token provided". It means our authentication is working. 

Now go to `http://localhost:3000/login` and you will see the two input fields in which first field is for username and last field is for password. 

When you enter any username listed in the users model you will get token and successful message. If you enter wrong username then you will get error. 

After this if you again go to `http://localhost:3000/secret` endpoint you will get you secret endpoint's content which in our case is 'Your secret value is 19'.


Woo hoo! You have successfully added the modern token based authentication in your app.

I hope this article gave you the basic knowledge about implementing  authentication logic in your app.

There are still many areas of improvement in the code like verifying the password along with username, adding database support , signup process and many more. 
So try them on your own and if stuck, comment below, I will create another article along with github repo for you.


Happy Coding!! :)