Logo

dev-resources.site

for different kinds of informations.

JWT Authentication With NodeJS

Published at
1/10/2025
Categories
node
authentication
jwt
Author
pedrotech
Categories
3 categories in total
node
open
authentication
open
jwt
open
Author
9 person written this
pedrotech
open
JWT Authentication With NodeJS

Create a Custom Authentication System with Node.js and JWT

Authentication is essential for modern web applications. In this tutorial, we’ll build a custom authentication system using Node.js, JWT, and React. You'll learn how to set up secure user registration and login, manage authentication states, and handle token expiration with refresh tokens.


Prerequisites

Before starting, ensure you have:

  • Node.js installed on your system.
  • Basic understanding of React, Node.js, and JavaScript.
  • A code editor like VS Code.

Step 1: Set Up the Backend

Start by creating the backend for handling user authentication.

1.1 Initialize the Project

mkdir node-jwt-auth
cd node-jwt-auth
npm init -y
Enter fullscreen mode Exit fullscreen mode

1.2 Install Required Libraries

npm install express jsonwebtoken bcrypt cors
Enter fullscreen mode Exit fullscreen mode

Step 2: Implement the Backend

2.1 Create the Server

Create a file named server.js in the project root:

const express = require("express");
const jwt = require("jsonwebtoken");
const bcrypt = require("bcrypt");
const cors = require("cors");

const app = express();
app.use(express.json()); // Built-in middleware to parse JSON requests
app.use(cors());

// Mock user database
const users = [];

// JWT secret keys
const ACCESS_TOKEN_SECRET = "your_access_token_secret";
const REFRESH_TOKEN_SECRET = "your_refresh_token_secret";

// Refresh token storage
let refreshTokens = [];

// User registration endpoint
app.post("/register", async (req, res) => {
  const { username, password } = req.body;

  // Check if user already exists
  if (users.find((user) => user.username === username)) {
    return res.status(400).json({ error: "User already exists" });
  }

  // Hash the password
  const hashedPassword = await bcrypt.hash(password, 10);

  // Save user
  users.push({ username, password: hashedPassword });
  res.status(201).json({ message: "User registered successfully" });
});

// User login endpoint
app.post("/login", async (req, res) => {
  const { username, password } = req.body;

  // Find user
  const user = users.find((user) => user.username === username);
  if (!user || !(await bcrypt.compare(password, user.password))) {
    return res.status(400).json({ error: "Invalid username or password" });
  }

  // Generate tokens
  const accessToken = jwt.sign({ username }, ACCESS_TOKEN_SECRET, {
    expiresIn: "15m",
  });
  const refreshToken = jwt.sign({ username }, REFRESH_TOKEN_SECRET);

  refreshTokens.push(refreshToken); // Store refresh token
  res.json({ accessToken, refreshToken });
});

// Refresh token endpoint
app.post("/token", (req, res) => {
  const { token } = req.body;
  if (!token || !refreshTokens.includes(token)) {
    return res.status(403).json({ error: "Invalid refresh token" });
  }

  try {
    const user = jwt.verify(token, REFRESH_TOKEN_SECRET);
    const accessToken = jwt.sign(
      { username: user.username },
      ACCESS_TOKEN_SECRET,
      {
        expiresIn: "15m",
      }
    );
    res.json({ accessToken });
  } catch {
    res.status(403).json({ error: "Invalid token" });
  }
});

// Logout endpoint
app.post("/logout", (req, res) => {
  const { token } = req.body;
  refreshTokens = refreshTokens.filter((t) => t !== token);
  res.status(200).json({ message: "Logged out successfully" });
});

// Protected route
app.get("/protected", (req, res) => {
  const authHeader = req.headers.authorization;
  const token = authHeader && authHeader.split(" ")[1];

  if (!token) return res.status(401).json({ error: "Unauthorized" });

  try {
    const decoded = jwt.verify(token, ACCESS_TOKEN_SECRET);
    res.json({ message: "Welcome to the protected route!", user: decoded });
  } catch {
    res.status(403).json({ error: "Invalid or expired token" });
  }
});

// Start the server
app.listen(3001, () => {
  console.log("Server running on http://localhost:3001");
});
Enter fullscreen mode Exit fullscreen mode

Step 3: Set Up the Frontend

3.1 Initialize the React App

npx create-react-app client
cd client
npm install axios
Enter fullscreen mode Exit fullscreen mode

Step 4: Create React Components

4.1 Create an AuthContext

Inside the src folder, create a new file AuthContext.js:

import React, { createContext, useState, useContext } from "react";
import axios from "axios";

const AuthContext = createContext();

export const AuthProvider = ({ children }) => {
  const [user, setUser] = useState(null);

  const login = async (username, password) => {
    try {
      const { data } = await axios.post("http://localhost:3001/login", {
        username,
        password,
      });
      localStorage.setItem("accessToken", data.accessToken);
      localStorage.setItem("refreshToken", data.refreshToken);
      setUser({ username });
    } catch (err) {
      console.error(err);
      alert("Invalid credentials");
    }
  };

  const logout = async () => {
    const refreshToken = localStorage.getItem("refreshToken");
    await axios.post("http://localhost:3001/logout", { token: refreshToken });
    localStorage.removeItem("accessToken");
    localStorage.removeItem("refreshToken");
    setUser(null);
  };

  return (
    <AuthContext.Provider value={{ user, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
};

export const useAuth = () => useContext(AuthContext);
Enter fullscreen mode Exit fullscreen mode

4.2 Create Login and Protected Components

Login Component
import React, { useState } from "react";
import { useAuth } from "./AuthContext";

const Login = () => {
  const { login } = useAuth();
  const [username, setUsername] = useState("");
  const [password, setPassword] = useState("");

  return (
    <div>
      <h1>Login</h1>
      <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={() => login(username, password)}>Login</button>
    </div>
  );
};

export default Login;
Enter fullscreen mode Exit fullscreen mode
Protected Component
import React, { useEffect, useState } from "react";
import axios from "axios";

const Protected = () => {
  const [message, setMessage] = useState("");

  useEffect(() => {
    const fetchData = async () => {
      try {
        const token = localStorage.getItem("accessToken");
        const { data } = await axios.get("http://localhost:3001/protected", {
          headers: { Authorization: `Bearer ${token}` },
        });
        setMessage(data.message);
      } catch {
        setMessage("Unauthorized");
      }
    };
    fetchData();
  }, []);

  return <div>{message}</div>;
};

export default Protected;
Enter fullscreen mode Exit fullscreen mode

Step 5: Run the Application

  1. Start the backend server:
   node server.js
Enter fullscreen mode Exit fullscreen mode
  1. Start the React frontend:
   npm start
Enter fullscreen mode Exit fullscreen mode

Final Thoughts

Congratulations! You've built a secure authentication system with Node.js, JWT, and React. For more tutorials, visit my YouTube Channel.

node Article's
30 articles in total
Favicon
Breaking the Scale Barrier: 1 Million Messages with NodeJS and Kafka
Favicon
assert in Nodejs and its usage in Grida source code
Favicon
Understanding Node.js Cluster: The Core Concepts
Favicon
🌟 A New Adventure Begins! 🛵🍕
Favicon
How “Silicon Valley” Inspired Me to Create a Photo Compressor CLI for Web Developers
Favicon
How Does This Jewelry Customization Site Work? Need Insights!
Favicon
Building a Secure Authentication API with TypeScript, Node.js, and MongoDB
Favicon
Understanding OAuth 1.0a Signature Generation: Postman vs. Node.js Library and Custom Implementation
Favicon
How to Fix the “Record to Delete Does Not Exist” Error in Prisma
Favicon
[Boost]
Favicon
Run NextJS App in shared-hosting cPanel domain!
Favicon
Construindo uma API segura e eficiente com @fastify/jwt e @fastify/mongodb
Favicon
New ways to engage with the stdlib community!
Favicon
Sore Throat: Causes, Symptoms, and Treatment
Favicon
Back to MonDEV 2025
Favicon
🌟 How to Fix Node.js Path Issues in VS Code (Step-by-Step Guide)
Favicon
How to write unit tests and E2E tests for NestJS applications
Favicon
Cookies auto clearing after browser refresh issue , CORS related express cookies issue
Favicon
Exploring TypeScript Support in Node.js v23.6.0
Favicon
Mastering Backend Node.js Folder Structure, A Beginner’s Guide
Favicon
how to setup express api from scratch
Favicon
Load Balancing Node.js Applications with Nginx Upstream Configuration
Favicon
Using LRU Cache in Node.js and TypeScript
Favicon
Welcome to Siitecch! Your Go-To Platform for Beginner-Friendly Full-Stack JavaScript Learning.
Favicon
I Really like Middleware in NodeJs/Express.
Favicon
Your own Telegram bot on NodeJS with TypeScript, Telegraf and Fastify (Part 3)
Favicon
Understanding Node.js Cluster: The Core Concepts
Favicon
JWT Authentication With NodeJS
Favicon
Stream de Arquivo PDF ou Imagem S3 - AWS
Favicon
Understanding Node.js Alpine Versions: A Lightweight Choice for Your Projects

Featured ones: