Adding Authentication to Node.js API with JWT
Tags: node
Published: 2023-09-22 13:38:12

Previous articles:

[How to Make API using NodeJS and Express](https://eneaslari.com/articleview/how-to-make-api-using-nodejs-and-express-65098dfeb40791b8339e8506 "how-to-make-api-using-nodejs-and-express")

[Enhancing Our Node.js API: Hot Reloading with Nodemon, Bundling with Webpack & Using ES6](https://eneaslari.com/articleview/enhancing-our-nodejs-api-hot-reloading-with-nodemon-bundling-with-webpack--using-es6-65098f11b40791b8339e8527 "enhancing-our-nodejs-api-hot-reloading-with-nodemon-bundling-with-webpack--using-es6-")

#### 1. JWT Overview

JSON Web Tokens (JWT) offer a compact and self-contained way to securely transmit information between parties as a JSON object. In authentication, when the user logs in using their credentials, a server-side application provides a signed token to the client. The client then uses this token to access protected routes or resources.

#### 2. Setting up JWT Authentication

**Step 1**: Install the necessary packages:

```bash
npm install jsonwebtoken bcryptjs
```

- `jsonwebtoken` - For generating and verifying JWTs.
- `bcryptjs` - For hashing and checking user passwords.

**Step 2**: Set up a User Model with Password Hashing (models/user.js):

If not already set, add a password field to the user schema. Then, implement password hashing using bcrypt:

```javascript
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');

const userSchema = new mongoose.Schema({
    // ... other fields ...
    password: {
        type: String,
        required: true,
    },
});

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

module.exports = mongoose.model('User', userSchema);
```

**Step 3**: Implement Signup and Login Routes:

- **Signup**:
    ```javascript
    const bcrypt = require('bcryptjs');
    const User = require('../models/user');

    exports.signup = async (req, res) => {
        const user = new User(req.body);
        await user.save();
        res.status(201).json({ message: "User registered successfully!" });
    };
    ```

- **Login**:
    ```javascript
    const jwt = require('jsonwebtoken');

    exports.login = async (req, res) => {
        const { email, password } = req.body;
        const user = await User.findOne({ email });

        if (!user || !await bcrypt.compare(password, user.password)) {
            return res.status(401).json({ error: "Invalid email or password" });
        }

        const token = jwt.sign({ userId: user.id }, 'YOUR_SECRET_KEY', { expiresIn: '1h' });

        res.json({ token });
    };
    ```

Remember to replace `'YOUR_SECRET_KEY'` with your actual secret key.

#### 3. Protecting Routes

To protect routes, we create a middleware that verifies the JWT:

**Middleware (middlewares/auth.js)**:

```javascript
const jwt = require('jsonwebtoken');

exports.verifyToken = (req, res, next) => {
    const token = req.headers['authorization'];

    if (!token) {
        return res.status(403).json({ error: 'No token provided' });
    }

    jwt.verify(token, 'YOUR_SECRET_KEY', (err, decoded) => {
        if (err) {
            return res.status(401).json({ error: 'Unauthorized' });
        }
        req.userId = decoded.userId;
        next();
    });
};
```

Now, apply this middleware to any route you want to protect. For instance, to protect a route that fetches user data:

```javascript
const express = require('express');
const { verifyToken } = require('../middlewares/auth');

const router = express.Router();

router.get('/protectedRoute', verifyToken, (req, res) => {
    // Your protected route logic here
});

module.exports = router;
```

With the setup above, only authenticated users with valid JWTs can access the protected routes. Remember to handle security considerations, such as storing JWTs securely in frontend applications, using HTTPS, and frequently rotating your secret keys.

With this, your Node.js and Express API now has a robust authentication mechanism using JWTs and can protect routes from unauthorized access.