A production-ready, enterprise-grade authentication and authorization system built with NestJS, featuring multiple authentication strategies, role-based and policy-based access control, and comprehensive security features.
- Features
- Tech Stack
- Prerequisites
- Installation
- Configuration
- API Documentation
- Project Structure
- Architecture
- Authentication Strategies
- Authorization
- Security Features
- Testing
- Postman Collection
- Development
- Production Deployment
-
Multiple Authentication Strategies:
- JWT-based authentication with access and refresh tokens
- Session-based authentication with Redis storage
- Google OAuth 2.0 integration
- API Key authentication
- Two-Factor Authentication (2FA) with TOTP
-
Authorization System:
- Role-Based Access Control (RBAC) with roles (USER, ADMIN)
- Policy-Based Access Control (ABAC) with custom policies
- Fine-grained permissions via scopes
-
User Management: Complete CRUD operations for users
-
Coffee Management: Example resource with protected endpoints
-
Token Management: Secure refresh token rotation with Redis storage
- NestJS Framework: Modern, scalable Node.js framework
- TypeORM: Type-safe database access with PostgreSQL
- Redis: Session storage and refresh token management
- JWT: Secure token-based authentication
- Passport.js: Flexible authentication middleware
- Input Validation: Comprehensive request validation using class-validator
- Error Handling: Centralized error handling with proper HTTP status codes
- CORS Support: Configurable cross-origin resource sharing
- Environment Configuration: Joi-based environment variable validation
- Node.js >= 20.0.0: JavaScript runtime
- NestJS 11.0.1: Progressive Node.js framework
- TypeScript 5.7.3: Type-safe development
- Express.js: HTTP server (via NestJS platform)
- PostgreSQL: Relational database
- TypeORM 0.3.20: Type-safe ORM with decorators
- Redis (via ioredis 5.9.0): Session storage and token management
- @nestjs/jwt 11.0.2: JWT token generation and validation
- @nestjs/passport 11.0.5: Authentication strategies
- passport 0.7.0: Authentication middleware
- bcrypt 6.0.0: Password hashing
- otplib 12.0.1: TOTP-based 2FA
- qrcode 1.5.4: QR code generation for 2FA
- google-auth-library 10.5.0: Google OAuth verification
- express-session 1.18.2: Session middleware
- connect-redis 9.0.0: Redis session store
- class-validator 0.14.3: DTO validation decorators
- class-transformer 0.5.1: Object transformation
- @nestjs/config 4.0.2: Configuration management
- joi 18.0.2: Environment variable validation
- Jest 30.0.0: Testing framework
- ESLint 9.18.0: Code linting
- Prettier 3.4.2: Code formatting
- TypeScript: Native TypeScript support
Before you begin, ensure you have the following installed:
- Node.js >= 20.0.0 (Download)
- npm >= 10.0.0 (comes with Node.js)
- PostgreSQL >= 12.0 (local installation or cloud database)
- Redis >= 6.0 (for session storage and token management)
- Git (for cloning the repository)
- Google OAuth Credentials (Get credentials)
- 2FA Authenticator App (Google Authenticator, Authy, etc.)
git clone <repository-url>
cd auth-nestjsnpm installCreate a .dev.env file in the root directory (or copy from .sample.env):
cp .sample.env .dev.envEdit .dev.env with your configuration:
# Application
NODE_ENV=development
PORT=3000
# Database
POSTGRES_HOST=localhost
POSTGRES_PORT=5432
POSTGRES_USERNAME=your_username
POSTGRES_PASSWORD=your_password
POSTGRES_DATABASE=auth_nestjs
# JWT
JWT_SECRET=your-super-secret-jwt-key-change-in-production
JWT_TOKEN_AUDIENCE=your-app-name
JWT_TOKEN_ISSUER=your-app-name
JWT_ACCESS_TOKEN_TTL=3600
JWT_REFRESH_TOKEN_TTL=86400
# Redis
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
# 2FA
TFA_APP_NAME=Your App Name
# Google OAuth (Optional)
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
GOOGLE_REDIRECT_URI=http://localhost:3000/auth/social/google/callback
# Sessions
SESSION_SECRET=your-super-secret-session-key-change-in-production
# CORS
CORS_ORIGIN=http://localhost:3000Important:
- Use strong, unique secrets for
JWT_SECRETandSESSION_SECRETin production - Configure PostgreSQL connection details
- Ensure Redis is running and accessible
- For Google OAuth, set up OAuth 2.0 credentials in Google Cloud Console
Create PostgreSQL database:
createdb auth_nestjsNote: TypeORM will automatically synchronize the schema in development mode (synchronize: true). For production, use migrations.
# Using Docker
docker run -d -p 6379:6379 redis:latest
# Or using local Redis
redis-serverDevelopment Mode (with hot reload):
npm run start:devProduction Mode:
npm run build
npm run start:prodThe server will start on http://localhost:3000 (or the port specified in your .env file).
| Variable | Description | Required | Default |
|---|---|---|---|
NODE_ENV |
Environment (development/production/test) | No | development |
PORT |
Server port | No | 3000 |
POSTGRES_HOST |
PostgreSQL host | Yes | - |
POSTGRES_PORT |
PostgreSQL port | No | 5432 |
POSTGRES_USERNAME |
PostgreSQL username | Yes | - |
POSTGRES_PASSWORD |
PostgreSQL password | Yes | - |
POSTGRES_DATABASE |
PostgreSQL database name | Yes | - |
JWT_SECRET |
JWT signing secret | Yes | - |
JWT_TOKEN_AUDIENCE |
JWT audience claim | Yes | - |
JWT_TOKEN_ISSUER |
JWT issuer claim | Yes | - |
JWT_ACCESS_TOKEN_TTL |
Access token TTL (seconds) | Yes | 3600 |
JWT_REFRESH_TOKEN_TTL |
Refresh token TTL (seconds) | Yes | 86400 |
REDIS_HOST |
Redis host | Yes | - |
REDIS_PORT |
Redis port | Yes | 6379 |
REDIS_PASSWORD |
Redis password | No | - |
TFA_APP_NAME |
2FA app name for QR code | Yes | - |
GOOGLE_CLIENT_ID |
Google OAuth client ID | No | - |
GOOGLE_CLIENT_SECRET |
Google OAuth client secret | No | - |
GOOGLE_REDIRECT_URI |
Google OAuth redirect URI | No | - |
SESSION_SECRET |
Session encryption secret | Yes | - |
CORS_ORIGIN |
CORS allowed origin | No | http://localhost:3000 |
http://localhost:3000
The API supports multiple authentication methods:
- Bearer Token:
Authorization: Bearer <accessToken> - API Key:
Authorization: ApiKey <apiKey> - Session Cookie: Automatically handled for session-based endpoints
Create a new user account
Request Body:
{
"name": "John Doe",
"email": "john.doe@example.com",
"password": "Password123!"
}Response: 201 Created
{
"id": 1,
"name": "John Doe",
"email": "john.doe@example.com",
"role": "user",
"isTfaEnabled": false
}Validation:
name: String, 2-32 characters, letters only, requiredemail: Valid email format, requiredpassword: 8-32 characters, must contain uppercase, lowercase, number, and special character, required
Sign in with email and password
Request Body:
{
"email": "john.doe@example.com",
"password": "Password123!",
"tfaCode": "123456"
}Response: 200 OK
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": 1,
"name": "John Doe",
"email": "john.doe@example.com",
"role": "user"
}
}Features:
- Returns JWT access token and refresh token
- If 2FA is enabled,
tfaCode(6-digit TOTP) is required - Tokens include user ID, email, and role
Refresh access token using refresh token
Request Body:
{
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}Response: 200 OK
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}Features:
- Invalidates old refresh token (token rotation)
- Returns new access and refresh tokens
- Refresh tokens stored in Redis for validation
Generate QR code for 2FA setup
Headers:
Authorization: Bearer <accessToken>
Response: 200 OK (QR code image as file stream)
Features:
- Requires authentication
- Generates TOTP secret and QR code
- Automatically enables 2FA for the authenticated user
- Scan QR code with authenticator app (Google Authenticator, Authy, etc.)
Sign in using session-based authentication
Request Body:
{
"email": "john.doe@example.com",
"password": "Password123!"
}Response: 200 OK
{
"id": 1,
"name": "John Doe",
"email": "john.doe@example.com",
"role": "user"
}Features:
- Creates session cookie (stored in Redis)
- No tokens returned (uses session cookie)
- Session persists across requests
Get current authenticated user (session-based)
Response: 200 OK
{
"id": 1,
"name": "John Doe",
"email": "john.doe@example.com",
"role": "user"
}Features:
- Requires valid session cookie
- Returns authenticated user information
Authenticate using Google OAuth token
Request Body:
{
"token": "google-id-token-here"
}Response: 200 OK
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": 1,
"name": "John Doe",
"email": "john.doe@example.com",
"role": "user",
"googleId": "123456789"
}
}Features:
- Verifies Google ID token
- Creates user if doesn't exist
- Returns JWT tokens (same as sign-in)
- Links Google account to user
Create a new user
Request Body:
{
"name": "Jane Doe",
"email": "jane.doe@example.com",
"password": "Password123!"
}Response: 201 Created
{
"id": 2,
"name": "Jane Doe",
"email": "jane.doe@example.com",
"role": "user"
}Get all users
Response: 200 OK
[
{
"id": 1,
"name": "John Doe",
"email": "john.doe@example.com",
"role": "user"
}
]Get user by ID
Response: 200 OK
{
"id": 1,
"name": "John Doe",
"email": "john.doe@example.com",
"role": "user"
}Update user
Request Body:
{
"name": "Updated Name",
"email": "updated@example.com",
"password": "NewPassword123!",
"isTfaEnabled": true
}Response: 200 OK
{
"id": 1,
"name": "Updated Name",
"email": "updated@example.com",
"role": "user",
"isTfaEnabled": true
}Note: All fields are optional.
Delete user by ID
Response: 200 OK
Create a new coffee (Requires ADMIN role)
Headers:
Authorization: Bearer <accessToken>
or
Authorization: ApiKey <apiKey>
Request Body:
{
"name": "Espresso",
"description": "Strong coffee"
}Response: 201 Created
{
"id": 1,
"name": "Espresso",
"description": "Strong coffee"
}Authorization:
- Requires
ADMINrole - Requires
OrganizationContributorPolicypolicy
Get all coffees (Requires authentication)
Headers:
Authorization: Bearer <accessToken>
or
Authorization: ApiKey <apiKey>
Response: 200 OK
[
{
"id": 1,
"name": "Espresso",
"description": "Strong coffee"
}
]Get coffee by ID (Requires authentication)
Response: 200 OK
{
"id": 1,
"name": "Espresso",
"description": "Strong coffee"
}Update coffee (Requires ADMIN role)
Request Body:
{
"name": "Updated Espresso",
"description": "Updated description"
}Response: 200 OK
Delete coffee (Requires ADMIN role)
Response: 200 OK
The project follows a clean, layered architecture:
HTTP Request
↓
Controller (controllers/)
↓
Service (services/)
↓
Repository/Entity (entities/)
↓
TypeORM
↓
PostgreSQL Database
-
Module Pattern: Feature-based module organization
IamModule: Identity and Access ManagementUsersModule: User managementCoffeesModule: Example resource module
-
Guard Pattern: Authentication and authorization guards
AuthenticationGuard: Global authentication guardAccessTokenGuard: JWT token validationApiKeyGuard: API key validationSessionGuard: Session validationRoleGuard: Role-based authorizationPoliciesGuard: Policy-based authorization
-
Strategy Pattern: Multiple authentication strategies
- JWT authentication
- Session authentication
- Google OAuth
- API Key authentication
-
Decorator Pattern: Custom decorators for metadata
@Auth(): Authentication type specification@Roles(): Role requirements@Policies(): Policy requirements@ActiveUser(): Current user injection
- Single Responsibility: Each module/class has one responsibility
- Dependency Injection: Services depend on abstractions
- Type Safety: Full TypeScript coverage with TypeORM types
- Error Handling: Centralized error handling with proper HTTP status codes
- Validation: class-validator for all inputs
- Separation of Concerns: Clear boundaries between layers
- Access Tokens: Short-lived (default: 1 hour)
- Refresh Tokens: Long-lived (default: 24 hours)
- Token Rotation: Refresh tokens are rotated on each use
- Redis Storage: Refresh token IDs stored in Redis for validation
- Claims: User ID, email, role included in token
- Redis Storage: Sessions stored in Redis
- Cookie-based: Secure, httpOnly cookies
- Passport.js: Session serialization/deserialization
- Automatic: Session persists across requests
- ID Token Verification: Validates Google ID tokens
- User Creation: Automatically creates users if needed
- Account Linking: Links Google account to user
- JWT Tokens: Returns standard JWT tokens after OAuth
- User-scoped: API keys belong to users
- Scope-based: Fine-grained permissions via scopes
- Alternative to JWT: Can be used instead of Bearer tokens
- TOTP-based: Time-based One-Time Password
- QR Code Generation: Easy setup with QR codes
- Optional: Can be enabled per user
- Required on Sign-in: If enabled, TOTP code required
Roles:
USER: Default role for regular usersADMIN: Administrative role with elevated permissions
Usage:
@Roles(Role.ADMIN)
@Get('admin-only')
adminOnly() {
// Only ADMIN role can access
}Policies:
OrganizationContributorPolicy: Example policy for organization contributors
Usage:
@Policies(new OrganizationContributorPolicy())
@Post('create')
create() {
// Only users matching policy can access
}Custom Policies:
- Implement
PolicyHandlerinterface - Register in
PolicyHandlerStorage - Use
@Policies()decorator
- class-validator: Comprehensive request body and parameter validation
- Type Safety: TypeScript + TypeORM types prevent type errors
- Automatic Sanitization: ValidationPipe with whitelist and transform
- bcrypt: Secure password hashing with salt rounds
- Password Requirements: Enforced via validation (uppercase, lowercase, number, special character)
- JWT Signing: HMAC SHA-256 signing
- Token Rotation: Refresh tokens rotated on each use
- Redis Validation: Refresh token IDs validated against Redis
- Expiration: Configurable token TTL
- Secure Cookies: httpOnly, secure (in production), sameSite
- Redis Storage: Centralized session storage
- Secret Key: Encrypted session data
- Configurable Origins: Environment-based CORS origin
- Credentials: Supports credentials in cross-origin requests
- Preflight: Proper OPTIONS handling
- Environment Variables: Sensitive data in environment variables
- No Hardcoded Secrets: All secrets in
.envfile - Parameterized Queries: TypeORM uses parameterized queries (SQL injection protection)
- Error Messages: Generic error messages prevent information leakage
All errors follow a consistent format:
400 Bad Request (Validation Errors)
{
"statusCode": 400,
"message": ["email must be an email", "password is required"],
"error": "Bad Request"
}401 Unauthorized (Authentication Errors)
{
"statusCode": 401,
"message": "Invalid credentials",
"error": "Unauthorized"
}403 Forbidden (Authorization Errors)
{
"statusCode": 403,
"message": "Access denied",
"error": "Forbidden"
}404 Not Found
{
"statusCode": 404,
"message": "User not found",
"error": "Not Found"
}409 Conflict (Unique Constraint)
{
"statusCode": 409,
"message": "User already exists",
"error": "Conflict"
}500 Internal Server Error
{
"statusCode": 500,
"message": "Internal server error",
"error": "Internal Server Error"
}# Unit tests
npm run test
# E2E tests
npm run test:e2e
# Test coverage
npm run test:cov
# Watch mode
npm run test:watch- Unit Tests:
*.spec.tsfiles alongside source files - E2E Tests:
test/directory withjest-e2e.jsonconfiguration
A complete Postman collection is included: Auth_NestJS_API.postman_collection.json
- Auto-save Variables: Automatically saves tokens, user IDs, and coffee IDs
- Collection Variables: Pre-configured variables for baseUrl, accessToken, refreshToken, userId, coffeeId, apiKey
- Request Examples: Sample request bodies for all endpoints
- Documentation: Detailed descriptions for each endpoint
- Test Scripts: Automatic variable saving on successful responses
- Open Postman
- Click Import button
- Select
Auth_NestJS_API.postman_collection.json - Collection will be imported with all endpoints organized by resource
baseUrl: API base URL (default:http://localhost:3000)accessToken: JWT access token (auto-populated after sign-in)refreshToken: JWT refresh token (auto-populated after sign-in)userId: Current user ID (auto-populated after sign-up/sign-in)coffeeId: Coffee ID (auto-populated after creating coffee)apiKey: API Key for authentication (set manually)
- Sign Up: Create a new user account
- Sign In: Get access and refresh tokens
- Generate 2FA QR Code: Set up two-factor authentication (optional)
- Refresh Token: Refresh access token
- Get Current User: Test authenticated endpoint
- Create Coffee: Test ADMIN-only endpoint with policy
- Google OAuth: Test Google authentication (if configured)
- Session Sign In: Test session-based authentication
# Start development server with hot reload
npm run start:dev
# Start production server
npm run start:prod
# Build for production
npm run build
# Run linting
npm run lint
# Format code
npm run format- TypeScript: Strict type checking enabled
- ESLint: Code linting with NestJS rules
- Prettier: Code formatting
- Naming Conventions:
- Files: kebab-case (e.g.,
authentication.service.ts) - Classes: PascalCase (e.g.,
AuthenticationService) - Methods/Variables: camelCase (e.g.,
signIn())
- Files: kebab-case (e.g.,
src/
├── main.ts # Application entry point
├── app.module.ts # Root module
├── app.controller.ts # Root controller
├── app.service.ts # Root service
├── iam/ # Identity and Access Management module
│ ├── iam.module.ts
│ ├── authentication/ # Authentication strategies
│ │ ├── authentication.controller.ts
│ │ ├── authentication.service.ts
│ │ ├── session-authentication.controller.ts
│ │ ├── session-authentication.service.ts
│ │ ├── api-key.service.ts
│ │ ├── otp-authentication.service.ts
│ │ ├── guards/ # Authentication guards
│ │ ├── decorators/ # Custom decorators
│ │ ├── dto/ # Data Transfer Objects
│ │ ├── storage/ # Token storage (Redis)
│ │ └── social/ # OAuth providers
│ ├── authorization/ # Authorization system
│ │ ├── guards/ # Authorization guards
│ │ ├── decorators/ # Authorization decorators
│ │ └── policies/ # Policy handlers
│ ├── config/ # Configuration
│ ├── entities/ # Database entities
│ ├── hashing/ # Password hashing
│ └── common/ # Shared services
├── users/ # Users module
│ ├── users.module.ts
│ ├── users.controller.ts
│ ├── users.service.ts
│ ├── entities/
│ ├── dto/
│ └── enums/
└── coffees/ # Coffees module (example)
├── coffees.module.ts
├── coffees.controller.ts
├── coffees.service.ts
├── entities/
└── dto/
- Create Entity: Add TypeORM entity in
entities/ - Create DTOs: Add request/response DTOs in
dto/ - Create Service: Add business logic in service file
- Create Controller: Add HTTP handlers in controller file
- Create Module: Register in module file
- Add Routes: Routes automatically registered via controllers
- Add Guards: Apply authentication/authorization guards as needed
Development:
- TypeORM auto-synchronization enabled (
synchronize: true) - Schema changes automatically applied
Production:
- Disable auto-synchronization
- Use migrations:
# Generate migration npm run typeorm migration:generate -- -n MigrationName # Run migrations npm run typeorm migration:run
- Set production environment variables
- Use strong database connection string
- Configure Redis connection
- Set strong JWT and session secrets
- Configure CORS origins
- Disable TypeORM auto-synchronization
- Enable production logging
- Use managed PostgreSQL service (AWS RDS, Supabase, Neon, etc.)
- Use managed Redis service (AWS ElastiCache, Redis Cloud, etc.)
- Enable PostgreSQL connection pooling
- Use environment-specific configuration
- Set up monitoring and alerting
- Configure reverse proxy (Nginx, Caddy)
- Enable HTTPS/SSL
- Set up backup strategy
- Monitor authentication failures
- Enable structured logging
- Use process manager (PM2, systemd)
- Change all default secrets
- Use strong JWT secret (32+ characters)
- Use strong session secret (32+ characters)
- Enable HTTPS
- Configure secure cookies (secure: true)
- Set up rate limiting (recommended)
- Enable CORS for specific origins only
- Disable TypeORM auto-synchronization
- Use environment variables for all secrets
- Set up database backups
- Monitor authentication attempts
- Set up error tracking (Sentry, etc.)
- Connection Pooling: TypeORM manages connection pooling automatically
- Redis Caching: Sessions and tokens cached in Redis
- Query Optimization: Use TypeORM query builder for complex queries
- Indexes: Ensure database indexes on frequently queried fields
- Token Validation: JWT validation is stateless and fast
-
Sign Up
POST /auth/sign-up { "name": "John Doe", "email": "john@example.com", "password": "Password123!" } # Returns: User object -
Sign In
POST /auth/sign-in { "email": "john@example.com", "password": "Password123!" } # Returns: { accessToken, refreshToken, user } -
Use Access Token
GET /coffees Authorization: Bearer <accessToken> # Returns: List of coffees
-
Refresh Token
POST /auth/refresh-token { "refreshToken": "<refreshToken>" } # Returns: { accessToken, refreshToken } (new tokens)
- Sign In (get access token)
- Generate QR Code
POST /auth/2fa/generate Authorization: Bearer <accessToken> # Returns: QR code image
- Scan QR Code with authenticator app
- Sign In with 2FA
POST /auth/sign-in { "email": "john@example.com", "password": "Password123!", "tfaCode": "123456" # From authenticator app }
-
Sign In with Session
POST /auth/session/sign-in { "email": "john@example.com", "password": "Password123!" } # Sets session cookie automatically -
Access Protected Endpoint
GET /auth/session/me # Cookie automatically sent, returns user
| Method | Endpoint | Auth Required | Role Required | Description |
|---|---|---|---|---|
POST |
/auth/sign-up |
No | - | Create user account |
POST |
/auth/sign-in |
No | - | Sign in with JWT |
POST |
/auth/refresh-token |
No | - | Refresh access token |
POST |
/auth/2fa/generate |
Yes | - | Generate 2FA QR code |
POST |
/auth/session/sign-in |
No | - | Sign in with session |
GET |
/auth/session/me |
Session | - | Get current user (session) |
POST |
/auth/social/google |
No | - | Google OAuth |
POST |
/users |
No | - | Create user |
GET |
/users |
No | - | Get all users |
GET |
/users/:id |
No | - | Get user by ID |
PATCH |
/users/:id |
No | - | Update user |
DELETE |
/users/:id |
No | - | Delete user |
POST |
/coffees |
Yes | ADMIN | Create coffee |
GET |
/coffees |
Yes | - | Get all coffees |
GET |
/coffees/:id |
Yes | - | Get coffee by ID |
PATCH |
/coffees/:id |
Yes | ADMIN | Update coffee |
DELETE |
/coffees/:id |
Yes | ADMIN | Delete coffee |
AuthType.NONE: No authentication requiredAuthType.BEARER: JWT Bearer token authenticationAuthType.API_KEY: API Key authenticationAuthType.SESSION: Session-based authentication
Role.USER: Regular user (default)Role.ADMIN: Administrative user
UNLICENSED
Auth NestJS API - Enterprise Authentication & Authorization System
Built with ❤️ using NestJS, TypeORM, PostgreSQL, Redis, and JWT