How to create an Authentication & Authorization feature in Express JS RESTful API?

Nadim Chowdhury - Jun 4 - - Dev Community

Creating an authentication and authorization feature in an Express.js RESTful API involves several steps. Here's a step-by-step guide:

Step 1: Set Up a New Express.js Project

  1. Initialize a New Project:
   mkdir project-name
   cd project-name
   npm init -y
Enter fullscreen mode Exit fullscreen mode
  1. Install Required Packages:
   npm install express mongoose jsonwebtoken bcryptjs
   npm install dotenv passport passport-jwt
Enter fullscreen mode Exit fullscreen mode

Step 2: Configure Environment Variables

  1. Create a .env File:
   PORT=3000
   MONGODB_URI=mongodb://localhost:27017/auth-db
   JWT_SECRET=your_jwt_secret
Enter fullscreen mode Exit fullscreen mode
  1. Load Environment Variables: Create config.js to load environment variables:
   require('dotenv').config();

   module.exports = {
     port: process.env.PORT || 3000,
     mongodbUri: process.env.MONGODB_URI,
     jwtSecret: process.env.JWT_SECRET,
   };
Enter fullscreen mode Exit fullscreen mode

Step 3: Set Up Mongoose Models

  1. Create a Directory Structure:
   mkdir -p src/models src/controllers src/routes src/middleware
Enter fullscreen mode Exit fullscreen mode
  1. Create User Model: Create src/models/User.js:
   const mongoose = require('mongoose');
   const bcrypt = require('bcryptjs');

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

   userSchema.pre('save', async function(next) {
     if (!this.isModified('password')) {
       return next();
     }
     const salt = await bcrypt.genSalt(10);
     this.password = await bcrypt.hash(this.password, salt);
     next();
   });

   userSchema.methods.matchPassword = async function(enteredPassword) {
     return await bcrypt.compare(enteredPassword, this.password);
   };

   const User = mongoose.model('User', userSchema);

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

Step 4: Set Up Controllers

  1. Create Auth Controller: Create src/controllers/authController.js:
   const jwt = require('jsonwebtoken');
   const User = require('../models/User');
   const config = require('../../config');

   const generateToken = (id) => {
     return jwt.sign({ id }, config.jwtSecret, { expiresIn: '1h' });
   };

   exports.registerUser = async (req, res) => {
     const { username, email, password } = req.body;

     const userExists = await User.findOne({ email });

     if (userExists) {
       return res.status(400).json({ message: 'User already exists' });
     }

     const user = await User.create({ username, email, password });

     if (user) {
       res.status(201).json({
         _id: user._id,
         username: user.username,
         email: user.email,
         token: generateToken(user._id),
       });
     } else {
       res.status(400).json({ message: 'Invalid user data' });
     }
   };

   exports.loginUser = async (req, res) => {
     const { email, password } = req.body;

     const user = await User.findOne({ email });

     if (user && (await user.matchPassword(password))) {
       res.json({
         _id: user._id,
         username: user.username,
         email: user.email,
         token: generateToken(user._id),
       });
     } else {
       res.status(401).json({ message: 'Invalid email or password' });
     }
   };

   exports.getUserProfile = async (req, res) => {
     const user = await User.findById(req.user.id);

     if (user) {
       res.json({
         _id: user._id,
         username: user.username,
         email: user.email,
       });
     } else {
       res.status(404).json({ message: 'User not found' });
     }
   };
Enter fullscreen mode Exit fullscreen mode

Step 5: Set Up Routes

  1. Create Auth Routes: Create src/routes/authRoutes.js:
   const express = require('express');
   const { registerUser, loginUser, getUserProfile } = require('../controllers/authController');
   const { protect } = require('../middleware/authMiddleware');

   const router = express.Router();

   router.post('/register', registerUser);
   router.post('/login', loginUser);
   router.get('/profile', protect, getUserProfile);

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

Step 6: Set Up Middleware

  1. Create Auth Middleware: Create src/middleware/authMiddleware.js:
   const jwt = require('jsonwebtoken');
   const User = require('../models/User');
   const config = require('../../config');

   exports.protect = async (req, res, next) => {
     let token;

     if (
       req.headers.authorization &&
       req.headers.authorization.startsWith('Bearer')
     ) {
       try {
         token = req.headers.authorization.split(' ')[1];

         const decoded = jwt.verify(token, config.jwtSecret);

         req.user = await User.findById(decoded.id).select('-password');

         next();
       } catch (error) {
         res.status(401).json({ message: 'Not authorized, token failed' });
       }
     }

     if (!token) {
       res.status(401).json({ message: 'Not authorized, no token' });
     }
   };
Enter fullscreen mode Exit fullscreen mode

Step 7: Set Up the Express Server

  1. Create the Server: Create src/index.js:
   const express = require('express');
   const mongoose = require('mongoose');
   const bodyParser = require('body-parser');
   const config = require('../config');
   const authRoutes = require('./routes/authRoutes');

   const app = express();

   mongoose.connect(config.mongodbUri, {
     useNewUrlParser: true,
     useUnifiedTopology: true,
     useCreateIndex: true,
   }).then(() => {
     console.log('Connected to MongoDB');
   }).catch((err) => {
     console.error('Error connecting to MongoDB', err);
   });

   app.use(bodyParser.json());
   app.use('/api/auth', authRoutes);

   app.listen(config.port, () => {
     console.log(`Server running on http://localhost:${config.port}`);
   });
Enter fullscreen mode Exit fullscreen mode

Step 8: Test the API

  1. Register a User:
   POST /api/auth/register
   Content-Type: application/json

   {
     "username": "john",
     "email": "john@example.com",
     "password": "password"
   }
Enter fullscreen mode Exit fullscreen mode
  1. Login a User:
   POST /api/auth/login
   Content-Type: application/json

   {
     "email": "john@example.com",
     "password": "password"
   }
Enter fullscreen mode Exit fullscreen mode
  1. Get User Profile (Protected Route):
   GET /api/auth/profile
   Authorization: Bearer <token>
Enter fullscreen mode Exit fullscreen mode

This guide provides a foundational approach to implementing authentication and authorization in an Express.js RESTful API. You can further expand and customize it based on your application's requirements.

If you enjoy my content and would like to support my work, you can buy me a coffee. Your support is greatly appreciated!

Disclaimer: This content is generated by AI.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .