How to create an Authentication & Authorization feature in Express JS RESTful API?
Published: 2024-06-04 05:38:23

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**:
   ```bash
   mkdir project-name
   cd project-name
   npm init -y
   ```
2. **Install Required Packages**:
   ```bash
   npm install express mongoose jsonwebtoken bcryptjs
   npm install dotenv passport passport-jwt
   ```

### Step 2: Configure Environment Variables
1. **Create a `.env` File**:
   ```plaintext
   PORT=3000
   MONGODB_URI=mongodb://localhost:27017/auth-db
   JWT_SECRET=your_jwt_secret
   ```

2. **Load Environment Variables**:
   Create `config.js` to load environment variables:
   ```javascript
   require('dotenv').config();

   module.exports = {
     port: process.env.PORT || 3000,
     mongodbUri: process.env.MONGODB_URI,
     jwtSecret: process.env.JWT_SECRET,
   };
   ```

### Step 3: Set Up Mongoose Models
1. **Create a Directory Structure**:
   ```bash
   mkdir -p src/models src/controllers src/routes src/middleware
   ```

2. **Create User Model**:
   Create `src/models/User.js`:
   ```javascript
   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;
   ```

### Step 4: Set Up Controllers
1. **Create Auth Controller**:
   Create `src/controllers/authController.js`:
   ```javascript
   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' });
     }
   };
   ```

### Step 5: Set Up Routes
1. **Create Auth Routes**:
   Create `src/routes/authRoutes.js`:
   ```javascript
   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;
   ```

### Step 6: Set Up Middleware
1. **Create Auth Middleware**:
   Create `src/middleware/authMiddleware.js`:
   ```javascript
   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' });
     }
   };
   ```

### Step 7: Set Up the Express Server
1. **Create the Server**:
   Create `src/index.js`:
   ```javascript
   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}`);
   });
   ```

### Step 8: Test the API
1. **Register a User**:
   ```http
   POST /api/auth/register
   Content-Type: application/json

   {
     "username": "john",
     "email": "john@example.com",
     "password": "password"
   }
   ```

2. **Login a User**:
   ```http
   POST /api/auth/login
   Content-Type: application/json

   {
     "email": "john@example.com",
     "password": "password"
   }
   ```

3. **Get User Profile (Protected Route)**:
   ```http
   GET /api/auth/profile
   Authorization: Bearer <token>
   ```

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.

Disclaimer: This content is generated by AI.