Skip to content

JWT authentication in Next.js with cookie storage, reusable auth functions, and HOCs for route protection. Provides secure session management and smooth navigation for authenticated and public users.

Notifications You must be signed in to change notification settings

shahmeerrizwan/Authentication_boilerplate

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

28 Commits
 
 
 
 
 
 

Repository files navigation

JWT Authentication for Backend and Frontend

This README file provides a step-by-step guide for implementing JSON Web Token (JWT) authentication in a full-stack application, using a Node.js backend and a Next.js frontend.

Table of Contents

  1. Overview
  2. Backend
  3. Frontend
  4. Best Practices
  5. References

Overview

JWT (JSON Web Token) is a compact, self-contained way to securely transmit information between parties. It is widely used for authentication and data exchange in web applications.


Backend

Prerequisites

  • Node.js installed
  • Basic understanding of Express.js
  • A database (e.g., MongoDB, PostgreSQL) for storing user data

Installation

  1. Initialize your Node.js project:
    npm init -y
  2. Install necessary dependencies:
    npm install express jsonwebtoken bcryptjs dotenv body-parser cors

Project Structure

Example backend project structure:

backend/
|-- src/
|   |-- controllers/
|   |-- middleware/
|   |-- routes/
|   |-- utils/
|   |-- app.js
|-- .env
|-- package.json
|-- README.md

Implementation Steps

1. Create JWT Utility Functions

Create a utility file for JWT operations (e.g., utils/jwt.js):

import jwt from 'jsonwebtoken';
const SECRET_KEY = process.env.JWT_SECRET || 'your-secret-key';

// Generate a token
export const generateToken = (payload, expiresIn = '1h') => {
  return jwt.sign(payload, SECRET_KEY, { expiresIn });
};

// Verify a token
export const verifyToken = (token) => {
  try {
    return jwt.verify(token, SECRET_KEY);
  } catch (error) {
    throw new Error('Invalid or expired token');
  }
};

2. Protect Routes with Middleware

Create a middleware function to protect routes (e.g., middleware/authMiddleware.js):

import { verifyToken } from '../utils/jwt.js';

export const authenticate = (req, res, next) => {
  const token = req.headers['authorization']?.split(' ')[1];

  if (!token) {
    return res.status(401).json({ message: 'Access token is required' });
  }

  try {
    const decoded = verifyToken(token);
    req.user = decoded;
    next();
  } catch (error) {
    return res.status(401).json({ message: 'Invalid or expired token' });
  }
};

3. Create Routes

Create an authRoutes.js file:

import express from 'express';
import bcrypt from 'bcryptjs';
import { generateToken } from '../utils/jwt.js';
import { authenticate } from '../middleware/authMiddleware.js';
const router = express.Router();

// Mock user data (use a real database in production)
const users = [];

// Register route
router.post('/register', async (req, res) => {
  const { username, password } = req.body;
  const hashedPassword = await bcrypt.hash(password, 10);
  users.push({ username, password: hashedPassword });
  res.status(201).json({ message: 'User registered successfully' });
});

// Login route
router.post('/login', async (req, res) => {
  const { username, password } = req.body;
  const user = users.find(u => u.username === username);

  if (!user || !(await bcrypt.compare(password, user.password))) {
    return res.status(401).json({ message: 'Invalid credentials' });
  }

  const token = generateToken({ username });
  res.status(200).json({ token });
});

// Protected route
router.get('/profile', authenticate, (req, res) => {
  res.status(200).json({ message: 'Welcome to your profile', user: req.user });
});

export default router;

Frontend

Setup

  1. Create a Next.js project:
    npx create-next-app@latest frontend
  2. Install Axios for API requests:
    npm install axios

Authentication Flow

  1. Registration:
    • Submit user credentials to the /register endpoint.
  2. Login:
    • Obtain a JWT token upon successful login and store it in a cookie or local storage.
  3. Access Protected Routes:
    • Attach the JWT token in the Authorization header for API requests.

API Integration

Create an Axios Instance

Create a utility file for Axios (e.g., utils/axios.js):

import axios from 'axios';

const apiClient = axios.create({
  baseURL: 'http://localhost:5000/api',
});

export const setAuthToken = (token) => {
  if (token) {
    apiClient.defaults.headers.common['Authorization'] = `Bearer ${token}`;
  } else {
    delete apiClient.defaults.headers.common['Authorization'];
  }
};

export default apiClient;

Example Components

Login Component:

'use client';
import { useState } from 'react';
import axios, { setAuthToken } from '../utils/axios';

const Login = () => {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');

  const handleLogin = async () => {
    try {
      const response = await axios.post('/login', { username, password });
      const token = response.data.token;
      setAuthToken(token);
      localStorage.setItem('token', token);
      alert('Login successful!');
    } catch (error) {
      console.error(error);
      alert('Login failed');
    }
  };

  return (
    <div>
      <input
        type="text"
        placeholder="Username"
        value={username}
        onChange={(e) => setUsername(e.target.value)}
      />
      <input
        type="password"
        placeholder="Password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
      />
      <button onClick={handleLogin}>Login</button>
    </div>
  );
};

export default Login;

Protected Route Component:

'use client';
import { useEffect, useState } from 'react';
import axios from '../utils/axios';

const Profile = () => {
  const [profile, setProfile] = useState(null);

  useEffect(() => {
    const fetchProfile = async () => {
      try {
        const response = await axios.get('/profile');
        setProfile(response.data);
      } catch (error) {
        console.error('Error fetching profile:', error);
      }
    };
    fetchProfile();
  }, []);

  if (!profile) return <div>Loading...</div>;

  return (
    <div>
      <h1>{profile.message}</h1>
      <pre>{JSON.stringify(profile.user, null, 2)}</pre>
    </div>
  );
};

export default Profile;

Best Practices

  1. Store the secret key securely: Use environment variables to manage sensitive data.
  2. Use HTTPS: Ensure tokens are transmitted securely.
  3. Set token expiration: Use short-lived tokens and refresh them periodically.
  4. Validate token payload: Avoid storing sensitive data in the token.
  5. Use secure storage: Prefer HttpOnly cookies for storing tokens over local storage.

References

About

JWT authentication in Next.js with cookie storage, reusable auth functions, and HOCs for route protection. Provides secure session management and smooth navigation for authenticated and public users.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published