A production-ready, interview-defensible authentication and authorization system built with MongoDB, Express, React, and Node.js. This project demonstrates secure password handling, JWT tokens, email verification, role-based access control, and best practices for building scalable applications.
- β User registration with email validation
- β Secure login with JWT tokens
- β HTTP-only cookie refresh tokens
- β Email verification system
- β Password reset functionality
- β Role-based access control (RBAC)
- β Admin user management
- β Bcrypt password hashing
- β Protected routes middleware
- β CORS and security headers
- β Authentication context with React hooks
- β Protected and admin routes
- β Form validation
- β Auto token refresh with axios interceptors
- β Loading states and error handling
- β Responsive UI with CSS Grid
- β Role-based UI rendering
- Node.js (v14+)
- MongoDB (local or Atlas)
- npm or yarn
Run this command to create the project structure:
# On Windows (run setup-directories.bat)
setup-directories.bat
# Or use Node.js
node setup.jsThis creates:
Project/
βββ backend/
β βββ config/
β βββ controllers/
β βββ middlewares/
β βββ models/
β βββ routes/
β βββ utils/
β βββ .env
β βββ package.json
β βββ server.js
βββ frontend/
β βββ public/
β βββ src/
β β βββ components/
β β βββ context/
β β βββ pages/
β β βββ services/
β β βββ styles/
β β βββ App.js
β β βββ index.js
β βββ package.json
βββ README.md
After running setup.js or the bat file, organize the files from the root:
Backend files to move:
backend-package.json β backend/package.json
backend-server.js β backend/server.js
backend-user-model.js β backend/models/User.js
backend-auth-controller.js β backend/controllers/authController.js
backend-auth-middleware.js β backend/middlewares/auth.js
backend-auth-routes.js β backend/routes/authRoutes.js
backend-admin-routes.js β backend/routes/adminRoutes.js
backend-email-utils.js β backend/utils/email.js
backend-db-config.js β backend/config/database.js
backend-.env.example β backend/.env
Frontend files to move:
frontend-package.json β frontend/package.json
frontend-app.js β frontend/src/App.js
frontend-auth-context.js β frontend/src/context/AuthContext.js
frontend-api-service.js β frontend/src/services/api.js
frontend-protected-routes.js β frontend/src/components/ProtectedRoutes.js
frontend-login-page.js β frontend/src/pages/Login.js
frontend-register-page.js β frontend/src/pages/Register.js
frontend-dashboard-page.js β frontend/src/pages/Dashboard.js
frontend-admin-page.js β frontend/src/pages/AdminDashboard.js
frontend-forgot-password-page.js β frontend/src/pages/ForgotPassword.js
frontend-reset-password-page.js β frontend/src/pages/ResetPassword.js
frontend-verify-email-page.js β frontend/src/pages/VerifyEmail.js
frontend-app-styles.css β frontend/src/App.css
frontend-auth-styles.css β frontend/src/styles/auth.css
frontend-dashboard-styles.css β frontend/src/styles/dashboard.css
frontend-admin-styles.css β frontend/src/styles/admin.css
Create additional files:
frontend/public/index.html(see below)frontend/src/index.js(see below)
-
Install dependencies:
cd backend npm install -
Create
.envfile:PORT=5000 MONGO_URI=mongodb://localhost:27017/mern-auth JWT_SECRET=your_super_secret_jwt_key_change_this_in_production JWT_REFRESH_SECRET=your_super_secret_refresh_key_change_this_in_production JWT_EXPIRY=15m REFRESH_TOKEN_EXPIRY=7d EMAIL_USER=your_email@gmail.com EMAIL_PASS=your_app_password_from_gmail CLIENT_URL=http://localhost:3000 NODE_ENV=development
-
Email Configuration (Gmail):
- Enable 2-factor authentication on Gmail
- Go to https://myaccount.google.com/apppasswords
- Select "Mail" and "Windows Computer"
- Copy the generated password into EMAIL_PASS
-
Start MongoDB:
# If using local MongoDB mongod # Or use MongoDB Atlas cloud database
-
Start the backend:
npm run dev
Backend should run on
http://localhost:5000
-
Install dependencies:
cd frontend npm install -
Create
.envfile:REACT_APP_API_URL=http://localhost:5000/api
-
Create
public/index.html:<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>MERN Authentication System</title> </head> <body> <div id="root"></div> </body> </html>
-
Create
src/index.js:import React from "react"; import ReactDOM from "react-dom/client"; import App from "./App"; import "./App.css"; const root = ReactDOM.createRoot(document.getElementById("root")); root.render( <React.StrictMode> <App /> </React.StrictMode>, );
-
Start the frontend:
npm start
Frontend should run on
http://localhost:3000
POST /api/auth/register- Register new userPOST /api/auth/login- Login userPOST /api/auth/logout- Logout userPOST /api/auth/refresh-token- Refresh access tokenGET /api/auth/profile- Get user profile (protected)GET /api/auth/verify-email/:token- Verify emailPOST /api/auth/forgot-password- Request password resetPOST /api/auth/reset-password/:token- Reset password
GET /api/admin/users- Get all users (admin only)GET /api/admin/users/:id- Get user by ID (admin only)DELETE /api/admin/users/:id- Delete user (admin only)PATCH /api/admin/users/:id/role- Update user role (admin only)
- Password Hashing: Bcrypt with salt rounds of 10
- JWT Tokens: Access tokens (15m) + Refresh tokens (7d)
- HTTP-Only Cookies: Refresh tokens stored securely
- CORS: Configured to accept requests only from CLIENT_URL
- Email Verification: Required before login
- Password Reset: Time-limited tokens (1 hour)
- Role-Based Access: Admin-only routes protected
- Environment Variables: Secrets never hardcoded
- Input Validation: Email, password length checks
- Unique Constraints: Email uniqueness enforced at DB level
-
Register:
- Go to http://localhost:3000/register
- Fill in details and submit
- Check email for verification link
-
Email Verification:
- Click the verification link in email
- Redirect to login page
-
Login:
- Enter email and password
- Access token stored in memory, refresh token in cookie
- Redirect to dashboard
-
Dashboard:
- View user profile
- Admin users see admin panel link
-
Admin Panel:
- View all users
- Change user roles
- Delete users
-
Password Reset:
- Click "Forgot Password" on login page
- Enter email
- Check email for reset link
- Set new password
backend/
βββ config/database.js # MongoDB connection
βββ controllers/
β βββ authController.js # Auth logic (register, login, etc.)
βββ middlewares/
β βββ auth.js # JWT verification, error handling
βββ models/
β βββ User.js # User schema and methods
βββ routes/
β βββ authRoutes.js # Auth endpoints
β βββ adminRoutes.js # Admin endpoints
βββ utils/
β βββ email.js # Email sending utilities
βββ server.js # Express app setup
βββ package.json
βββ .env # Environment variables
frontend/
βββ public/
β βββ index.html
βββ src/
β βββ components/
β β βββ ProtectedRoutes.js
β βββ context/
β β βββ AuthContext.js # Auth state management
β βββ pages/
β β βββ Login.js
β β βββ Register.js
β β βββ Dashboard.js
β β βββ AdminDashboard.js
β β βββ ForgotPassword.js
β β βββ ResetPassword.js
β β βββ VerifyEmail.js
β βββ services/
β β βββ api.js # Axios with interceptors
β βββ styles/
β β βββ auth.css
β β βββ dashboard.css
β β βββ admin.css
β βββ App.js
β βββ App.css
β βββ index.js
β βββ index.css
βββ package.json
1. User Registration
β
2. Email Verification (link sent)
β
3. User Login
β Access Token (memory) + Refresh Token (HTTP-only cookie)
β
4. Protected Route Access
β Attach access token to requests
β If expired, use refresh token to get new access token
β
5. Dashboard Access
β
6. Logout
β Clear tokens
- Access Token: Short-lived (15 min), used for API requests
- Refresh Token: Long-lived (7 days), used to get new access token
- Storage: Access token in memory (safe), refresh token in HTTP-only cookie
- Global auth state management
- User data, tokens, loading, error states
- Methods: register, login, logout, forgotPassword, resetPassword, verifyEmail
PrivateRoute: Redirects to /login if not authenticatedAdminRoute: Redirects to /dashboard if not adminPublicRoute: Redirects to /dashboard if already authenticated
- Uses Nodemailer with Gmail
- HTML templates for verification and password reset
- Includes links with security tokens
Issue: MongoDB connection fails
- Ensure MongoDB is running (mongod)
- Check MONGO_URI in .env matches your setup
- For Atlas, whitelist your IP
Issue: Emails not sending
- Enable 2FA on Gmail
- Generate app password correctly
- Use 16-character app password
- Check EMAIL_USER and EMAIL_PASS in .env
Issue: CORS errors
- Ensure CLIENT_URL in backend .env matches frontend URL
- Check credentials: true in axios config
Issue: Token refresh fails
- Ensure refresh token cookie is being set
- Check browser cookie settings (HttpOnly enabled)
- Verify REFRESH_TOKEN_EXPIRY format (e.g., "7d")
- express: Web framework
- mongoose: MongoDB ODM
- jsonwebtoken: JWT handling
- bcryptjs: Password hashing
- nodemailer: Email sending
- cors: Cross-origin requests
- dotenv: Environment variables
- cookie-parser: Parse cookies
- react: UI library
- react-router-dom: Routing
- axios: HTTP client
- react-scripts: Build tooling
Edit backend/.env:
JWT_EXPIRY=30m # Access token duration
REFRESH_TOKEN_EXPIRY=14d # Refresh token durationIn MongoDB, manually update a user:
db.users.updateOne({ email: "admin@example.com" }, { $set: { role: "admin" } });Edit backend/utils/email.js HTML templates
Edit CSS files in frontend/src/styles/
This project is open source and available under the MIT License.
This is a production-ready authentication system suitable for:
- Portfolio projects
- Interview demonstrations
- Starting point for larger applications
- Teaching authentication concepts
The code follows best practices for:
- Security (password hashing, JWT, environment variables)
- Code organization (MVC pattern)
- Error handling (try-catch, validation)
- User experience (loading states, error messages)
For issues or questions:
- Check the Common Issues section
- Review the code comments
- Verify environment variables
- Check MongoDB/Gmail configurations
Happy coding! π