Integrating JSON Web Tokens (JWT) into an Express.js and React.js application is a common approach for handling user authentication and authorization. JWTs provide a secure way to transmit information between parties and can be used to verify the authenticity of the data. Below, I'll guide you through the process of implementing JWT-based authentication in an Express.js backend and a React.js frontend.
1. Setting Up the Express.js Backend
1.1 Install Dependencies
npm install express express-validator jsonwebtoken bcrypt cors
express
: Web framework for Node.jsexpress-validator
: Middleware for data validationjsonwebtoken
: Library for handling JWTsbcrypt
: Library for hashing passwordscors
: Middleware for enabling Cross-Origin Resource Sharing
1.2 Create the Express App (server.js)
const express = require('express');
const { body, validationResult } = require('express-validator');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
const cors = require('cors');
const app = express();
app.use(express.json());
app.use(cors());
const secretKey = 'your_secret_key';
// Dummy user data (replace with database)
const users = [
{
id: 1,
username: 'user1',
password: '$2b$10$OfNBzvMk9grhHoueARtvQe1G.9vj4tOTL/zIG2LzgsC.BgF8RbS.C', // Hashed password for "password123"
},
];
// Middleware to verify JWT
const verifyToken = (req, res, next) => {
const token = req.header('Authorization');
if (!token) return res.status(401).json({ message: 'Access denied' });
try {
const decoded = jwt.verify(token, secretKey);
req.user = decoded;
next();
} catch (error) {
res.status(400).json({ message: 'Invalid token' });
}
};
// Routes
app.post('/api/login', [
body('username').notEmpty(),
body('password').notEmpty(),
], (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
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 }, secretKey, { expiresIn: '1h' });
res.json({ token });
});
app.get('/api/data', verifyToken, (req, res) => {
res.json({ message: 'Protected data', user: req.user });
});
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
This is a basic Express.js setup with a login route (/api/login
) for authentication and a protected route (/api/data
) that requires a valid JWT for access.
2. Setting Up the React.js Frontend
2.1 Create a React App
npx create-react-app react-jwt-auth
cd react-jwt-auth
2.2 Install Dependencies
npm install axios react-router-dom
axios
: Library for making HTTP requestsreact-router-dom
: Library for handling routing in React
2.3 Create Components and Routes
App.js
import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import Home from './components/Home';
import Login from './components/Login';
import ProtectedData from './components/ProtectedData';
function App() {
return (
<Router>
<Switch>
<Route path="/" exact component={Home} />
<Route path="/login" component={Login} />
<Route path="/protected-data" component={ProtectedData} />
</Switch>
</Router>
);
}
export default App;
Home.js
import React from 'react';
import { Link } from 'react-router-dom';
const Home = () => {
return (
<div>
<h1>Welcome to the Home Page</h1>
<Link to="/login">Login</Link>
</div>
);
};
export default Home;
Login.js
import React, { useState } from 'react';
import axios from 'axios';
const Login = ({ history }) => {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const handleLogin = async (e) => {
e.preventDefault();
try {
const response = await axios.post('http://localhost:5000/api/login', { username, password });
localStorage.setItem('token', response.data.token);
history.push('/protected-data');
} catch (error) {
console.error('Login failed', error);
}
};
return (
<div>
<h1>Login</h1>
<form onSubmit={handleLogin}>
<label>
Username:
<input type="text" value={username} onChange={(e) => setUsername(e.target.value)} />
</label>
<br />
<label>
Password:
<input type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
</label>
<br />
<button type="submit">Login</button>
</form>
</div>
);
};
export default Login;
ProtectedData.js
import React, { useEffect, useState } from 'react';
import axios from 'axios';
const ProtectedData = () => {
const [data, setData] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await axios.get('http://localhost:5000/api/data', {
headers: { Authorization: localStorage.getItem('token') },
});
setData(response.data);
} catch (error) {
console.error('Failed to fetch protected data', error);
}
};
fetchData();
}, []);
return (
<div>
<h1>Protected Data</h1>
{data ? (
<p>{data.message}</p>
) : (
<p>Loading...</p>
)}
</div>
);
};
export default ProtectedData;
3. Running the Application
3.1 Start the Express.js Backend
node server.js
3.2 Start the React.js Frontend
npm start
Visit http://localhost:3000 in your browser. You can now navigate between the home page, login page, and the protected data page. The protected data page will require authentication, and only authenticated users will be able to access it.
This example provides a basic implementation of
JWT-based authentication in an Express.js backend and a React.js frontend. Keep in mind that in a production environment, you should use secure practices for storing and handling sensitive information, such as user credentials and secret keys. Additionally, consider using HTTPS to encrypt data transmitted between the client and server.