Introduction
Building an authentication system is one of the most important steps in creating a secure web application. It controls how users sign up, log in, and access protected content. When I decided to build a full authentication system using React and Node.js, my goal was to keep it simple, secure, and easy to understand.
This guide shares the approach I followed, along with practical steps that can help you build your own authentication system from scratch.
Understanding the Core Flow
Before writing any code, I focused on the basic flow of authentication. A typical system includes:
User registration
User login
Password protection
Session or token management
Access to protected routes
Understanding this flow made it easier to structure both the frontend and backend.
Setting Up the Backend with Node.js
I started by setting up a Node.js server using Express. This handled all authentication logic and database communication.
After initializing the project, I installed essential packages:
npm install express bcryptjs jsonwebtoken cors dotenv
bcryptjs for password hashing
jsonwebtoken for authentication tokens
cors for handling cross-origin requests
Then I created a basic server:
const express = require("express");
const app = express();
app.use(express.json());
app.listen(5000, () => console.log("Server running"));
This formed the base for handling authentication routes.
Creating the User Model
Next, I set up a user structure in the database. Each user needed:
Name
Password (hashed)
When a user registers, their password is never stored as plain text. Instead, it is hashed using bcrypt:
const hashedPassword = await bcrypt.hash(password, 10);
This step is essential for security.
Building the Registration Endpoint
The registration route allows new users to create an account.
app.post("/register", async (req, res) => {
const { name, email, password } = req.body;
const hashedPassword = await bcrypt.hash(password, 10);
// Save user to database
res.json({ message: "User registered successfully" });
});
This route collects user data, hashes the password, and stores it securely.
Implementing Login with JWT
Login is where authentication becomes active. After verifying user credentials, I generated a JSON Web Token (JWT):
const token = jwt.sign({ id: user.id }, "secretkey", {
expiresIn: "1h",
});
This token is sent to the client and used for future requests.
The login route checks:
If the user exists
If the password matches
Only then is access granted.
Protecting Routes
To secure certain routes, I created a middleware that verifies the token:
const authMiddleware = (req, res, next) => {
const token = req.headers.authorization;
if (!token) return res.status(401).json({ message: "Unauthorized" });
try {
const verified = jwt.verify(token, "secretkey");
req.user = verified;
next();
} catch {
res.status(400).json({ message: "Invalid token" });
}
};
This ensures only authenticated users can access protected data.
Setting Up the React Frontend
On the frontend, I used React to create simple forms for registration and login.
The login form sends a request to the backend:
const response = await fetch("http://localhost:5000/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password }),
});
Once the token is received, I stored it in local storage:
localStorage.setItem("token", data.token);
This allows the user to stay logged in across sessions.
Handling Protected Pages in React
To restrict access to certain pages, I checked whether a token exists before rendering content.
If no token is found, the user is redirected to the login page. This simple check ensures only authenticated users can view protected sections.
Challenges I Faced
One challenge was handling token expiration. If a token expires, users need to log in again. I solved this by checking responses from protected routes and redirecting users when needed.
Another issue was managing state between components. Keeping authentication status consistent across the app required careful handling of local storage and React state.
Best Practices I Followed
To make the system more reliable, I followed a few important practices:
Never store plain-text passwords
Keep secret keys in environment variables
Validate user input on both the frontend and backend
Use HTTPS in production
Separate authentication logic from UI components
These steps helped improve both security and maintainability.
Final Thoughts
Building a full authentication system using React and Node.js gave a clear understanding of how secure user management works. While the process may seem complex at first, breaking it into smaller steps makes it more manageable and easier to implement in real projects.
With registration, login, token handling, and protected routes in place, there is now a solid foundation for authentication. This setup can be extended with features like password reset, email verification, and role-based access as applications grow. Such structured development approaches are commonly followed by a website design company in Coimbatore, where security, scalability, and clean architecture are important aspects of building reliable web applications.
Top comments (0)