🚀 React Mastery Series – Day 27: Authentication & Authorization in React – JWT, Protected Routes & Role-Based Access Control (RBAC)
Welcome back to the React Mastery Series!
In the previous article, we explored React Testing and learned how to write reliable applications using:
- Unit Testing
- Integration Testing
- End-to-End (E2E) Testing
- React Testing Library
- Playwright
- Mocking APIs
- Enterprise testing strategies
Today, we'll cover one of the most critical topics in modern web development:
Authentication & Authorization in React
Almost every enterprise application requires users to log in before accessing sensitive information.
Examples include:
- Internet Banking
- E-Commerce Platforms
- Healthcare Portals
- Insurance Applications
- HR Management Systems
- CRM Platforms
Building authentication correctly is essential for both security and user experience.
Authentication vs Authorization
Many developers use these terms interchangeably, but they have different meanings.
Authentication
Authentication answers the question:
Who are you?
Example:
Username
↓
Password
↓
Identity Verified
Authorization
Authorization answers the question:
What are you allowed to access?
Example:
User Logged In
↓
Check Role
↓
Admin?
↓
Yes → Admin Dashboard
No → User Dashboard
Authentication verifies identity.
Authorization verifies permissions.
Real-World Banking Example
Imagine an online banking system.
Two users log in.
Customer
↓
View Accounts
Transfer Money
Download Statements
Bank Employee
↓
Approve Loans
Manage Customers
View Reports
Both users are authenticated.
But they have different permissions.
This is authorization.
Authentication Flow
A typical login flow looks like this:
User Enters Credentials
│
▼
Backend API
│
▼
Credentials Valid?
│
▼
┌─────────────┐
│ │
Yes No
│ │
▼ ▼
Generate JWT Show Error
│
▼
Store Token
│
▼
Navigate to Dashboard
What is JWT?
JWT stands for:
JSON Web Token
It is a compact token used to securely identify authenticated users.
A JWT contains three parts:
Header.Payload.Signature
Example:
xxxxx.yyyyy.zzzzz
The frontend doesn't need to understand every part of the token.
It simply stores the token and sends it with future API requests.
Login Request
React sends credentials to the backend.
Example:
POST /login
Request body:
{
"email": "user@example.com",
"password": "password123"
}
Successful response:
{
"token": "jwt-token",
"user": {
"id": 101,
"name": "Siva",
"role": "ADMIN"
}
}
Storing Authentication State
After login, applications usually store:
- User information
- Authentication status
- Access token
Example Redux state:
interface AuthState {
user: User | null;
token: string | null;
isAuthenticated: boolean;
}
This state becomes available throughout the application.
Token Storage Options
There are multiple ways to store authentication tokens.
| Storage | Suitable? | Notes |
|---|---|---|
| localStorage | Sometimes | Persists after browser restart but is accessible to JavaScript. |
| sessionStorage | Sometimes | Cleared when the browser tab closes. |
| HTTP-Only Cookies | Recommended | More resistant to JavaScript-based attacks because scripts cannot access them. |
Many enterprise applications prefer HTTP-Only Cookies because they offer stronger protection against certain attack vectors.
Attaching Tokens to API Requests
Authenticated requests typically include the token in the Authorization header.
Example:
GET /accounts
Authorization: Bearer jwt-token
Using Axios:
api.interceptors.request.use((config) => {
const token = localStorage.getItem("token");
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
This automatically attaches the token to every request.
Protected Routes
Some pages should only be accessible to authenticated users.
Examples:
/
Login
About
Public routes.
/dashboard
/accounts
/profile
/settings
Protected routes.
Creating a Protected Route
Example:
import { Navigate } from "react-router-dom";
type ProtectedRouteProps = {
children: React.ReactNode;
isAuthenticated: boolean;
};
function ProtectedRoute({
children,
isAuthenticated,
}: ProtectedRouteProps) {
if (!isAuthenticated) {
return <Navigate to="/login" replace />;
}
return <>{children}</>;
}
export default ProtectedRoute;
Usage:
<Rout path="/dashboard" element={
<ProtectedRouteisAuthenticated={isAuthenticated}>
<Dashboard />
</ProtectedRoute>
}
/>
If the user isn't logged in, they're redirected to the login page.
Role-Based Access Control (RBAC)
Authentication answers:
"Who is the user?"
RBAC answers:
"What can this user do?"
Example:
ADMIN
↓
Dashboard
Users
Reports
Settings
CUSTOMER
↓
Dashboard
Accounts
Transactions
Different roles see different features.
Role-Based Rendering
Example:
{
user.role === "ADMIN" && (<AdminPanel />);
}
The component is rendered only for administrators.
Remember:
Frontend checks improve the user experience, but the backend must always enforce authorization.
Logout Flow
Logging out should:
- Clear authentication state
- Remove stored tokens
- Redirect to the login page
Example:
function logout() {
localStorage.removeItem("token");
dispatch(clearUser());
navigate("/login");
}
After logout:
User Clicks Logout
│
▼
Remove Token
│
▼
Clear Redux State
│
▼
Navigate to Login
Refresh Tokens
Access tokens usually have a short expiration time.
Instead of forcing users to log in repeatedly:
Access Token Expires
│
▼
Refresh Token
│
▼
New Access Token
│
▼
Continue Session
This improves both security and user experience.
Enterprise Authentication Architecture
React App
│
▼
Login API
│
▼
JWT Issued
│
▼
Redux/Auth Context
│
▼
Axios Interceptor
│
▼
Protected APIs
Each layer has a specific responsibility.
Folder Structure
A scalable authentication module:
src
├── features
│ └── auth
│ ├── components
│ ├── hooks
│ ├── pages
│ ├── services
│ ├── authSlice.ts
│ └── types.ts
├── routes
│ └── ProtectedRoute.tsx
├── api
└── axios.ts
This keeps authentication logic organized and maintainable.
Common Mistakes
1. Storing Sensitive Data in the Frontend
Avoid storing confidential information such as passwords or secrets in React applications.
Only store what's necessary for the client.
2. Relying Only on Frontend Authorization
Hiding buttons isn't enough.
The backend must always verify permissions before returning sensitive data or performing privileged actions.
3. Forgetting Token Expiration
Applications should gracefully handle expired tokens by:
- Refreshing them (when applicable)
- Redirecting users to log in again if refresh fails
4. Not Clearing Authentication State on Logout
Always remove tokens and reset application state when users sign out.
Best Practices
- Use HTTPS for all authenticated communication.
- Protect sensitive routes.
- Handle expired tokens gracefully.
- Keep authentication logic centralized.
- Separate authentication from authorization.
- Validate permissions on the backend.
- Store only the minimum required user information in the frontend.
Key Takeaways
Today, we learned:
✅ Authentication verifies user identity.
✅ Authorization determines what users are allowed to access.
✅ JWT is commonly used for stateless authentication.
✅ Protected routes prevent unauthorized access to pages.
✅ RBAC enables role-specific experiences.
✅ Refresh tokens improve both security and usability.
✅ Backend authorization is mandatory, even if the frontend hides restricted features.
Coming Next 🚀
In Day 28, we will explore:
React Design Patterns – Compound Components, Render Props, Higher-Order Components & Custom Hooks
We will learn:
- Why design patterns matter
- Compound Components
- Render Props
- Higher-Order Components (HOCs)
- Provider Pattern
- Custom Hook Pattern
- Composition over inheritance
- Enterprise React design principles
These patterns will help you build reusable, maintainable, and scalable React applications like those used in large engineering teams.
Happy Coding! 🚀
Top comments (0)