Authentication is a crucial aspect of any web application. Among various authentication methods, cookie-based authentication remains one of the most commonly used approaches due to its simplicity and wide browser support. In this blog, we will explore the implementation of cookie-based authentication, how it works, and why it’s effective. We'll also address common doubts and provide a step-by-step guide to help you implement it in your web application.
What are cookies?
Cookies are small pieces of data stored on the client’s browser by websites they visit. They serve various purposes in web development, such as maintaining user sessions, personalizing user experiences, and tracking user behaviour. Cookies are sent along with every HTTP request to the server, allowing websites to remember user preferences and state between different pages or visits.
How are Cookies Implemented in Node.js?
In Node.js, cookies can be implemented using the
cookie-parser
middleware. This middleware parses cookies from incoming HTTP requests, making them accessible in the request object. Here's how you can implement cookies in Node.js:We can understand the use of cookies by directly implementing on nodejs
Let’s implement cookie-based authentication in a Node.js backend and React frontend.
Backend Implementation(Node.js)
- Setting Up Express (read the comments to understand the code )
const express = require('express'); const app = express(); const cors = require('cors'); const cookieParser = require('cookie-parser'); const mongoose = require('mongoose'); const bcrypt = require('bcrypt'); const jwt = require('jsonwebtoken'); require('dotenv').config(); app.use(express.json()); app.use(cookieParser()); app.use(cors({ origin: 'http://localhost:3000', // Frontend URL credentials: true, // Allow cookies to be sent })); // ------------------------------------------------------------------------------------------ // MongoDB Connection mongoose.connect('mongodb://127.0.0.1:27017/auth', { useNewUrlParser: true, useUnifiedTopology: true, }) .then(() => console.log('Connected to MongoDB')) .catch(err => console.error('MongoDB connection error:', err)); // User Schema and Model const userSchema = new mongoose.Schema({ username: { type: String, required: true, unique: true }, email: { type: String, required: true, unique: true }, password: { type: String, required: true } }); const User = mongoose.model("User", userSchema); // ------------------------------------------------------------------------------------------ // Middleware function checkAuth(req, res, next) { const authToken = req.cookies.authToken; // Get token from cookie if (!authToken) { return res.status(403).json({ message: 'No token provided' }); } // Verify the token jwt.verify(authToken, process.env.JWT_SECRET, (err, decoded) => { if (err) { return res.status(401).json({ message: 'Unauthorized' }); } // Attach decoded user data to req.user req.user = decoded; // We can access this user info in subsequent handlers next(); // Proceed to the next middleware or route handler }); } // ------------------------------------------------------------------------------------------ // Signup Route app.post('/signup', async (req, res) => { const { username, email, password } = req.body; try { // Check if user already exists const existingUser = await User.findOne({ email }); if (existingUser) { return res.status(400).json({ message: 'User already exists' }); } // Hash the password const hashedPassword = await bcrypt.hash(password, 10); // Create a new user const user = new User({ username, email, password: hashedPassword }); await user.save(); // Generate JWT Token const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, { expiresIn: '1h' }); // Set Cookie res.cookie('authToken', token, { httpOnly: true, maxAge: 3600000 // 1 hour }); res.status(201).json({ message: 'User registered successfully', token }); } catch (e) { console.error(e); res.status(500).json({ message: 'Internal server error' }); } }); // Signin Route app.post('/signin', async (req, res) => { const { email, password } = req.body; try { // Find the user by email const user = await User.findOne({ email }); if (!user) { return res.status(404).json({ message: 'User not found' }); } // Compare passwords const isPasswordValid = await bcrypt.compare(password, user.password); if (!isPasswordValid) { return res.status(401).json({ message: 'Invalid credentials' }); } // Generate JWT Token const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, { expiresIn: '1h' }); // Set Cookie res.cookie('authToken', token, { httpOnly: true, maxAge: 3600000 // 1 hour }); res.status(200).json({ message: 'Login successful', token }); } catch (e) { console.error(e); res.status(500).json({ message: 'Internal server error' }); } }); // Protected Route app.get('/protected', checkAuth, (req, res) => { res.status(200).json({ message: 'Welcome to the protected route', user: req.user }); }); {userData && <p>Welcome, {userData.username}!</p>} // ------------------------------------------------------------------------------------------ const PORT = 8080; app.listen(PORT, () => console.log(`Server running on port ${PORT}`));