Skip to content

jarolthecoder/secure-auth-system

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

39 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Secure Auth System

A production-ready, reusable authentication REST API built with Node.js, TypeScript, Express, and MySQL. Features dual-token JWT authentication, email verification, password reset, refresh token rotation with reuse detection, and rate limiting on all sensitive endpoints.

Overview

This API is designed as a reusable authentication foundation to eliminate the need to rebuild auth for each new project. It targets standard web applications—SaaS platforms, client dashboards, and consumer apps—and implements common, production-ready patterns for session and token management.

Tokens are stored as one-way hashes in the database, so a database leak does not expose usable credentials.

Key Features

  • Dual-Token Auth — Short-lived JWT access token + opaque HttpOnly refresh token cookie
  • Refresh Token Rotation — Reuse detection: a consumed token being replayed immediately revokes all sessions for that user
  • Email Verification — Opaque token-based verification flow with resend support
  • Password Reset — Single-use time-limited token via email; invalidates all active sessions on success
  • Rate Limiting — Per-endpoint limits on all auth routes (register, login, refresh, password reset, etc.)
  • Scheduled Cleanup — Daily cron job purges expired refresh, verification, and reset tokens
  • Type Safety — Full TypeScript with strict typing and Zod request validation
  • Security Hardening — Helmet headers, CORS, bcrypt hashing, timing-safe token comparisons

Tech Stack

Backend: Node.js | TypeScript | Express Database: MySQL | mysql2 (raw SQL, no ORM)
Authentication: JWT | bcryptjs
Validation: Zod
Email: Nodemailer
Security: Helmet | express-rate-limit
Logging: Winston
Scheduler: node-cron

Architecture

graph TB
    Client[Client / Frontend]
    Routes[Routes Layer]
    MW[Middleware Layer<br/>Auth · Validation · Rate Limit]
    Controllers[Controllers Layer]
    Services[Services Layer]
    Models[Models Layer]
    DB[(MySQL Database)]
    Email[Email Service<br/>Nodemailer]

    Client -->|HTTP Request| Routes
    Routes -->|Validation + Auth| MW
    MW -->|Processed Request| Controllers
    Controllers -->|Business Logic| Services
    Services -->|Raw SQL| Models
    Models -->|mysql2 Pool| DB
    Services -->|Transactional Emails| Email
    Controllers -->|HTTP Response| Client

    style Client fill:#e1f5ff
    style Routes fill:#fff4e1
    style MW fill:#ffe1f5
    style Controllers fill:#e1ffe1
    style Services fill:#f5e1ff
    style Models fill:#ffe1e1
    style DB fill:#333,color:#fff
    style Email fill:#e1e1ff
Loading

Design Pattern: Layered architecture — routes handle HTTP, controllers parse requests and delegate, services own business logic, models execute raw SQL against the database.

Project Structure

secure-auth-system/
├── database/
│   └── schema.sql                  # Full MySQL schema
├── src/
│   ├── @types/                     # Global TypeScript type definitions
│   ├── config/
│   │   ├── adapters/               # Thin wrappers: JWT, bcrypt, env-var
│   │   └── db/                     # mysql2 pool setup
│   ├── constants/                  # HTTP status codes, auth constants
│   ├── controllers/
│   │   ├── auth/                   # Auth controller
│   │   └── user/                   # User controller
│   ├── data/
│   │   ├── models/                 # Raw SQL models (User, RefreshToken, etc.)
│   │   ├── schemas/                # Zod validation schemas
│   │   └── entities/               # Entity type definitions
│   ├── middlewares/                # authenticate, errorHandler, rateLimiter, validation
│   ├── routes/
│   │   ├── auth/                   # /api/v1/auth routes
│   │   └── user/                   # /api/v1/users routes
│   ├── services/
│   │   ├── auth/                   # AuthService, TokenService, EmailVerificationService, PasswordResetService
│   │   ├── cleanup/                # Expired token cleanup
│   │   ├── cron/                   # Scheduled job definitions
│   │   ├── email/                  # Email sending
│   │   └── user/                   # UserService
│   ├── templates/email/            # HTML email templates
│   ├── utils/                      # CustomError, ApiResponse, logger, time parser, token helpers
│   ├── app.ts                      # Express app setup + middleware registration
│   └── server.ts                   # Entry point — connects DB, starts server
├── .env.template
├── package.json
├── tsconfig.json
└── README.md

API Endpoints

Base path: /api/v1

Authentication

Method Endpoint Description
POST /auth/register Register — triggers verification email
POST /auth/login Login — returns access token, sets refresh cookie
POST /auth/logout Revoke refresh token, clear cookie
POST /auth/refresh Rotate refresh token, return new access token
GET /auth/me Restore session from refresh cookie
POST /auth/verify-email Verify email with token from link
POST /auth/resend-verification Resend verification email
POST /auth/forgot-password Send password reset email
POST /auth/reset-password Reset password using token from link

Users

Method Endpoint Description Auth
GET /users/me Get authenticated user profile Bearer token

Health

Method Endpoint Description
GET /health Health check

Getting Started

Prerequisites

  • Node.js v18+
  • MySQL v8+
  • SMTP credentials (Gmail app password or similar)

Installation

# Clone repository
git clone https://github.com/jarolthecoder/secure-auth-system.git
cd secure-auth-system

# Install dependencies
npm install

# Configure environment
cp .env.template .env
# Edit .env with your credentials

# Create database
mysql -u root -p < database/schema.sql

# Start development server
npm run dev

Server runs at http://localhost:<PORT>

Environment Variables

Variable Description
NODE_ENV development or production
PORT Server port
FRONTEND_URL Frontend origin — used in email links
ALLOWED_ORIGINS Comma-separated CORS origins
DATABASE_HOST MySQL host
DATABASE_PORT MySQL port
DATABASE_USER MySQL user
DATABASE_PASSWORD MySQL password
DATABASE_NAME Database name
DATABASE_SSL true when DB is on a remote/managed host
JWT_SECRET Min 32 random chars — openssl rand -hex 32
ACCESS_TOKEN_EXP e.g. 15m
REFRESH_TOKEN_EXP e.g. 7d
EMAIL_VERIFICATION_TOKEN_EXP e.g. 30m
PASSWORD_RESET_TOKEN_EXP e.g. 15m
MAILER_SERVICE Nodemailer service, e.g. gmail
MAILER_EMAIL Sender address
MAILER_SECRET_KEY App password or API key

Authentication Flow

Dual-token system with opaque refresh tokens:

  • Access Token (JWT, short-lived e.g. 15m) — sent as Authorization: Bearer header for protected routes
  • Refresh Token (opaque, long-lived e.g. 7d) — stored as an HttpOnly cookie; only a bcrypt hash is persisted in the DB
# Authenticate requests
Authorization: Bearer <ACCESS_TOKEN>

# Rotate tokens when access token expires
POST /api/v1/auth/refresh   (refresh cookie sent automatically)

Token reuse detection: if a previously-rotated refresh token is presented again, all sessions for that user are immediately revoked — this limits the blast radius of a stolen token.

Development

npm run dev      # Start with hot reload (ts-node-dev)
npm run build    # Compile TypeScript to dist/
npm start        # Build then run dist/app.js

Technical Highlights

  • Layered Architecture — Routes, controllers, services, and models each own a single responsibility with no cross-layer leakage
  • Opaque Refresh Tokens — Format jti.rawSecret; only the bcrypt hash of rawSecret is stored — a DB leak exposes nothing replayable
  • Token Reuse Detection — Replaying a rotated refresh token triggers full session revocation for that user
  • Timing-Safe Comparisons — All token and credential validations are constant-time to prevent timing oracle attacks
  • Centralized Error Handling — Throw CustomError.<type>() anywhere; global errorHandler middleware formats the response consistently
  • Adapter PatternJwtAdapter, bcryptAdapter, and envs wrap third-party libraries so the rest of the codebase never imports them directly
  • Fast-fail Config — All env vars validated at startup via env-var; missing vars crash immediately with a clear message
  • Scheduled CleanupCronService runs at 6 AM daily to purge expired tokens via CleanupService

Deployment

  • Deploy behind a reverse proxy (nginx, Caddy) — required for accurate IP-based rate limiting
  • Set DATABASE_SSL=true for any managed or remote database
  • Set NODE_ENV=production to enable secure cookies and suppress stack traces in error responses
  • Rate limiting uses an in-memory store — add rate-limit-redis before scaling to multiple instances

Author

Jarol Riera

License

This project is licensed under the MIT License — see the LICENSE file for details.

About

A production-ready, reusable authentication REST API

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors