A full-stack MERN authentication system with JWT sessions, bcrypt-hashed passwords, and role-based access control — wrapped in a glassmorphism UI over full-screen video backgrounds. No plain login form here: a single card handles both sign-in and sign-up, floating over a looping video on the way in and a different scene once you're through the gate.
(Drop a screenshot of the login card and dashboard here — drag a PNG into this file on GitHub)
Aegis is a secure authentication system built around a simple idea: the gate into an app should feel as considered as the app itself.
Instead of two separate login and signup pages, Aegis uses one glass card with a pill toggle — flip between "Log In" and "Sign Up" instantly, no navigation, no reload. The card floats over a full-screen looping video, offset to one side of the screen so the scenery stays the star. Once authenticated, the dashboard carries the same visual language over a second video, so the whole experience feels like one continuous space rather than a form bolted onto a template.
Under the hood, it's a properly secured MERN application: passwords are hashed with bcrypt before they ever touch the database, sessions are handled with signed JWTs, and routes are protected on both the API (middleware) and the client (route guards) — with a working role-based access layer distinguishing regular users from admins.
- Registration and login with bcrypt-hashed passwords (never stored in plain text)
- JWT-based sessions, persisted client-side and auto-attached to every API request
- Protected routes enforced server-side (middleware) and client-side (route guards)
- Role-based access control (
user/admin) with a dedicated admin-only endpoint
- Combined login/signup card with an animated mode toggle — zero page reloads
- Glassmorphism styling — frosted blur, translucent surface, soft border and shadow
- Full-screen looping video backgrounds, distinct for the auth page and the dashboard
- Show/hide password toggle
- Fully responsive — the card re-centers gracefully on small screens
- REST API with proper error handling and status codes
- Mongoose schema validation, unique email constraint, pre-save password hashing hook
- Environment-variable driven config,
.envexcluded from version control - Clean separation of concerns (routes / controllers / models / middleware)
- Deployed with a production-ready SPA routing setup (
vercel.jsonrewrites)
| Layer | Technology |
|---|---|
| Frontend | React 18 · Vite · React Router · Axios · plain CSS (no UI framework) |
| Backend | Node.js · Express · Mongoose |
| Database | MongoDB (Atlas) |
| Auth | JSON Web Tokens · bcryptjs |
| Deployment | Vercel (frontend) · Render (backend) · MongoDB Atlas (database) |
PRODIGY_FS_01/
├── backend/ # Express + MongoDB REST API
│ ├── config/db.js
│ ├── controllers/authController.js
│ ├── middleware/authMiddleware.js
│ ├── models/User.js
│ ├── routes/authRoutes.js
│ ├── routes/protectedRoutes.js
│ ├── server.js
│ └── .env.example
└── frontend/ # React (Vite) client
├── public/
│ ├── auth-bg.mp4
│ └── dashboard-bg.mp4
├── src/
│ ├── api/axios.js
│ ├── context/AuthContext.jsx
│ ├── components/ProtectedRoute.jsx
│ ├── pages/AuthPage.jsx
│ ├── pages/Dashboard.jsx
│ ├── App.jsx
│ ├── index.css
│ └── main.jsx
├── vercel.json
└── .env.example
| Element | Choice |
|---|---|
| Name | Aegis — a shield in Greek mythology; a gate that protects what's behind it |
| Palette | Deep video-scene backdrop · frosted white glass overlay · soft coral error state |
| Layout | Auth card offset to the right of the frame; dashboard card centered — the video does the talking on the sides |
| Signature Element | The glass card itself — a single reusable surface that adapts between login, signup, and the authenticated dashboard |
| Area | Status |
|---|---|
Backend API — register, login, /me, error handling |
✅ Done |
| Password hashing (bcrypt) | ✅ Done |
| JWT session issuing & verification middleware | ✅ Done |
Role-based access control (user / admin) |
✅ Done — tested with both roles |
| Frontend — combined auth card, protected route guard | ✅ Done |
| Glassmorphism UI + video backgrounds | ✅ Done |
| Responsive layout (mobile → desktop) | ✅ Done |
| Deployment (Vercel + Render) | ✅ Done — live links above |
cd backend
npm install
cp .env.example .env # fill in your real MONGO_URI and JWT_SECRET
npm run dev # → http://localhost:5000cd frontend
npm install
npm run dev # → http://localhost:5173| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
| POST | /api/auth/register |
Register a new user | No |
| POST | /api/auth/login |
Log in and receive a JWT | No |
| GET | /api/auth/me |
Get the current logged-in user | Yes |
| GET | /api/protected/dashboard |
Sample protected route | Yes |
| GET | /api/protected/admin |
Admin-only route | Yes (admin role) |
New accounts default to role: "user". To test the admin route, update a user's role directly in MongoDB:
db.users.updateOne({ email: "you@example.com" }, { $set: { role: "admin" } })Log out and back in, then call /api/protected/admin with the JWT in the Authorization: Bearer <token> header. A user-role account hitting the same route receives a 403 Forbidden.
Database — MongoDB Atlas
Free M0 cluster, database user, and 0.0.0.0/0 network access (required for Render's dynamic IPs).
Backend — Render
Root directory backend, build command npm install, start command npm start, with MONGO_URI, JWT_SECRET, JWT_EXPIRES_IN, and CLIENT_URL (no trailing slash) set as environment variables.
Frontend — Vercel
Root directory frontend, build command npm run build, output directory dist, with VITE_API_URL pointing at the deployed backend's /api route. Includes a vercel.json rewrite rule so client-side routes (/login, /dashboard) don't 404 on direct load or refresh.
Note: the backend is on Render's free tier, which spins down after 15 minutes of inactivity. The first request after idling may take 30–50 seconds to respond — that's expected, not a bug.
- Add real screenshots of the login card and dashboard
- Optional bonus polish: "forgot password" flow, email verification
MIT — open source and free to use.
by Aditya Dixit
