In a MERN (MongoDB, Express.js, React.js, Node.js) app, managing sessions and cookies usually needs a library like express-session
for session control and cookie-parser
for understanding cookies. I'll show you how to add session and cookies handling to a simple MERN authentication example below.
Backend (Node.js/Express):
- Install necessary packages:
cd mern-auth-backend
npm install express express-session mongoose cors bcrypt jsonwebtoken cookie-parser
- Update
server.js
:
const express = require('express');
const session = require('express-session');
const mongoose = require('mongoose');
const cors = require('cors');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const cookieParser = require('cookie-parser');
const app = express();
const PORT = process.env.PORT || 5000;
const JWT_SECRET = 'your-secret-key';
const SESSION_SECRET = 'your-session-secret'; // Change this to a long, random string in production
app.use(cors({ credentials: true, origin: 'http://localhost:3000' }));
app.use(express.json());
app.use(cookieParser());
app.use(session({
secret: SESSION_SECRET,
resave: true,
saveUninitialized: true,
cookie: {
maxAge: 1000 * 60 * 60 * 24, // 1 day
httpOnly: true,
secure: false, // Set to true in production if using HTTPS
},
}));
mongoose.connect('mongodb://localhost:27017/mern-auth', {
useNewUrlParser: true,
useUnifiedTopology: true,
});
const userSchema = new mongoose.Schema({
username: String,
password: String,
});
const User = mongoose.model('User', userSchema);
app.post('/api/register', async (req, res) => {
try {
const { username, password } = req.body;
const hashedPassword = await bcrypt.hash(password, 10);
const user = new User({ username, password: hashedPassword });
await user.save();
res.status(201).json({ message: 'User registered successfully' });
} catch (error) {
res.status(500).json({ error: 'Internal Server Error' });
}
});
app.post('/api/login', async (req, res) => {
try {
const { username, password } = req.body;
const user = await User.findOne({ username });
if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const passwordMatch = await bcrypt.compare(password, user.password);
if (!passwordMatch) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const token = jwt.sign({ username: user.username }, JWT_SECRET);
// Set the token in a cookie
res.cookie('token', token, {
httpOnly: true,
maxAge: 1000 * 60 * 60 * 24, // 1 day
secure: false, // Set to true in production if using HTTPS
});
res.json({ token });
} catch (error) {
res.status(500).json({ error: 'Internal Server Error' });
}
});
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
In this updated backend code:
The
express-session
middleware is used for session handling, andcookie-parser
is used to parse cookies.The session is configured to use a session secret and set cookies with specific properties.
When a user logs in, the server generates a JWT token and sets it in a cookie.
Frontend (React):
- Install the
axios
library for making HTTP requests:
cd mern-auth-client
npm install axios
- Update
src/App.js
:
import React, { useState, useEffect } from 'react';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
import axios from 'axios';
const App = () => {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [token, setToken] = useState('');
const handleRegister = () => {
fetch('/api/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ username, password }),
})
.then((res) => res.json())
.then((data) => alert(data.message))
.catch((error) => console.error('Error registering:', error));
};
const handleLogin = async () => {
try {
const response = await axios.post('http://localhost:5000/api/login', {
username,
password,
});
const { token } = response.data;
setToken(token);
alert('Login successful');
} catch (error) {
alert('Invalid credentials');
}
};
const handleLogout = () => {
// Clear the token in the cookie and state
document.cookie = 'token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
setToken('');
};
useEffect(() => {
// You can use the token for authenticated requests here
// For simplicity, let's just log it to the console
console.log('Token:', token);
}, [token]);
return (
<Router>
<div>
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/register">Register</Link>
</li>
<li>
<Link to="/login">Login</Link>
</li>
</ul>
</nav>
<hr />
<Route path="/" exact>
<h2>Home</h2>
{token ? (
<button onClick={handleLogout}>Logout</button>
) : (
<p>Please register or log in.</p>
)}
</Route>
<Route path="/register">
<h2>Register</h2>
<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={handleRegister}>Register</button>
</Route>
<Route path="/login">
<h2>Login</h2>
<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>
</Route>
</div>
</Router>
);
};
export default App;
In this updated frontend code:
The
axios
library helps make HTTP requests.- When a user logs in, the frontend sends a POST request to the
/api/login
endpoint. The server sets the token in a cookie and returns it in the response.
- When a user logs in, the frontend sends a POST request to the
Now, when a user logs in, a session starts, and a token is saved in a cookie for later authenticated requests. This is a simple setup. In a real-world situation, you'd want to add more security steps, like using HTTPS and protecting the JWT secret.