JWT Authentication: Secure Full-Stack Flow
Authentication is the most critical part of any MERN application. We use JSON Web Tokens (JWT) for stateless, secure communication.
The Auth Flow
- Login: User sends credentials to
/api/login. - Verify: Server validates credentials against MongoDB.
- Sign: Server creates a JWT signed with a secret key.
- Send: Server sends the token to the client.
- Store: Client stores the token (securely in
HttpOnlycookie or memory). - Request: For every subsequent request, the client sends the token in the
Authorizationheader. - Protect: The server uses a middleware to verify the token before allowing access.
javascript code// Auth Middleware const protect = catchAsync(async (req, res, next) => { let token; if (req.headers.authorization?.startsWith('Bearer')) { token = req.headers.authorization.split(' ')[1]; } if (!token) return next(new AppError('You are not logged in!', 401)); const decoded = await promisify(jwt.verify)(token, process.env.JWT_SECRET); req.user = await User.findById(decoded.id); next(); });
Security Best Practices
- Password Hashing: Always use
bcryptorargon2. Never store plain text passwords. - JWT Expiration: Set a short expiration (e.g., 1h) and use refresh tokens for longer sessions.
- HttpOnly Cookies: Prevents XSS attacks from stealing your tokens.
- CORS: Configure Cross-Origin Resource Sharing to only allow your frontend domain.
React Side: Protected Routes
In React, use a wrapper component or a hook to check if a user is authenticated before rendering a private page.