Authentication Middleware
Relevant source files
The following files were used as context for generating this wiki page:
Introduction
The authentication middleware system is a critical security component of the backend API, designed to protect routes and manage user access. It leverages JSON Web Tokens (JWT) to handle user authentication and role-based authorization. The system is primarily composed of two middleware functions, protect and authorize, which work in tandem to verify a user's identity and permissions before allowing access to secured API endpoints.
The overall authentication flow begins when a user logs in via the frontend. The backend validates their credentials, and upon success, generates a JWT. This token is then sent with subsequent requests to protected routes, where the middleware validates the token and checks user roles. This ensures that sensitive data and administrative functionalities are only accessible to authenticated and authorized users.
Core Middleware Functions
The core logic is contained within backend/src/middleware/auth.js, which exports two main functions for securing Express routes.
Sources: backend/src/middleware/auth.js
protect Middleware
The protect middleware is the first line of defense for secured endpoints. Its primary responsibility is to verify that a request is accompanied by a valid, non-expired JWT.
Key Logic:
- Token Extraction: It attempts to extract the JWT from two possible locations, in order of priority:
- An HTTP cookie, with the name defined by
process.env.COOKIE_NAMEor defaulting to"token". - The
Authorizationheader, expecting the formatBearer <token>.
- An HTTP cookie, with the name defined by
- Token Validation: If a token is found, it is verified using
jwt.verifyagainst the secret key stored inprocess.env.JWT_SECRET. - User Hydration: Upon successful verification, the decoded payload from the token is attached to the Express
requestobject asreq.user. This object contains the user's ID and roles. - Error Handling: If no token is found, or if the token is invalid or expired, the middleware terminates the request and sends a
401 Unauthorizedresponse.
The following diagram illustrates the execution flow of the protect middleware.
Sources: backend/src/middleware/auth.js:8-27
authorize Middleware
The authorize middleware provides role-based access control (RBAC). It is designed to be used after the protect middleware has successfully authenticated a user.
Key Logic:
- Higher-Order Function:
authorizeis a higher-order function that accepts a list of role strings (e.g.,"admin") as arguments and returns an Express middleware function. - Role Check: The returned middleware checks if the
req.user.roleproperty (populated byprotect) is included in the list of allowed roles passed toauthorize. - Access Control: If the user's role is not permitted, the middleware terminates the request and sends a
403 Forbiddenresponse. Otherwise, it callsnext()to proceed.
// backend/src/middleware/auth.js:29-39
export const authorize =
(...roles) =>
(req, res, next) => {
const role = req.user?.role;
if (!role || !roles.includes(role)) {
return res
.status(403)
.json({ success: false, error: { message: "Forbidden" } });
}
next();
};
Sources: backend/src/middleware/auth.js:29-39
Authentication and Token Generation
The user authentication process and subsequent JWT generation are handled by the /api/auth/login endpoint.
Sources: backend/src/routes/auth.js
Login Flow
When a user attempts to sign in from the frontend, a POST request is sent to /api/auth/login with their email and password. The backend performs the following steps:
- Validation: Checks if
emailandpasswordare provided. - User Lookup: Finds the user in the
TaiKhoancollection by their email. It uses.select('+MatKhau')to explicitly include the password field, which is normally hidden. - Password Verification: Compares the plaintext password from the request with the hashed password from the database using the
comparePasswordmethod on theTaiKhoanmodel. This method internally usesbcrypt.compare. - Token Generation: If the password is correct, a JWT is created by calling the
signTokenfunction, which signs a payload containing the user's ID ({ id: user._id }) with theJWT_SECRET. The token has a configured expiration (JWT_EXPIRES_INor a default of '7d'). - Response: The server sends a
200 OKresponse containing the generatedtokenand user information.
The following sequence diagram shows the complete login process, from the user interface to the database.
JWT Payload and Signing
The JWT is created using the signToken helper function.
| Property | Description | Source |
|---|---|---|
| Payload | An object containing the user's ID: { id: user._id }. | backend/src/routes/auth.js:13-17 |
| Secret | The secret key used for signing, loaded from process.env.JWT_SECRET. | backend/src/routes/auth.js:14 |
| Expires In | The token's lifetime, loaded from process.env.JWT_EXPIRES_IN or defaulting to '7d'. | backend/src/routes/auth.js:15 |
The decoded payload is later used by the protect middleware to identify the user.
Sources: backend/src/routes/auth.js:12-17, backend/src/middleware/auth.js:21-26
Middleware Usage
The authentication middleware is applied to Express routes to secure them. This is configured in both the main server file and individual route files.
Route Registration
In server.js, the authentication routes are registered under the /api/auth path, making the login endpoint available at /api/auth/login.
// backend/src/server.js:82-83
app.use("/api/auth", require("./routes/auth"));
app.use("/api/taikhoan", require("./routes/taiKhoan"));
Sources: backend/src/server.js:82-83
Protecting Routes
The users.js route file provides a clear example of how protect and authorize are used.
router.use(protect);: This line applies theprotectmiddleware to all subsequent routes defined in this file. Every request to/api/users/*will first require a valid JWT.router.get("/", authorize("admin"), getUsers);: This applies theauthorizemiddleware specifically to theGET /api/usersendpoint. Only users with the role"admin"can access this route, after they have been authenticated byprotect.
The table below summarizes the protection on user management routes.
| Method | Endpoint | Middleware | Required Role |
|---|---|---|---|
| GET | /api/users | protect, authorize | admin |
| GET | /api/users/:id | protect | Any authenticated user |
| PUT | /api/users/:id | protect | Any authenticated user |
| DELETE | /api/users/:id | protect, authorize | admin |
Sources: backend/src/routes/users.js:46-65
Frontend Integration
The frontend application in frontend/ interacts with the authentication system via a dedicated API service and UI components.
authAPI Service
The frontend/src/services/authAPI.js file centralizes all authentication-related API calls.
- It creates an
axiosinstance configured to communicate with the backend API. - The
loginfunction sends aPOSTrequest to/auth/loginwith the user's credentials. - The
withCredentials: trueoption is set, which is necessary if the backend were to use session cookies. However, the current implementation returns the token in the response body.
// frontend/src/services/authAPI.js:29
export const login = (credentials) => api.post("/auth/login", credentials);
Sources: frontend/src/services/authAPI.js:29
Sign-In Page
The frontend/src/pages/Sign_in.js component provides the user interface for logging in.
- It captures the user's email and password from a form.
- On submission, it calls the
loginfunction from theuseAuthcontext (which presumably uses theauthAPIservice). - Upon a successful response, it checks the user's role and redirects them accordingly:
- Admins are redirected to
/admin/overview. - Regular users are redirected to
/dashboard.
- Admins are redirected to
- It also handles loading states and displays error messages returned from the API.
Sources: frontend/src/pages/Sign_in.js:29-55
Summary
The authentication system is a robust, JWT-based mechanism that effectively secures the backend API. The protect middleware handles token verification, while the authorize middleware manages role-based access. This combination allows for fine-grained control over API endpoints. The entire flow, from user login on the React frontend to token validation on the Express backend, is clearly defined and implemented, providing a solid security foundation for the application.