Authentication is a critical aspect of any web application. Whether it’s protecting sensitive user data or controlling access to certain features, knowing how to set up secure authentication is essential. In this interactive blog post, we’ll dive into how to set up authentication in a MERN (MongoDB, Express, React, Node.js) stack application. We’ll cover step-by-step instructions, from creating a backend authentication system to integrating it with the frontend.
Let’s break this down into five main sections:
Before diving into authentication, let’s first make sure the MERN stack is set up. If you don’t have it installed yet, follow these steps:
Make sure Node.js is installed on your system. Run this command to check the version:
bash
Copy code
node -v
Create a new directory for your project:
bash
Copy code
mkdir mern-auth-app
cd mern-auth-app
Initialize a package.json
file:
bash
Copy code
npm init -y
Install the necessary dependencies:
bash
Copy code
npm install express mongoose dotenv jsonwebtoken bcryptjs
For the frontend (React), in another terminal, create the React app:
bash
Copy code
npx create-react-app client
Install Axios in the React app to handle HTTP requests:
bash
Copy code
cd client
npm install axios
In this section, we’ll set up a Node.js server with Express and configure MongoDB as our database.
Create a server.js
file at the root of your project:
bash
Copy code
touch server.js
Add the following code to server.js
to set up an Express server and connect to MongoDB:
javascript
Copy code
const express =
require(
‘express’);
const mongoose =
require(
‘mongoose’);
const dotenv =
require(
‘dotenv’);
dotenv.
config();
const app =
express();
app.
use(express.
json());
// Connect to MongoDB
mongoose.
connect(process.
env.
MONGO_URI, {
useNewUrlParser:
true,
useUnifiedTopology:
true })
.
then(
() =>
console.
log(
‘MongoDB connected’))
.
catch(
err =>
console.
log(err));
// Sample Route
app.
get(
‘/’,
(req, res) => {
res.
send(
‘API is running…’);
});
const
PORT = process.
env.
PORT ||
5000;
app.
listen(
PORT,
() =>
console.
log(
`Server running on port ${PORT}`));
Create a .env
file to store your MongoDB URI:
bash
Copy code
touch .
env
Add your MongoDB connection string in .env
:
env
Copy code
MONGO_URI=your_mongodb_connection_string
Now, let’s create a User
model to store user information like email and password.
Create a models
folder and inside it, a User.js
file:
bash
Copy code
mkdir models
touch models/User.js
Define the User schema:
javascript
Copy code
const mongoose =
require(
‘mongoose’);
const userSchema =
new mongoose.
Schema({
name: {
type:
String,
required:
true
},
email: {
type:
String,
required:
true,
unique:
true
},
password: {
type:
String,
required:
true
},
});
module.
exports = mongoose.
model(
‘User’, userSchema);
We will use JWT (JSON Web Tokens) for authentication. JWT is a widely used standard for securely transmitting information between parties as a JSON object.
We need to create a route to allow users to register by providing their name, email, and password.
Create a routes
folder and inside it, a auth.js
file:
bash
Copy code
mkdir routes
touch routes/auth.js
Add a registration route in auth.js
:
javascript
Copy code
const express =
require(
‘express’);
const
User =
require(
‘../models/User’);
const bcrypt =
require(
‘bcryptjs’);
const jwt =
require(
‘jsonwebtoken’);
const router = express.
Router();
// Register a user
router.
post(
‘/register’,
async (req, res) => {
const { name, email, password } = req.
body;
try {
// Check if the user already exists
let user =
await
User.
findOne({ email });
if (user) {
return res.
status(
400).
json({
message:
‘User already exists’ });
}
// Hash the password
const salt =
await bcrypt.
genSalt(
10);
const hashedPassword =
await bcrypt.
hash(password, salt);
// Save new user
user =
new
User({
name,
email,
password: hashedPassword
});
await user.
save();
// Generate JWT
const token = jwt.
sign({
userId: user.
_id }, process.
env.
JWT_SECRET, {
expiresIn:
‘1h’
});
res.
status(
201).
json({ token });
}
catch (err) {
console.
error(err.
message);
res.
status(
500).
send(
‘Server error’);
}
});
module.
exports = router;
In server.js
, add the route:
javascript
Copy code
app.
use(
‘/api/auth’,
require(
‘./routes/auth’));
Now, we need a login route where users can authenticate themselves by providing their email and password.
Add a login route in auth.js
:
javascript
Copy code
// Login a user
router.
post(
‘/login’,
async (req, res) => {
const { email, password } = req.
body;
try {
// Check if the user exists
let user =
await
User.
findOne({ email });
if (!user) {
return res.
status(
400).
json({
message:
‘Invalid credentials’ });
}
// Validate password
const isMatch =
await bcrypt.
compare(password, user.
password);
if (!isMatch) {
return res.
status(
400).
json({
message:
‘Invalid credentials’ });
}
// Generate JWT
const token = jwt.
sign({
userId: user.
_id }, process.
env.
JWT_SECRET, {
expiresIn:
‘1h’
});
res.
json({ token });
}
catch (err) {
console.
error(err.
message);
res.
status(
500).
send(
‘Server error’);
}
});
Next, let’s create a simple frontend using React for user registration and login.
Inside the client
folder, create a components
folder for the forms.
bash
Copy code
mkdir client/src/components
Create a Register.js
component:
javascript
Copy code
import
React, { useState }
from
‘react’;
import axios
from
‘axios’;
const
Register = () => {
const [formData, setFormData] =
useState({
name:
”,
email:
”,
password:
”
});
const { name, email, password } = formData;
const
onChange = e =>
setFormData({ ...formData, [e.
target.
name]: e.
target.
value });
const
onSubmit =
async e => {
e.
preventDefault();
try {
const config = {
headers: {
‘Content-Type’:
‘application/json’ } };
const body =
JSON.
stringify({ name, email, password });
const res =
await axios.
post(
‘/api/auth/register’, body, config);
console.
log(res.
data);
// Handle response (token)
}
catch (err) {
console.
error(err.
response.
data);
}
};
return (
<form onSubmit={onSubmit}>
<input type=”text” name=”name” value={name} onChange={onChange} placeholder=”Name” required />
<input type=”email” name=”email” value={email} onChange={onChange} placeholder=”Email” required />
<input type=”password” name=”password” value={password} onChange={onChange} placeholder=”Password” required />
<button type=”submit”>Register</button>
</form>
);
};
export
default
Register;
Repeat the process to create a Login.js
component with a similar structure but for logging in.
To connect the React frontend with the Node.js backend:
Make sure the proxy
field is set in client/package.json
:
json
Copy code
“proxy”:
“http://localhost:5000”
Use Axios in React components to make requests to the backend, as shown in the Register.js
component.
You now have a basic authentication system for your MERN stack application. By following this guide, you’ve learned how to set up user registration and login functionality using JWT. You can extend this by adding more security features, password resets, user roles, or OAuth for third-party logins.
Comments are closed