Files
retail-nest/docs/AUTH_SYSTEM.md
2025-10-10 16:04:10 +07:00

12 KiB

JWT Authentication System - Retail POS API

Overview

A complete JWT-based authentication system for the NestJS Retail POS backend, implementing secure user authentication, role-based access control (RBAC), and comprehensive user management.

Features

  • JWT authentication with Passport.js
  • Role-based access control (Admin, Manager, Cashier, User)
  • Secure password hashing with bcrypt (10 rounds)
  • Global authentication guards with public route support
  • Token validation and refresh mechanism
  • User management with CRUD operations
  • Swagger API documentation
  • TypeORM database integration

Architecture

Modules

  1. AuthModule (src/modules/auth/)

    • Authentication logic
    • JWT token generation and validation
    • Login and registration endpoints
    • Password validation
  2. UsersModule (src/modules/users/)

    • User CRUD operations
    • User repository pattern
    • Role management
  3. Common Guards (src/common/guards/)

    • Global JWT authentication guard
    • Role-based authorization guard
  4. Common Decorators (src/common/decorators/)

    • @CurrentUser() - Extract user from request
    • @Public() - Mark routes as public
    • @Roles() - Specify required roles

User Roles

enum UserRole {
  ADMIN = 'admin',      // Full access to all endpoints
  MANAGER = 'manager',  // Product and category management
  CASHIER = 'cashier',  // Transaction processing only
  USER = 'user',        // Read-only access
}

API Endpoints

Authentication Endpoints

1. Register User

POST /api/auth/register
Content-Type: application/json

{
  "name": "John Doe",
  "email": "john@example.com",
  "password": "Password123!",
  "roles": ["user"]  // Optional, defaults to ["user"]
}

Response:

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "user": {
    "id": "uuid",
    "name": "John Doe",
    "email": "john@example.com",
    "roles": ["user"],
    "isActive": true,
    "createdAt": "2025-01-15T10:00:00.000Z"
  }
}

Validation Rules:

  • Name: Required, max 255 characters
  • Email: Valid email format, unique
  • Password: Min 8 characters, must contain uppercase, lowercase, and number
  • Roles: Optional array of valid UserRole values

2. Login User

POST /api/auth/login
Content-Type: application/json

{
  "email": "admin@retailpos.com",
  "password": "Admin123!"
}

Response:

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "user": {
    "id": "uuid",
    "name": "Admin User",
    "email": "admin@retailpos.com",
    "roles": ["admin"],
    "isActive": true,
    "createdAt": "2025-01-15T10:00:00.000Z"
  }
}

3. Get Current User Profile

GET /api/auth/profile
Authorization: Bearer <access_token>

Response:

{
  "success": true,
  "data": {
    "id": "uuid",
    "email": "admin@retailpos.com",
    "name": "Admin User",
    "roles": ["admin"],
    "isActive": true
  }
}

4. Refresh Access Token

POST /api/auth/refresh
Authorization: Bearer <access_token>

Response:

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "user": {
    "id": "uuid",
    "name": "Admin User",
    "email": "admin@retailpos.com",
    "roles": ["admin"],
    "isActive": true,
    "createdAt": "2025-01-15T10:00:00.000Z"
  }
}

User Management Endpoints (Protected)

1. Get All Users (Admin/Manager)

GET /api/users
Authorization: Bearer <access_token>

Required Roles: Admin, Manager


2. Get User by ID (Admin/Manager)

GET /api/users/:id
Authorization: Bearer <access_token>

Required Roles: Admin, Manager


3. Create User (Admin Only)

POST /api/users
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "name": "New User",
  "email": "newuser@example.com",
  "password": "Password123!",
  "roles": ["cashier"],
  "isActive": true
}

Required Roles: Admin


4. Update User (Admin Only)

PATCH /api/users/:id
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "name": "Updated Name",
  "roles": ["manager"],
  "isActive": false
}

Required Roles: Admin

Note: Password cannot be updated via this endpoint


5. Delete User (Admin Only)

DELETE /api/users/:id
Authorization: Bearer <access_token>

Required Roles: Admin

Response: 204 No Content


Usage Examples

1. Using @Public() Decorator

Mark routes as public (skip JWT authentication):

@Controller('products')
export class ProductsController {
  @Get()
  @Public()  // This route is accessible without authentication
  async findAll() {
    return this.productsService.findAll();
  }

  @Post()
  // This route requires authentication (global guard)
  async create(@Body() dto: CreateProductDto) {
    return this.productsService.create(dto);
  }
}

2. Using @Roles() Decorator

Restrict routes to specific roles:

@Controller('products')
export class ProductsController {
  @Post()
  @Roles(UserRole.ADMIN, UserRole.MANAGER)  // Only admin and manager can create
  async create(@Body() dto: CreateProductDto) {
    return this.productsService.create(dto);
  }

  @Delete(':id')
  @Roles(UserRole.ADMIN)  // Only admin can delete
  async remove(@Param('id') id: string) {
    return this.productsService.remove(id);
  }
}

3. Using @CurrentUser() Decorator

Extract current user from request:

@Controller('profile')
export class ProfileController {
  @Get()
  @UseGuards(JwtAuthGuard)
  async getProfile(@CurrentUser() user: User) {
    // user object is automatically extracted from request
    return {
      id: user.id,
      email: user.email,
      name: user.name,
    };
  }
}

Security Features

Password Security

  • Hashing Algorithm: bcrypt with 10 salt rounds
  • Validation Rules:
    • Minimum 8 characters
    • At least one uppercase letter
    • At least one lowercase letter
    • At least one number
  • Password Exclusion: Password field is never returned in API responses (@Exclude decorator)

JWT Configuration

  • Secret: Configured via JWT_SECRET environment variable
  • Expiration: 1 day (configurable via JWT_EXPIRES_IN)
  • Token Storage: Client-side (localStorage or secure storage)
  • Token Format: Bearer token in Authorization header

Global Guards

  • JWT Authentication: Applied globally to all routes
  • Public Routes: Use @Public() decorator to bypass authentication
  • Role-Based Access: Use @Roles() decorator for authorization

Database Schema

Users Table

CREATE TABLE users (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  name VARCHAR(255) NOT NULL,
  email VARCHAR(255) UNIQUE NOT NULL,
  password VARCHAR(255) NOT NULL,
  roles TEXT NOT NULL DEFAULT 'user',
  isActive BOOLEAN DEFAULT true,
  createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

  INDEX idx_users_email (email)
);

Environment Variables

# JWT Configuration
JWT_SECRET=retail-pos-super-secret-key-change-in-production-2025
JWT_EXPIRES_IN=1d

# Database
DB_HOST=localhost
DB_PORT=5432
DB_USERNAME=postgres
DB_PASSWORD=postgres
DB_DATABASE=retail_pos

# Bcrypt
BCRYPT_ROUNDS=10

Setup Instructions

1. Install Dependencies

All required dependencies are already installed:

  • @nestjs/jwt
  • @nestjs/passport
  • passport
  • passport-jwt
  • bcrypt

2. Run Database Migration

npm run migration:run

3. Seed Default Users

npm run seed:run

This creates three default users:

4. Start Development Server

npm run start:dev

5. Access Swagger Documentation

Open browser: http://localhost:3000/api/docs


Testing the Authentication System

1. Test Login

curl -X POST http://localhost:3000/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "admin@retailpos.com",
    "password": "Admin123!"
  }'

2. Test Protected Endpoint

# Get the access_token from login response
curl -X GET http://localhost:3000/api/auth/profile \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

3. Test Role-Based Access

# Admin only endpoint (get all users)
curl -X GET http://localhost:3000/api/users \
  -H "Authorization: Bearer YOUR_ADMIN_TOKEN"

File Structure

src/
├── modules/
│   ├── auth/
│   │   ├── dto/
│   │   │   ├── login.dto.ts
│   │   │   ├── register.dto.ts
│   │   │   ├── auth-response.dto.ts
│   │   │   └── index.ts
│   │   ├── guards/
│   │   │   ├── jwt-auth.guard.ts
│   │   │   └── local-auth.guard.ts
│   │   ├── interfaces/
│   │   │   └── jwt-payload.interface.ts
│   │   ├── strategies/
│   │   │   ├── jwt.strategy.ts
│   │   │   └── local.strategy.ts
│   │   ├── auth.controller.ts
│   │   ├── auth.service.ts
│   │   └── auth.module.ts
│   └── users/
│       ├── dto/
│       │   ├── create-user.dto.ts
│       │   ├── update-user.dto.ts
│       │   ├── user-response.dto.ts
│       │   └── index.ts
│       ├── entities/
│       │   └── user.entity.ts
│       ├── users.controller.ts
│       ├── users.service.ts
│       ├── users.repository.ts
│       └── users.module.ts
├── common/
│   ├── decorators/
│   │   ├── current-user.decorator.ts
│   │   ├── public.decorator.ts
│   │   ├── roles.decorator.ts
│   │   └── index.ts
│   └── guards/
│       ├── jwt-auth.guard.ts
│       ├── roles.guard.ts
│       └── index.ts
└── database/
    ├── migrations/
    │   └── 1704470000000-CreateUsersTable.ts
    └── seeds/
        ├── users.seed.ts
        └── run-seeds.ts

Best Practices

  1. Never log passwords - Always hash before storing
  2. Use HTTPS in production - Never send tokens over HTTP
  3. Rotate JWT secrets regularly - Update JWT_SECRET periodically
  4. Implement refresh tokens - For long-lived sessions
  5. Log authentication events - Track login attempts and failures
  6. Rate limit auth endpoints - Prevent brute force attacks
  7. Validate all inputs - Use DTOs with class-validator
  8. Handle token expiration - Provide clear error messages
  9. Use strong passwords - Enforce password complexity
  10. Implement account lockout - After multiple failed attempts

Error Responses

400 Bad Request

{
  "success": false,
  "error": {
    "statusCode": 400,
    "message": "Validation failed",
    "details": [
      "password must be at least 8 characters long",
      "email must be a valid email address"
    ]
  },
  "timestamp": "2025-01-15T10:00:00.000Z",
  "path": "/api/auth/register"
}

401 Unauthorized

{
  "success": false,
  "error": {
    "statusCode": 401,
    "message": "Invalid credentials"
  },
  "timestamp": "2025-01-15T10:00:00.000Z",
  "path": "/api/auth/login"
}

403 Forbidden

{
  "success": false,
  "error": {
    "statusCode": 403,
    "message": "Insufficient permissions"
  },
  "timestamp": "2025-01-15T10:00:00.000Z",
  "path": "/api/users"
}

409 Conflict

{
  "success": false,
  "error": {
    "statusCode": 409,
    "message": "Email already registered"
  },
  "timestamp": "2025-01-15T10:00:00.000Z",
  "path": "/api/auth/register"
}

Next Steps

  1. Implement Refresh Tokens: Add refresh token table and rotation logic
  2. Add Email Verification: Send verification emails on registration
  3. Implement Password Reset: Forgot password functionality
  4. Add Two-Factor Authentication: Enhanced security with 2FA
  5. Implement Session Management: Track active sessions
  6. Add Rate Limiting: Protect against brute force attacks
  7. Implement Account Lockout: Lock accounts after failed attempts
  8. Add Audit Logging: Track all authentication events
  9. Implement Social Login: Google, Facebook, etc.
  10. Add API Key Authentication: For service-to-service communication

Support

For issues or questions: