Tech Radioo

Your Daily Dose of Tech

Add JSON Web Token (JWT) Authentication to your API

Authenticating an API is crucial for ensuring security, controlling access, and maintaining data integrity. By verifying the identity of users or systems accessing the API, authentication helps prevent unauthorized access, data breaches, and malicious activity. It also enables the enforcement of permissions and roles, ensuring that only authorized entities can interact with specific endpoints or resources. Proper API authentication safeguards sensitive data, enhances trust in the system, and supports compliance with industry standards and regulations.

To add JWT (JSON Web Token) authentication to your Node.js API, follow these steps:

1. Install Required Packages

You’ll need to install a few npm packages for JWT and password hashing. Here are the key ones:

$ npm install jsonwebtoken bcryptjs express body-parser
  • jsonwebtoken: To sign and verify tokens.
  • bcryptjs: To hash passwords.
  • express: For creating the API.
  • body-parser: To parse JSON request bodies.

2. Create the API Structure

Create a basic folder structure:

/my-app
  /config
    db.js          # For database connection
  /controllers
    authController.js   # For handling authentication logic
  /models
    User.js        # User model
  /routes
    authRoutes.js   # Routes for authentication
  /middleware
    auth.js         # Middleware for protecting routes
  server.js        # Entry point of the application

3. Create User Model

For simplicity, let’s assume you’re using MongoDB with Mongoose for user storage.

// /models/User.js
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');

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

// Hash password before saving user
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();
});

// Compare entered password with stored hash
userSchema.methods.matchPassword = async function (enteredPassword) {
  return await bcrypt.compare(enteredPassword, this.password);
};

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

4. Create Authentication Controller

In this controller, you’ll define methods for user registration, login, and generating JWT tokens.

// /controllers/authController.js
const User = require('../models/User');
const jwt = require('jsonwebtoken');

// Register a new user
exports.register = async (req, res) => {
  const { username, password } = req.body;
  try {
    const user = await User.create({ username, password });
    const token = generateToken(user._id);
    res.status(201).json({ token });
  } catch (error) {
    res.status(400).json({ error: 'User registration failed' });
  }
};

// Login an existing user
exports.login = async (req, res) => {
  const { username, password } = req.body;
  try {
    const user = await User.findOne({ username });
    if (!user || !(await user.matchPassword(password))) {
      return res.status(401).json({ error: 'Invalid credentials' });
    }
    const token = generateToken(user._id);
    res.json({ token });
  } catch (error) {
    res.status(500).json({ error: 'Login failed' });
  }
};

// Generate JWT Token
const generateToken = (id) => {
  return jwt.sign({ id }, 'your_jwt_secret', { expiresIn: '1h' });
};

5. Create Authentication Routes

Now, set up the routes for registration and login:

// /routes/authRoutes.js
const express = require('express');
const { register, login } = require('../controllers/authController');

const router = express.Router();

router.post('/register', register);
router.post('/login', login);

module.exports = router;

6. Create Middleware to Protect Routes

You’ll need middleware to verify the JWT for protected routes:

// /middleware/auth.js
const jwt = require('jsonwebtoken');
const User = require('../models/User');

const 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, 'your_jwt_secret');
      
      // Attach user to request
      req.user = await User.findById(decoded.id).select('-password');
      
      next();
    } catch (error) {
      res.status(401).json({ error: 'Not authorized, token failed' });
    }
  }

  if (!token) {
    res.status(401).json({ error: 'Not authorized, no token' });
  }
};

module.exports = protect;

7. Protect Your Routes

Now that the authentication logic is ready, you can protect any route by using the protect middleware:

// /routes/privateRoutes.js
const express = require('express');
const protect = require('../middleware/auth');

const router = express.Router();

router.get('/profile', protect, (req, res) => {
  res.json({ user: req.user });
});

module.exports = router;

8. Set Up the Server

Finally, tie everything together in your server.js file:

// server.js
const express = require('express');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');

const authRoutes = require('./routes/authRoutes');
const privateRoutes = require('./routes/privateRoutes');

const app = express();

// Database connection
mongoose.connect('mongodb://localhost:27017/myapp', {
  useNewUrlParser: true,
  useUnifiedTopology: true,
});

app.use(bodyParser.json());

app.use('/api/auth', authRoutes);
app.use('/api/private', privateRoutes);

app.listen(5000, () => {
  console.log('Server running on http://localhost:5000');
});

9. Testing the API

  • Register: Send a POST request to /api/auth/register with a JSON body { "username": "yourusername", "password": "yourpassword" }.
  • Login: Send a POST request to /api/auth/login with a JSON body { "username": "yourusername", "password": "yourpassword" }.
  • Access Protected Route: Use the token received from login or register and send it in the Authorization header (as Bearer <token>) to access protected routes like /api/private/profile.

Conclusion

By following these steps, you can successfully add JWT authentication to your Node.js API, ensuring secure user authentication and access to protected routes.

Leave a Reply

Your email address will not be published. Required fields are marked *