DEV Community

Gagan
Gagan

Posted on

Build a Login and Registration System Using Node.js: A Step-By-Step Guide

Introduction

Building a login and registration system is a fundamental task for any web application. It ensures secure access control and user authentication, laying the foundation for a functional app. This guide is tailored for beginners, providing an easy-to-follow approach using Node.js. By the end of this tutorial, you'll have a functional authentication system in place.


What Will You Learn?

  • How to set up a Node.js project
  • Implement user registration
  • Add secure login functionality
  • Hash passwords for security using bcrypt
  • Validate user input
  • Use JWT for token-based authentication
  • Handle common errors and edge cases

Setting Up Your Environment

1. Install Node.js

Before you start, ensure Node.js is installed on your system. Download it from Node.js Official Website and follow the installation instructions.

2. Initialize Your Project

mkdir node-auth-system
cd node-auth-system
npm init -y
Enter fullscreen mode Exit fullscreen mode

This creates a package.json file to manage your project dependencies.

3. Install Required Packages

npm install express body-parser mongoose bcrypt jsonwebtoken dotenv cors
Enter fullscreen mode Exit fullscreen mode

4. Create a Basic Project Structure

Organize your files for a clean workflow:

node-auth-system/
├── .env
├── app.js
├── models/
│   └── User.js
├── routes/
│   └── auth.js
├── config/
│   └── db.js
└── package.json
Enter fullscreen mode Exit fullscreen mode

Setting Up MongoDB

We'll use MongoDB to store user information. If you don’t have MongoDB installed, set it up here. Alternatively, use a cloud database like MongoDB Atlas.

Configuring Database Connection

In config/db.js:

const mongoose = require('mongoose');
const connectDB = async () => {
  try {
    await mongoose.connect(process.env.MONGO_URI, {
      useNewUrlParser: true,
      useUnifiedTopology: true,
    });
    console.log('MongoDB Connected...');
  } catch (err) {
    console.error(err.message);
    process.exit(1);
  }
};

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

Add your MongoDB connection string in .env:

MONGO_URI=your_mongo_connection_string
JWT_SECRET=your_jwt_secret
PORT=5000
Enter fullscreen mode Exit fullscreen mode

Building the User Model

Create a User.js file inside the models directory:

const mongoose = require('mongoose');
const bcrypt = require('bcrypt');

const UserSchema = new mongoose.Schema({
  username: {
    type: String,
    required: true,
    unique: true,
  },
  email: {
    type: String,
    required: true,
    unique: true,
  },
  password: {
    type: String,
    required: true,
  },
});

// Hash password before saving
UserSchema.pre('save', async function (next) {
  if (!this.isModified('password')) return next();
  this.password = await bcrypt.hash(this.password, 10);
  next();
});

module.exports = mongoose.model('User', UserSchema);
Enter fullscreen mode Exit fullscreen mode

Creating the Authentication Routes

Initialize Express

In app.js:

const express = require('express');
const dotenv = require('dotenv');
const connectDB = require('./config/db');
const authRoutes = require('./routes/auth');

dotenv.config();
connectDB();

const app = express();
app.use(express.json());
app.use('/api/auth', authRoutes);

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

Define Authentication Endpoints

In routes/auth.js:

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

// Register User
router.post('/register', async (req, res) => {
  const { username, email, password } = req.body;

  try {
    if (!username || !email || !password) {
      return res.status(400).json({ message: 'All fields are required' });
    }

    const userExists = await User.findOne({ email });
    if (userExists) {
      return res.status(400).json({ message: 'User already exists' });
    }

    const newUser = await User.create({ username, email, password });
    res.status(201).json({ message: 'User registered successfully', user: newUser });
  } catch (err) {
    res.status(500).json({ message: 'Server error' });
  }
});

// Login User
router.post('/login', async (req, res) => {
  const { email, password } = req.body;

  try {
    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({ message: 'Login successful', token });
  } catch (err) {
    res.status(500).json({ message: 'Server error' });
  }
});

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

Testing the Application

Using Postman or Thunder Client

  1. Register a user:

    • Endpoint: POST /api/auth/register
    • Body:
     {
       "username": "john_doe",
       "email": "john@example.com",
       "password": "123456"
     }
    
  2. Login a user:

    • Endpoint: POST /api/auth/login
    • Body:
     {
       "email": "john@example.com",
       "password": "123456"
     }
    

FAQs

Why Use Bcrypt for Password Hashing?

Bcrypt securely hashes passwords, adding a salt to make brute-force attacks significantly harder.

How Does JWT Work?

JWT encodes user information into a token, ensuring secure, stateless authentication. It’s useful for scaling as the server doesn’t need to store session data.

How to Handle Expired Tokens?

Include middleware to check token validity and return a proper error message if expired.


This guide takes you from zero to hero in building a login and registration system with Node.js, covering critical features and security considerations. Experiment and expand your application by adding roles, password reset features, and frontend integration.

Top comments (0)