Skip to content

Simple User Authentication in Node.js

In this guide, we’ll set up a basic user authentication system in Node.js without using any frameworks like Express. We’ll focus on login functionality, hashing passwords, and managing sessions using cookies.

Steps for Authentication

  1. Set Up the Server
  2. Hash and Store Passwords
  3. Handle User Login
  4. Use Cookies for Sessions
  5. Protect Routes Based on Session

1. Set Up the Server

First, let’s set up a basic HTTP server using the built-in Node.js modules.

const http = require("http"); // HTTP module for creating the server
const url = require("url"); // URL module for parsing request URLs
const bcrypt = require("bcryptjs"); // bcrypt for password hashing
const cookie = require("cookie"); // cookie for handling session cookies

2. Hash and Store Passwords

For security, it is important to hash user passwords before storing them in the database. This prevents sensitive data from being stored as plain text. We will use the bcryptjs library for hashing passwords.

How to Hash a Password

Below is a simple example of how to hash a password using bcryptjs:

const bcrypt = require("bcryptjs"); // Import bcryptjs
const password = "password123"; // The user's password
// Hash the password with a salt rounds value of 10
bcrypt.hash(password, 10, (err, hashedPassword) => {
if (err) throw err; // Handle any errors
console.log(hashedPassword); // Log the hashed password, which you can store in the database
});

3. Handle User Login

To implement the login functionality, we will accept the user’s email and password through a POST request. We’ll then compare the submitted password with the stored hashed password using bcrypt.compareSync().

const http = require("http");
const url = require("url");
const bcrypt = require("bcryptjs");
const cookie = require("cookie");
// Sample users stored with bcrypt hashed passwords
let users = {
"user@example.com": {
password: "$2a$10$1z0OMm1YVVV0ZkBkFOQy/ON2A9gdZ1dATh9ZkdP1RRUPgVpm1ZtD6", // bcrypt hash for 'password123'
},
};
http
.createServer((req, res) => {
const { method, url: reqUrl } = req;
const parsedUrl = url.parse(reqUrl, true);
if (method === "POST" && parsedUrl.pathname === "/login") {
let body = "";
req.on("data", (chunk) => {
body += chunk;
});
req.on("end", () => {
const { email, password } = JSON.parse(body); // Parse the JSON body
const user = users[email];
if (user && bcrypt.compareSync(password, user.password)) {
// Password matched, create session
const sessionId = Math.random().toString(36).substr(2); // Generate a random session ID
res.setHeader(
"Set-Cookie",
cookie.serialize("session", sessionId, { httpOnly: true })
);
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({ message: "Login successful!" }));
} else {
res.writeHead(401, { "Content-Type": "application/json" });
res.end(JSON.stringify({ message: "Invalid email or password" }));
}
});
return;
}
res.writeHead(404, { "Content-Type": "application/json" });
res.end(JSON.stringify({ message: "Not found" }));
})
.listen(3000, () => {
console.log("Server running at http://localhost:3000");
});

4. Use Cookies for Sessions

When a user logs in successfully, we generate a session ID and send it as a cookie. This session ID helps to identify the user in future requests, eliminating the need for them to re-enter their login credentials.

After validating the user’s login credentials, we generate a session ID and store it in a cookie:

const sessionId = Math.random().toString(36).substr(2); // Generate a random session ID
res.setHeader(
"Set-Cookie",
cookie.serialize("session", sessionId, { httpOnly: true })
);

5. Protect Routes Based on Session

To protect routes, such as /dashboard, and ensure that only logged-in users can access them, we check for the session cookie. If the session cookie is present, the user is considered authenticated and allowed access. Otherwise, the user is denied access.

We will check the session cookie in the incoming request. If the session exists, we allow the user to access the protected route. Otherwise, we return an unauthorized response.

if (method === "GET" && parsedUrl.pathname === "/dashboard") {
const cookies = cookie.parse(req.headers.cookie || ""); // Parse the cookies from the request header
const session = cookies.session; // Extract the session cookie
if (session) {
// If session exists, the user is authenticated and can access the dashboard
res.writeHead(200, { "Content-Type": "text/html" });
res.end("<h1>Welcome to your dashboard!</h1>");
} else {
// If session doesn't exist, user is unauthorized
res.writeHead(401, { "Content-Type": "application/json" });
res.end(
JSON.stringify({ message: "You must be logged in to access this page" })
);
}
return;
}

Conclusion

In this guide, you learned how to set up a simple authentication system in Node.js using the built-in HTTP module. Here’s a quick overview of what we covered:

  1. Hashing and Comparing Passwords:

    • We used bcrypt to hash user passwords and securely compare them during login.
  2. User Login:

    • We handled user login by creating a POST route where users submit their email and password.
  3. Session Management:

    • We managed user sessions by using cookies to store a session ID after successful login.
  4. Route Protection:

    • We protected a route (e.g., /dashboard) using a GET request to ensure only logged-in users can access it. We checked the session cookie to verify if the user is authenticated.

This setup provides a simple authentication flow without using any external frameworks. It serves as a great starting point for building more advanced authentication systems. You can extend this foundation by integrating a real database, enhancing security, and adding features like JWT or OAuth for better scalability and security.