NodeAuth is a secure, session-aware authentication system built with TypeScript, Node.js, Express, PostgreSQL (via Sequelize), and JWT. It:
- Supports signup, login, email verification, password reset, token refresh, and logout.
- Uses JWTs for access (15 min) and refresh (14 days) tokens, sent as HttpOnly cookies.
- Ties tokens to server-side Sessions, so tokens are only valid if the session exists and hasn’t expired.
- Prevents token theft from being sufficient for impersonation by requiring both JWT and valid DB session.
- Sends email verification and reset tokens using the Resend API.
- Features modular structure with validation (Zod), error handling, and cookie/session utilities.
- Plans future support for social logins (Google, GitHub, etc.).
Run it locally with npm install, npm run push, and npm run dev. Environment variables go in .env.local.
- TL:DR
- Table of Contents
- NodeAuth
- Installation and Setup
- File Structure
- Authentication Flow & Sessions
- Session & Cookies
- Routes Summary
- Middleware
- Error Handling
- Future work
NodeAuth is a Node.js/Express authentication service written in TypeScript. It uses PostgreSQL (via Sequelize) for user/session data and JSON Web Tokens (JWT) stored in cookies for authentication. Unlike a purely stateless JWT system, NodeAuth tracks user sessions in the database and signs each token with a session ID. This “hybrid” approach combines fast JWT validation with server-side session control – access tokens are short-lived (15 min) and must be refreshed using a valid session.
-
Prerequisites: Ensure you have Node.js (>=16) and a PostgreSQL database running.
-
Clone the repo:
git clone <repo-url>andcd nodeauth. -
Install dependencies: Run
npm install. -
Configure environment: Create a
.env.localfile at the root with:NODE_ENV=development PORT=3000 APP_ORIGIN=http://localhost:3000 DATABASE_URL=postgres://user:pass@localhost:5432/dbname EMAIL_FROM=your@email.com RESEND_API_KEY=your_resend_api_key SALT_ROUNDS=10 JWT_ACCESS_SECRET=your_access_secret JWT_REFRESH_SECRET=your_refresh_secret
-
Sync database schema:
npm run push
-
Run the server:
npm run dev
src/
├── server.ts # Express entrypoint
├── auth/
│ ├── auth.route.ts # Route definitions
│ ├── auth.controller.ts # Route handlers
│ └── auth.schema.ts # Zod schemas
├── main/
│ ├── main.route.ts
│ └── main.controller.ts
├── db/
│ ├── db.ts # Sequelize config
│ ├── sync.ts # DB sync script
│ ├── models/
│ │ ├── user.model.ts
│ │ ├── account.model.ts
│ │ ├── session.model.ts
│ │ ├── verification.model.ts
│ │ └── associations.ts
├── middleware/
│ ├── authHandler.ts
│ └── errorHandler.ts
├── lib/
│ ├── AppError.ts
│ ├── date.ts
│ ├── httpStatusCode.ts
│ ├── emailTemplates.ts
│ └── utils/
│ ├── env.ts
│ ├── cookies.ts
│ ├── sendMail.ts
│ ├── userToken.ts
│ ├── appAssert.ts
│ └── catchAsyncErrors.ts
-
Signup:
POST /auth/signup- Body:
{ email, name, password } - Response: 201 with email verification link sent.
- Body:
-
Verify Email:
POST /auth/email/verify?token=...- Response: 201 and sets accessToken + refreshToken cookies.
-
Login:
POST /auth/login- Body:
{ email, password } - Response: 201 and sets cookies.
- Body:
-
Token Refresh:
POST /auth/refresh- Uses refreshToken cookie
- Response: 200 and sets new accessToken (and possibly refreshToken).
-
Password Reset Flow:
POST /auth/password/forgot→ sends reset linkPOST /auth/password/reset?token=...→ resets password, destroys sessions
-
Logout:
POSt /logout- Deletes session and clears cookies.
- accessToken: JWT
{ userId, sessionId }, expires in 15 min. - refreshToken: JWT
{ sessionId }, expires in 14 days. - Stored in
HttpOnly,SameSite=Strictcookies. refreshTokencookie scoped to/auth/refresh.- logged_in: expires in 15 min.
- Stored in
SameSite=laxcookies, only used for redirects. - Tokens are only valid if session exists in DB.
| Method | Path | Description |
|---|---|---|
| POST | /auth/signup |
Register new user |
| POST | /auth/email/verify |
Verify email via token |
| POST | /auth/login |
Log in with credentials |
| POST | /auth/password/forgot |
Send password reset email |
| POST | /auth/password/reset |
Reset password using token |
| POST | /auth/refresh |
Refresh access token |
| GET | / |
Fetch user details |
| POST | /logout |
Logout and clear session |
authHandler: Verifies access token from cookie, setsreq.userId.errorHandler: Centralized error catching and formatting.
- Zod validation → 400 Bad Request
- Invalid auth → 401 Unauthorized
- Server errors → 500 Internal Server Error
- Refresh errors → Cookies cleared automatically
- Rate limiting and brute-force protection