A secure authentication system built with Nest.js, featuring email and password login, JWT tokens, refresh token rotation, multi-session management, and comprehensive security features.
This authentication system implements modern security practices including Argon2 password hashing, JWT-based token management with automatic rotation, multi-device session handling, and comprehensive audit logging. The project demonstrates clean architecture principles, type safety with TypeScript, and defensive programming practices to handle edge cases and security threats.
This implementation uses a hybrid token-session authentication model that combines the best of both worlds. Access tokens are JWT-based for stateless API authentication, allowing for fast verification without database lookups on every request. However, each JWT is tied to a database-backed session that stores a hashed refresh token, device information, and metadata—enabling powerful server-side controls that pure JWT systems lack.
The hybrid approach provides significant advantages: refresh tokens are stored securely in the database (not in JWTs), enabling immediate revocation when needed; sessions can be managed with concurrent limits and automatic cleanup; and detailed audit trails track every login attempt, device, and IP address. This architecture delivers the performance benefits of stateless JWTs for frequent API calls while maintaining the security and administrative control of session-based systems, including token rotation, forced logouts, and real-time session monitoring.
Fortify Auth is designed with a modular, secure, and scalable architecture using Nest.js and TypeScript. The system combines stateless JWT authentication with stateful session management for enhanced security and control.
Registration → Email Verification → Login → Session Creation → Access/Refresh Tokens
- Registration: Users create an account and verify their email via OTP.
- Login: Credentials are verified, and a new session is created.
- Session: Stores device info, IP, user agent, and refresh token hash. Limits concurrent sessions per user.
- Tokens: Short-lived access tokens (JWT) and long-lived refresh tokens (rotating) manage secure API access.
- Access Token: Stateless JWT stored in an HTTP-only cookie.
- Refresh Token: Stored hashed in the database for rotation and revocation.
- Benefits:
- Fast API verification with JWTs.
- Full server-side session control.
- Forced logouts and automatic session cleanup.
- Tracks device, IP, and user agent per session.
- Limits maximum concurrent sessions per user.
- Argon2 password hashing.
- Rotating tokens with timing-safe comparisons.
- Device fingerprints and IP logging.
- CSRF protection
- Rate limiting
- Auth Module: Handles registration, login, password resets, token rotation.
- OTP Module: Secure OTP generation and validation.
- Email Module: Sends verification and notification emails.
- Prisma Module: Database access and schema management.
- User Registration with email verification
- Secure Login with password hashing (Argon2)
- JWT access tokens with session-based refresh token rotation
- Token rotation with refresh token invalidation
- Password Reset Flow with OTP verification
- Email Notifications for all critical actions
- Account Lockout after multiple failed login attempts
- Session Management with configurable concurrent session limits
- Device Tracking (IP address, user agent, device info)
- Timing-Safe Token Comparison to prevent timing attacks
- Secure Cookie Handling (HttpOnly, SameSite, Secure flags)
- OTP System with expiration and single-use enforcement
- Login Activity Tracking for audit trails
- CSRF Protection to prevent cross-site request forgery attacks
- Rate Limiting to mitigate brute-force and abuse attempts
- Email verification before account activation
- Password reset via secure OTP
- Multiple active sessions support
- Automatic session cleanup
- Detailed error messages with remaining attempts
- Framework: Nest.js
- Language: TypeScript
- Database: PostgreSQL
- ORM: Prisma
- Authentication: Passport.js + JWT
- Password Hashing: Argon2
- Email Service: Nodemailer
- Device Detection: ua-parser-js
- Hashed with Argon2 (memory-hard algorithm)
- No plaintext password storage
- Configurable failed login attempt threshold
- Temporary account lockout duration
- Automatic unlock after cooldown period
- Short-lived access tokens
- Refresh token rotation on every use
- SHA-256 hashing for refresh tokens
- Timing-safe comparison to prevent timing attacks
- HTTP-only cookies (JavaScript cannot access)
- Secure flag in production
- SameSite protection against CSRF
- Device fingerprinting
- IP address tracking
- 6-digit cryptographically secure codes
- 5-minute expiration window
- Single-use enforcement
- Argon2 hashing for storage
- Automatic invalidation of previous codes
- Limit repeated requests to endpoints
- Prevents brute-force attacks and abuse
fortify-auth-api/
├── prisma/
│ ├── migrations/ # Database migration files
│ └── schema.prisma # Database schema definition
├── src/
│ ├── auth/
│ │ ├── decorators/ # Custom parameter decorators
│ │ ├── dto/ # Data transfer objects
│ │ ├── guards/ # Route protection guards
│ │ ├── strategies/ # Authentication strategies
│ │ ├── types/ # TypeScript type definitions
│ │ ├── auth.controller.ts # Authentication endpoints
│ │ ├── auth.service.ts # Authentication business logic
│ │ └── auth.module.ts # Auth module configuration
│ ├── otp/
│ │ ├── otp.service.ts # OTP generation and validation
│ │ └── otp.module.ts # OTP module configuration
│ ├── email/
│ │ ├── types/ # Email-related types
│ │ ├── email.service.ts # Email sending service
│ │ └── email.module.ts # Email module configuration
│ ├── prisma/
│ │ ├── prisma.service.ts # Prisma client wrapper
│ │ └── prisma.module.ts # Prisma module configuration
│ ├── generated/
│ │ └── prisma/ # Prisma generated client
│ ├── app.controller.ts # Root controller
│ ├── app.service.ts # Root service
│ ├── app.module.ts # Root module
│ └── main.ts # Application entry point
- Node.js: v18 (LTS) or higher
- PostgreSQL database
- npm: v9 or higher (comes with Node 18+)
- Clone the repository
git clone https://github.com/Jeyaprabakar01/fortify-auth-api.git
cd fortify-auth-api- Install dependencies
npm install- Create a .env file
# Application
NODE_ENV="development"
WEB_APP_URL="http://localhost:3000"
# Database
DATABASE_URL="postgresql://user:password@localhost:5432/authdb"
# JWT Configuration
JWT_SECRET="your-super-secret-jwt-key"
JWT_EXPIRY=15m
# Token Configuration
ACCESS_TOKEN_MAXAGE=15 # minutes
REFRESH_TOKEN_MAXAGE=7 # days
# Session Configuration
SESSION_EXPIRY=7 # days
SESSION_MAX_CONCURRENT=5
# Account Lock Configuration
ACCOUNT_LOCK_MAX_ATTEMPTS=5
ACCOUNT_LOCK_DURATION=15 # minutes
# Email Configuration (configure based on your provider)
SMTP_HOST="smtp.example.com"
SMTP_PORT=465
SMTP_USER="your-email@example.com"
SMTP_PASS="your-email-password"- Run database migrations
npx prisma migrate dev- Start the development server
npm run start:devPOST /auth/register
Content-Type: application/json
{
"fullName": "John Doe",
"email": "john@example.com",
"password": "SecurePass123!"
}POST /auth/verify-email
Content-Type: application/json
{
"email": "john@example.com",
"otpCode": "123456"
}POST /auth/login
Content-Type: application/json
{
"email": "john@example.com",
"password": "SecurePass123!"
}POST /auth/reset-password
Content-Type: application/json
{
"email": "john@example.com"
}POST /auth/update-password
Content-Type: application/json
{
"email": "john@example.com",
"otpCode": "123456",
"password": "NewSecurePass123!"
}POST /auth/refresh
Cookie: refresh_token=<token>POST /auth/logout
Cookie: access_token=<token>
Authorization: Bearer <token>