A full-stack application that demonstrates JWT-based authentication using a React frontend and Express.js backend.
Important
Requires Node.js 22 or earlier. jsonwebtoken depends transitively on
buffer-equal-constant-time, which uses SlowBuffer — removed in Node 24.
On newer Node the backend crashes at startup with
TypeError: Cannot read properties of undefined (reading 'prototype').
-
Backend: from the
backenddirectory runnpm install, then copy.env.exampleto.envand fill it in:cp .env.example .env node -e "console.log(require('crypto').randomBytes(48).toString('hex'))" # JWT_SECRET
The server refuses to start if
JWT_SECRETorMONGODB_URIare missing — this is intentional, so a missing secret fails loudly instead of silently producing tokens anyone could forge. -
Frontend: from the
frontenddirectory runnpm install.
- Backend: in the
backenddirectory, runnode server.js. - Frontend: in the
frontenddirectory, runnpm start.
| Method | Route | Auth | Description |
|---|---|---|---|
POST |
/api/auth/register |
— | Create an account. Password must be at least 8 characters. |
POST |
/api/auth/login |
— | Returns { token }. The token expires after JWT_EXPIRES_IN (default 1h). |
GET |
/api/auth/me |
Bearer | Returns the current user. Example of a route verified server-side. |
curl -H "Authorization: Bearer $TOKEN" http://localhost:3001/api/auth/me- User registration with password length validation
- User login with bcrypt password hashing
- JWT tokens with expiry, signed with a secret read from the environment
requireAuthmiddleware that verifies the token server-side- CORS restricted to the origins listed in
CORS_ORIGIN - Request bodies type-checked before reaching MongoDB (prevents NoSQL operator injection)
This is a teaching example, not production-ready. Before real use you would also want:
- Token storage: the frontend keeps the token in
localStorage, which any XSS can read. Production apps should prefer anHttpOnlycookie. - Rate limiting:
/loginhas none, so it is open to brute force. Add something likeexpress-rate-limit. - Refresh tokens / revocation: there is no way to invalidate an issued token
before it expires. The unused
tokens[]array on the user model was a start in that direction. - Account lockout, email verification, password reset — none implemented.
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
MIT
ReactAuthenticationJWT is free and open source. If you find it useful, please ⭐️ star the repo — and if you'd like to support my open-source work, you can 💛 sponsor me on GitHub or ☕️ buy me a coffee. Completely optional, always appreciated. 🙏