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
- Set Up the Server
- Hash and Store Passwords
- Handle User Login
- Use Cookies for Sessions
- 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 serverconst url = require("url"); // URL module for parsing request URLsconst bcrypt = require("bcryptjs"); // bcrypt for password hashingconst cookie = require("cookie"); // cookie for handling session cookies2. 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 10bcrypt.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 passwordslet 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 IDres.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:
-
Hashing and Comparing Passwords:
- We used bcrypt to hash user passwords and securely compare them during login.
-
User Login:
- We handled user login by creating a POST route where users submit their email and password.
-
Session Management:
- We managed user sessions by using cookies to store a session ID after successful login.
-
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.
- We protected a route (e.g.,
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.