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 . 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;
Add your MongoDB connection string in .env:
MONGO_URI=your_mongo_connection_string
JWT_SECRET=your_jwt_secret
PORT=5000
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);
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}`));
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;
Testing the Application
Using Postman or Thunder Client
Register a user:
- Endpoint:
POST /api/auth/register
- Body:
CODE{
"username": "john_doe",
"email": "[email protected]",
"password": "123456"
}
- Endpoint:
Login a user:
- Endpoint:
POST /api/auth/login
- Body:
CODE{
"email": "[email protected]",
"password": "123456"
}
- Endpoint:
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.
SOCIAL SHARE CARD GENERATOR