Secure React + Express Login System with JWT

Aamir Khan
0
Secure React + Express Login System with JWT

🔐 Secure React + Express Login System with JWT

Secure React + Express Login System with JWT

Meta Description: Learn how to build a secure login system using React, Express.js, and JSON Web Tokens (JWT). Perfect for junior developers, students, and self-taught coders.

🚀 Introduction

When building full stack applications, securing user authentication is critical. JSON Web Tokens (JWT) make this easy and stateless. In this beginner-friendly tutorial, we’ll walk through creating a simple login system using React for the frontend, Express for the backend, and JWT for authentication.

This tutorial is ideal for junior developers, students, or self-taught coders diving into real-world full stack security.

📦 Step 1: Set Up the Backend (Express + JWT)

🔧 Install Dependencies

npm install express cors jsonwebtoken bcryptjs dotenv

📁 Folder Structure

backend/
├── server.js
├── .env
└── users.js

🔐 .env

JWT_SECRET=supersecurekey

🔙 users.js (mock users DB)

const bcrypt = require('bcryptjs');

const users = [
  {
    id: 1,
    username: 'testuser',
    password: bcrypt.hashSync('password123', 10)
  }
];

module.exports = users;

🚀 server.js

const express = require('express');
const jwt = require('jsonwebtoken');
const cors = require('cors');
const bcrypt = require('bcryptjs');
const users = require('./users');
require('dotenv').config();

const app = express();
app.use(cors());
app.use(express.json());

app.post('/login', (req, res) => {
  const { username, password } = req.body;
  const user = users.find(u => u.username === username);
  if (!user || !bcrypt.compareSync(password, user.password)) {
    return res.status(401).json({ message: 'Invalid credentials' });
  }
  const token = jwt.sign({ id: user.id, username: user.username }, process.env.JWT_SECRET, {
    expiresIn: '1h'
  });
  res.json({ token });
});

app.get('/protected', (req, res) => {
  const authHeader = req.headers.authorization;
  if (!authHeader) return res.status(403).json({ message: 'No token provided' });

  const token = authHeader.split(' ')[1];
  jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
    if (err) return res.status(403).json({ message: 'Invalid token' });
    res.json({ message: `Welcome ${user.username}!` });
  });
});

app.listen(5000, () => console.log('Server running on port 5000'));

⚛️ Step 2: Set Up the Frontend (React + Fetch)

📁 Folder Structure

frontend/
├── App.js
├── Login.js
└── Protected.js

🔐 Login.js

import { useState } from 'react';

function Login({ setToken }) {
  const [form, setForm] = useState({ username: '', password: '' });

  const handleChange = (e) => {
    setForm({ ...form, [e.target.name]: e.target.value });
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    const res = await fetch('http://localhost:5000/login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(form),
    });
    const data = await res.json();
    if (data.token) {
      setToken(data.token);
      localStorage.setItem('token', data.token);
    } else {
      alert('Login failed');
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input name="username" onChange={handleChange} placeholder="Username" />
      <input name="password" type="password" onChange={handleChange} placeholder="Password" />
      <button type="submit">Login</button>
    </form>
  );
}

export default Login;

🔐 Protected.js

import { useEffect, useState } from 'react';

function Protected({ token }) {
  const [message, setMessage] = useState('');

  useEffect(() => {
    fetch('http://localhost:5000/protected', {
      headers: {
        Authorization: `Bearer ${token}`,
      },
    })
      .then(res => res.json())
      .then(data => setMessage(data.message));
  }, [token]);

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

export default Protected;

📌 App.js

import { useState } from 'react';
import Login from './Login';
import Protected from './Protected';

function App() {
  const [token, setToken] = useState(localStorage.getItem('token') || '');

  return (
    <div>
      {token ? <Protected token={token} /> : <Login setToken={setToken} />}
    </div>
  );
}

export default App;

📷 Preview

🧠 Recap

  • Created a secure backend with Express and JWT
  • Built a frontend with React login and protected routes
  • Connected both ends using fetch and bearer tokens

You now have a real-world working login system ready to extend with databases, refresh tokens, and more!

💬 Conclusion

Authentication is the gatekeeper of your full stack app. With JWT, it's clean, simple, and secure. This project gives you a solid foundation to build more robust auth systems.

🗣️ What’s Next?

  • 👉 Try adding signup, logout, and refresh tokens
  • 🔁 Share this guide with your friends
  • 📬 Subscribe to CodeJourneyWithAamir for more full stack magic
  • 💬 Leave a comment if you tried it or got stuck!
💖

Enjoyed the Post?

If my blog helped you, please consider supporting me!

Support Me 💖

Post a Comment

0 Comments

Hi Everyone, please do not spam in comments.

Post a Comment (0)

Made with Love by

Welcome to Code Journey with Aamir – your one-stop hub for full-stack development tutorials, projec…