DEV Community

kathir b
kathir b

Posted on

How to Build a Secure User Authentication System in Node.js

Introduction

User authentication is a critical part of any web application. In this tutorial, we'll build a secure authentication system in Node.js using JWT (JSON Web Token), bcrypt for password hashing, and Express.js for handling routes.

By the end of this guide, you'll be able to:

  • Register users securely
  • Hash passwords using bcrypt
  • Authenticate users using JWT
  • Protect API routes with middleware

Step 1: Set Up Your Node.js Project

First, create a new project and install dependencies:

mkdir auth-system && cd auth-system
npm init -y
npm install express bcryptjs jsonwebtoken dotenv cors mongoose
Enter fullscreen mode Exit fullscreen mode

Create a .env File

Create a .env file for environment variables:

PORT=5000
MONGO_URI=mongodb://localhost:27017/auth_db
JWT_SECRET=your_jwt_secret_key
Enter fullscreen mode Exit fullscreen mode

Step 2: Set Up Express Server

Create server.js:

require('dotenv').config();
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const userRoutes = require('./routes/userRoutes');

const app = express();
app.use(express.json());
app.use(cors());
app.use('/api/users', userRoutes);

mongoose.connect(process.env.MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true })
  .then(() => console.log('MongoDB Connected'))
  .catch(err => console.log(err));

const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
Enter fullscreen mode Exit fullscreen mode

Step 3: Create User Model

Create models/User.js:

const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
  name: { type: String, required: true },
  email: { type: String, required: true, unique: true },
  password: { type: String, required: true }
});
module.exports = mongoose.model('User', userSchema);
Enter fullscreen mode Exit fullscreen mode

Step 4: Implement User Registration

Create routes/userRoutes.js:

const express = require('express');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const User = require('../models/User');
const router = express.Router();

// Register user
router.post('/register', async (req, res) => {
  try {
    const { name, email, password } = req.body;
    const existingUser = await User.findOne({ email });
    if (existingUser) return res.status(400).json({ message: 'User already exists' });

    const hashedPassword = await bcrypt.hash(password, 10);
    const user = new User({ name, email, password: hashedPassword });
    await user.save();

    res.status(201).json({ message: 'User registered successfully' });
  } catch (error) {
    res.status(500).json({ message: 'Server error' });
  }
});

module.exports = router;
Enter fullscreen mode Exit fullscreen mode

Step 5: Implement User Login & JWT Authentication

Add login logic in userRoutes.js:

router.post('/login', async (req, res) => {
  try {
    const { email, password } = req.body;
    const user = await User.findOne({ email });
    if (!user) return res.status(400).json({ message: 'Invalid credentials' });

    const isMatch = await bcrypt.compare(password, user.password);
    if (!isMatch) return res.status(400).json({ message: 'Invalid credentials' });

    const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, { expiresIn: '1h' });
    res.json({ token });
  } catch (error) {
    res.status(500).json({ message: 'Server error' });
  }
});
Enter fullscreen mode Exit fullscreen mode

Step 6: Protect API Routes with Middleware

Create middleware/auth.js:

const jwt = require('jsonwebtoken');
module.exports = (req, res, next) => {
  const token = req.header('Authorization');
  if (!token) return res.status(401).json({ message: 'Access Denied' });

  try {
    const verified = jwt.verify(token, process.env.JWT_SECRET);
    req.user = verified;
    next();
  } catch (error) {
    res.status(400).json({ message: 'Invalid Token' });
  }
};
Enter fullscreen mode Exit fullscreen mode

Apply middleware to protect routes:

const auth = require('../middleware/auth');
router.get('/profile', auth, (req, res) => {
  res.json({ message: 'Welcome to your profile' });
});
Enter fullscreen mode Exit fullscreen mode

Step 7: Test Your API with cURL or Postman

  • Register User:
  curl -X POST -H "Content-Type: application/json" -d '{"name": "John Doe", "email": "john@example.com", "password": "password123"}' http://localhost:5000/api/users/register
Enter fullscreen mode Exit fullscreen mode
  • Login User:
  curl -X POST -H "Content-Type: application/json" -d '{"email": "john@example.com", "password": "password123"}' http://localhost:5000/api/users/login
Enter fullscreen mode Exit fullscreen mode
  • Access Protected Route:
  curl -X GET -H "Authorization: Bearer YOUR_JWT_TOKEN" http://localhost:5000/api/users/profile
Enter fullscreen mode Exit fullscreen mode

Conclusion

You have successfully built a secure user authentication system in Node.js with JWT authentication! 🎉

Next Steps:
✅ Connect to a frontend (React/Angular/Vue)
✅ Implement password reset functionality
✅ Add role-based access control (RBAC)

Happy Coding! 🚀

Heroku

Build apps, not infrastructure.

Dealing with servers, hardware, and infrastructure can take up your valuable time. Discover the benefits of Heroku, the PaaS of choice for developers since 2007.

Visit Site

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay