🔐 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!
Hi Everyone, please do not spam in comments.