Introduction
Authentication is the process of checking whether a user is who they claim to be. It is an important part of Node.js applications such as websites, dashboards, and REST APIs. In this chapter, you will practice authentication from the basics using Node.js, Express.js, passwords, sessions, and JSON Web Tokens (JWT). Each example is explained step by step so beginners can understand how user login systems work. Node.js Authentication practice questions with solutions help to understand the concepts.
Question 1: How do you create a basic login check in Node.js?
Problem
Create a simple Node.js program that checks whether a username and password are correct.
Solution
const username = "rahul";
const password = "12345";
const enteredUsername = "rahul";
const enteredPassword = "12345";
if (
enteredUsername === username &&
enteredPassword === password
) {
console.log("Login successful.");
} else {
console.log("Invalid username or password.");
}
Output
Login successful.
Step-by-Step Explanation
We first create a username and password:
const username = "rahul";
const password = "12345";
Then we simulate information entered by a user:
const enteredUsername = "rahul";
const enteredPassword = "12345";
The if condition checks both values:
if (
enteredUsername === username &&
enteredPassword === password
)
If both values match, login is successful.
Question 2: How do you create a login API using Express.js?
Problem
Create an Express.js API that accepts a username and password and checks them against sample credentials.
Solution
First install Express:
npm init -y
npm install express
Create index.js:
const express = require("express");
const app = express();
app.use(express.json());
const user = {
username: "rahul",
password: "12345"
};
app.post("/login", (req, res) => {
const {
username,
password
} = req.body;
if (
username === user.username &&
password === user.password
) {
return res.json({
success: true,
message: "Login successful."
});
}
res.status(401).json({
success: false,
message: "Invalid username or password."
});
});
app.listen(3000, () => {
console.log(
"Server running on port 3000"
);
});
Test the API
Send:
POST http://localhost:3000/login
JSON body:
{
"username": "rahul",
"password": "12345"
}
Response
{
"success": true,
"message": "Login successful."
}
Invalid Login
Send:
{
"username": "rahul",
"password": "wrong"
}
Response:
{
"success": false,
"message": "Invalid username or password."
}
Step-by-Step Explanation
express.json() allows Express to read JSON:
app.use(express.json());
The login route is:
app.post("/login", ...)
User information is read from:
req.body
If the credentials are incorrect, we return:
res.status(401)
HTTP status 401 commonly indicates that authentication is required or the supplied authentication credentials are invalid.
Important Point
A real authentication system should not compare plain-text passwords like this. Passwords should be securely hashed.
Question 3: How do you hash a password using bcrypt in Node.js?
Problem
Hash a user’s password before storing it in a database.
Solution
Install bcrypt:
npm install bcrypt
Create index.js:
const bcrypt = require("bcrypt");
async function hashPassword() {
const password = "mySecret123";
const hashedPassword =
await bcrypt.hash(
password,
10
);
console.log(
"Original password:",
password
);
console.log(
"Hashed password:",
hashedPassword
);
}
hashPassword();
Example Output
Original password: mySecret123
Hashed password:
$2b$10$...
Step-by-Step Explanation
Import bcrypt:
const bcrypt = require("bcrypt");
Create the password:
const password = "mySecret123";
Hash it:
const hashedPassword =
await bcrypt.hash(
password,
10
);
The number 10 is the salt-rounds value used by bcrypt.
Important Point
A hash is not the same as encryption. Password hashing is designed so that the original password is not stored directly.
Question 4: How do you compare a password with a bcrypt hash?
Problem
Check whether a user’s entered password matches the stored bcrypt password hash.
Solution
const bcrypt = require("bcrypt");
async function checkPassword() {
const password =
"mySecret123";
const storedHash =
await bcrypt.hash(
"mySecret123",
10
);
const isCorrect =
await bcrypt.compare(
password,
storedHash
);
if (isCorrect) {
console.log(
"Password is correct."
);
} else {
console.log(
"Incorrect password."
);
}
}
checkPassword();
Output
Password is correct.
Step-by-Step Explanation
The stored password should be a hash:
const storedHash =
await bcrypt.hash(
"mySecret123",
10
);
The entered password is checked using:
bcrypt.compare(
password,
storedHash
);
The result is a Boolean:
true
or:
false
Question 5: How do you create user registration with bcrypt?
Problem
Create an Express.js registration API that securely hashes a password before storing the user.
Solution
Install packages:
npm install express bcrypt
Create index.js:
const express = require("express");
const bcrypt = require("bcrypt");
const app = express();
app.use(express.json());
const users = [];
app.post("/register", async (req, res) => {
try {
const {
username,
password
} = req.body;
if (!username || !password) {
return res.status(400).json({
success: false,
message:
"Username and password are required."
});
}
const existingUser =
users.find(
user =>
user.username === username
);
if (existingUser) {
return res.status(409).json({
success: false,
message:
"Username already exists."
});
}
const hashedPassword =
await bcrypt.hash(
password,
10
);
const user = {
id: users.length + 1,
username: username,
password: hashedPassword
};
users.push(user);
res.status(201).json({
success: true,
message:
"User registered successfully."
});
} catch (error) {
res.status(500).json({
success: false,
message:
"Registration failed."
});
}
});
app.listen(3000, () => {
console.log(
"Server running on port 3000"
);
});
Test Request
POST http://localhost:3000/register
Body:
{
"username": "rahul",
"password": "mySecret123"
}
Response
{
"success": true,
"message": "User registered successfully."
}
What Gets Stored?
Instead of storing:
mySecret123
the application stores a bcrypt hash similar to:
$2b$10$...
Step-by-Step Explanation
The password comes from:
req.body.password
It is hashed:
const hashedPassword =
await bcrypt.hash(
password,
10
);
Then the hashed value is stored:
password: hashedPassword
Important Point
In a real application, users should normally be stored in a database rather than an in-memory array.
Question 6: How do you create a login system with bcrypt?
Problem
Create registration and login routes using Express.js and bcrypt.
Solution
const express = require("express");
const bcrypt = require("bcrypt");
const app = express();
app.use(express.json());
const users = [];
// Register
app.post("/register", async (req, res) => {
try {
const {
username,
password
} = req.body;
if (!username || !password) {
return res.status(400).json({
message:
"Username and password are required."
});
}
const existingUser =
users.find(
user =>
user.username === username
);
if (existingUser) {
return res.status(409).json({
message:
"User already exists."
});
}
const hashedPassword =
await bcrypt.hash(
password,
10
);
users.push({
username: username,
password: hashedPassword
});
res.status(201).json({
message:
"Registration successful."
});
} catch (error) {
res.status(500).json({
message:
"Registration failed."
});
}
});
// Login
app.post("/login", async (req, res) => {
try {
const {
username,
password
} = req.body;
const user =
users.find(
user =>
user.username === username
);
if (!user) {
return res.status(401).json({
message:
"Invalid username or password."
});
}
const passwordCorrect =
await bcrypt.compare(
password,
user.password
);
if (!passwordCorrect) {
return res.status(401).json({
message:
"Invalid username or password."
});
}
res.json({
message:
"Login successful."
});
} catch (error) {
res.status(500).json({
message:
"Login failed."
});
}
});
app.listen(3000, () => {
console.log(
"Server running on port 3000"
);
});
Step-by-Step Login Flow
User enters username and password
↓
Express receives POST /login
↓
Find user
↓
Compare password with bcrypt
↓
Password correct?
↙ ↘
YES NO
↓ ↓
Login successful 401 Error
Important Point
The server never needs to know the original stored password. It checks the entered password against the stored hash.
Question 7: How do you create a JWT after successful login?
Problem
Create a login API that generates a JSON Web Token after the user’s password is verified.
Solution
Install packages:
npm install express bcrypt jsonwebtoken
Create index.js:
const express = require("express");
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const app = express();
app.use(express.json());
const JWT_SECRET =
"replace-this-with-a-long-random-secret";
const users = [];
// Register
app.post("/register", async (req, res) => {
const {
username,
password
} = req.body;
const hashedPassword =
await bcrypt.hash(
password,
10
);
users.push({
username: username,
password: hashedPassword
});
res.status(201).json({
message:
"User registered."
});
});
// Login
app.post("/login", async (req, res) => {
const {
username,
password
} = req.body;
const user =
users.find(
user =>
user.username === username
);
if (!user) {
return res.status(401).json({
message:
"Invalid username or password."
});
}
const passwordCorrect =
await bcrypt.compare(
password,
user.password
);
if (!passwordCorrect) {
return res.status(401).json({
message:
"Invalid username or password."
});
}
const token =
jwt.sign(
{
username: user.username
},
JWT_SECRET,
{
expiresIn: "1h"
}
);
res.json({
message:
"Login successful.",
token: token
});
});
app.listen(3000, () => {
console.log(
"Server running on port 3000"
);
});
Example Login Response
{
"message": "Login successful.",
"token": "eyJhbGciOiJIUzI1NiIs..."
}
Step-by-Step Explanation
Import JWT:
const jwt =
require("jsonwebtoken");
After verifying the password, create a token:
const token =
jwt.sign(
{
username: user.username
},
JWT_SECRET,
{
expiresIn: "1h"
}
);
The token contains information that the server can later verify.
Important Point
Do not put passwords or other sensitive secrets inside the JWT payload.
Also, do not hard-code a production JWT secret in your source code. Use an environment variable.
Question 8: How do you protect a route using JWT authentication middleware?
Problem
Create middleware that checks whether a request contains a valid JWT.
Solution
const express = require("express");
const jwt = require("jsonwebtoken");
const app = express();
app.use(express.json());
const JWT_SECRET =
"replace-this-with-a-long-random-secret";
function authenticateToken(
req,
res,
next
) {
const authHeader =
req.headers.authorization;
const token =
authHeader &&
authHeader.split(" ")[1];
if (!token) {
return res.status(401).json({
message:
"Authentication token required."
});
}
jwt.verify(
token,
JWT_SECRET,
(error, user) => {
if (error) {
return res.status(403).json({
message:
"Invalid or expired token."
});
}
req.user = user;
next();
}
);
}
app.get(
"/profile",
authenticateToken,
(req, res) => {
res.json({
message:
"Welcome to your profile.",
user:
req.user
});
}
);
app.listen(3000, () => {
console.log(
"Server running on port 3000"
);
});
Send the Token
The client should send the token in the Authorization header:
Authorization: Bearer YOUR_TOKEN
Step-by-Step Explanation
Read the authorization header:
req.headers.authorization
A typical value is:
Bearer eyJhbGciOi...
The token is extracted using:
authHeader.split(" ")[1]
Then JWT verifies it:
jwt.verify(
token,
JWT_SECRET,
...
);
If verification succeeds:
req.user = user;
Then:
next();
allows the request to continue to the protected route.
Authentication Flow
Login
↓
Verify Password
↓
Create JWT
↓
Client Stores Token
↓
Client Sends Token
↓
Authentication Middleware
↓
Verify JWT
↓
Protected Route
Important Point
A valid JWT proves that the token was issued and has not been invalidated by expiration or signature failure. Your application still needs appropriate authorization checks to decide what that user is allowed to do.
Question 9: How do you connect authentication with MongoDB?
Problem
Create a registration API that stores users in MongoDB with a hashed password.
Solution
Install the required packages:
npm install express mongodb bcrypt
Create index.js:
const express = require("express");
const {
MongoClient
} = require("mongodb");
const bcrypt = require("bcrypt");
const app = express();
app.use(express.json());
const uri =
"mongodb://127.0.0.1:27017";
const client =
new MongoClient(uri);
let users;
// Connect to MongoDB
async function connectDatabase() {
await client.connect();
const db =
client.db("authDemo");
users =
db.collection("users");
console.log(
"MongoDB connected."
);
}
// Register
app.post(
"/register",
async (req, res) => {
try {
const {
username,
password
} = req.body;
if (!username || !password) {
return res.status(400).json({
message:
"Username and password are required."
});
}
const existingUser =
await users.findOne({
username: username
});
if (existingUser) {
return res.status(409).json({
message:
"User already exists."
});
}
const hashedPassword =
await bcrypt.hash(
password,
10
);
await users.insertOne({
username:
username,
password:
hashedPassword
});
res.status(201).json({
message:
"User registered successfully."
});
} catch (error) {
console.error(
error.message
);
res.status(500).json({
message:
"Registration failed."
});
}
}
);
// Start server
async function startServer() {
try {
await connectDatabase();
app.listen(3000, () => {
console.log(
"Server running on port 3000"
);
});
} catch (error) {
console.error(
error.message
);
}
}
startServer();
Test Registration
Send:
POST http://localhost:3000/register
Body:
{
"username": "rahul",
"password": "mySecret123"
}
MongoDB Document
The database will contain a document similar to:
{
"_id": "ObjectId(...)",
"username": "rahul",
"password": "$2b$10$..."
}
Step-by-Step Explanation
First, MongoDB is connected:
await client.connect();
The users collection is selected:
users =
db.collection("users");
Before creating a user, we check whether the username already exists:
await users.findOne({
username: username
});
The password is hashed:
await bcrypt.hash(
password,
10
);
Finally, the user is stored:
await users.insertOne({
username: username,
password: hashedPassword
});
Important Point
The database should store the password hash, not the original password.
Question 10: How do you build a complete Node.js authentication system with registration, login, JWT, and protected routes?
Problem
Build a beginner-friendly authentication API using:
- Node.js
- Express.js
- MongoDB
- bcrypt
- JWT
The API should support registration, login, and a protected profile route.
Solution
Install packages:
npm init -y
npm install express mongodb bcrypt jsonwebtoken
Create index.js:
const express = require("express");
const {
MongoClient
} = require("mongodb");
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const app = express();
app.use(express.json());
const uri =
"mongodb://127.0.0.1:27017";
const client =
new MongoClient(uri);
const JWT_SECRET =
process.env.JWT_SECRET ||
"development-secret-change-this";
let users;
// Connect to MongoDB
async function connectDatabase() {
await client.connect();
const db =
client.db("authDemo");
users =
db.collection("users");
console.log(
"MongoDB connected successfully."
);
}
// Register
app.post(
"/register",
async (req, res) => {
try {
const {
username,
password
} = req.body;
if (
!username ||
!password
) {
return res.status(400).json({
success: false,
message:
"Username and password are required."
});
}
const existingUser =
await users.findOne({
username: username
});
if (existingUser) {
return res.status(409).json({
success: false,
message:
"Username already exists."
});
}
const hashedPassword =
await bcrypt.hash(
password,
10
);
await users.insertOne({
username:
username,
password:
hashedPassword
});
res.status(201).json({
success: true,
message:
"Registration successful."
});
} catch (error) {
console.error(
error.message
);
res.status(500).json({
success: false,
message:
"Registration failed."
});
}
}
);
// Login
app.post(
"/login",
async (req, res) => {
try {
const {
username,
password
} = req.body;
const user =
await users.findOne({
username:
username
});
if (!user) {
return res.status(401).json({
success: false,
message:
"Invalid username or password."
});
}
const passwordCorrect =
await bcrypt.compare(
password,
user.password
);
if (!passwordCorrect) {
return res.status(401).json({
success: false,
message:
"Invalid username or password."
});
}
const token =
jwt.sign(
{
userId:
user._id.toString(),
username:
user.username
},
JWT_SECRET,
{
expiresIn:
"1h"
}
);
res.json({
success: true,
message:
"Login successful.",
token:
token
});
} catch (error) {
console.error(
error.message
);
res.status(500).json({
success: false,
message:
"Login failed."
});
}
}
);
// Authentication middleware
function authenticateToken(
req,
res,
next
) {
const authHeader =
req.headers.authorization;
const token =
authHeader &&
authHeader.split(" ")[1];
if (!token) {
return res.status(401).json({
success: false,
message:
"Authentication token required."
});
}
jwt.verify(
token,
JWT_SECRET,
(error, user) => {
if (error) {
return res.status(403).json({
success: false,
message:
"Invalid or expired token."
});
}
req.user =
user;
next();
}
);
}
// Protected profile route
app.get(
"/profile",
authenticateToken,
async (req, res) => {
try {
const user =
await users.findOne({
username:
req.user.username
});
if (!user) {
return res.status(404).json({
success: false,
message:
"User not found."
});
}
res.json({
success: true,
profile: {
username:
user.username
}
});
} catch (error) {
res.status(500).json({
success: false,
message:
"Unable to load profile."
});
}
}
);
// Start server
async function startServer() {
try {
await connectDatabase();
app.listen(3000, () => {
console.log(
"Server running on port 3000"
);
});
} catch (error) {
console.error(
"Server startup failed:",
error.message
);
}
}
startServer();
Step 1: Register a User
Send:
POST http://localhost:3000/register
Body:
{
"username": "rahul",
"password": "mySecret123"
}
Response:
{
"success": true,
"message": "Registration successful."
}
Step 2: Login
Send:
POST http://localhost:3000/login
Body:
{
"username": "rahul",
"password": "mySecret123"
}
Response:
{
"success": true,
"message": "Login successful.",
"token": "eyJhbGciOiJIUzI1NiIs..."
}
Copy the returned token.
Step 3: Access the Protected Route
Send:
GET http://localhost:3000/profile
Add this HTTP header:
Authorization: Bearer YOUR_TOKEN
For example:
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Response
{
"success": true,
"profile": {
"username": "rahul"
}
}
What Happens Behind the Scenes?
REGISTER
↓
Receive Password
↓
bcrypt.hash()
↓
Store Hash
↓
MongoDB
LOGIN
↓
Receive Credentials
↓
Find User in DB
↓
bcrypt.compare()
↓
Password Valid?
↙ ↘
YES NO
↓ ↓
Create JWT 401 Error
↓
Return Token
PROTECTED ROUTE
↓
Receive Bearer Token
↓
JWT Verify
↓
Valid Token?
↙ ↘
YES NO
↓ ↓
next() 401/403
↓
Protected Route
Important Security Improvements for Real Applications
The example above is designed for learning. A production authentication system should additionally consider:
- Store the JWT secret in an environment variable.
- Use HTTPS in production.
- Validate usernames and passwords.
- Use appropriate password policies.
- Never store plain-text passwords.
- Avoid returning passwords in API responses.
- Use secure session/token storage appropriate for the client application.
- Consider token expiration and refresh-token strategies where appropriate.
- Add rate limiting to login endpoints.
- Avoid revealing whether a particular username exists when that information could help attackers.
- Keep dependencies updated.
- Use secure cookies when using cookie-based authentication.
- Add authorization checks for protected resources.
- Do not put sensitive information inside JWT payloads.
Key Takeaways
- Authentication verifies the identity of a user.
- Registration and login are two common authentication processes.
- Express.js can be used to create authentication APIs.
req.bodycan be used to read login and registration data.- HTTP
401is commonly used when authentication credentials are missing or invalid. - Passwords should never be stored as plain text.
bcryptcan be used to hash passwords.bcrypt.hash()creates a password hash.bcrypt.compare()checks an entered password against a stored hash.- A password hash should be stored instead of the original password.
- MongoDB can store users and their password hashes.
findOne()can be used to locate a user during login.- JWT stands for JSON Web Token.
jsonwebtokencan create and verify JWTs.jwt.sign()creates a JWT.jwt.verify()verifies a JWT.- JWTs can contain non-sensitive user information such as a user ID.
- Passwords and other secrets should not be placed inside JWT payloads.
- JWT expiration can be configured with
expiresIn. - Middleware can protect Express routes.
req.headers.authorizationcan contain the client’s authentication header.- Bearer tokens are commonly sent using the
Authorizationheader. next()allows an Express middleware function to pass control to the next handler.- Authentication and authorization are different concepts.
- Authentication asks, “Who are you?”
- Authorization asks, “What are you allowed to do?”
- A valid login does not automatically mean the user can access every resource.
- JWT secrets should be kept outside source code in production.
- Environment variables are commonly used for application secrets.
- HTTPS should be used when transmitting authentication credentials.
- Login endpoints should be protected against brute-force attempts.
- User input should be validated before processing.
- Authentication errors should be handled carefully.
- Never expose passwords or password hashes in API responses.
- MongoDB, Express.js, bcrypt, and JWT can be combined to build practical authentication systems.
FAQs
1. What is authentication in Node.js?
Authentication is the process of verifying the identity of a user.
For example, when a user enters a username and password, the Node.js server checks whether the credentials are valid.
Username + Password
↓
Node.js Server
↓
Verify Credentials
↓
Login Allowed or Rejected
2. Why should passwords be hashed in Node.js?
Passwords should be hashed so that the original password is not stored directly in the database.
For example, instead of storing:
mySecret123
the database stores a password hash similar to:
$2b$10$...
Libraries such as bcrypt are commonly used for password hashing.
3. What is bcrypt in Node.js authentication?
bcrypt is a password-hashing library commonly used in Node.js applications.
You can hash a password with:
const hash =
await bcrypt.hash(
password,
10
);
You can verify a password with:
const valid =
await bcrypt.compare(
password,
hash
);
4. What is JWT authentication in Node.js?
JWT authentication uses a signed JSON Web Token to represent an authenticated user.
After successful login, the server can create a token:
const token =
jwt.sign(
{
userId: "123"
},
JWT_SECRET,
{
expiresIn: "1h"
}
);
The client can then send the token when accessing protected routes.
5. What is authentication middleware in Express.js?
Authentication middleware checks whether a request has valid authentication information before allowing it to reach a protected route.
For example:
app.get(
"/profile",
authenticateToken,
(req, res) => {
res.json({
message: "Profile"
});
}
);
The authenticateToken middleware runs before the profile route.
6. Where should passwords be stored in a Node.js application?
Passwords should not be stored as plain text.
A secure application should store a strong password hash, such as a bcrypt hash, in the database.
For example:
{
username: "rahul",
password: "$2b$10$..."
}
The original password should not be stored in the database.
7. What is the difference between authentication and authorization?
Authentication verifies who the user is.
Authorization determines what the authenticated user is allowed to do.
For example:
Authentication:
"Are you Rahul?"
Authorization:
"Can Rahul delete this student?"
Both concepts are important when building secure Node.js applications.
Written by Shubhranshu Shekhar, who has trained 20000+ students in coding.
