after claude code
This commit is contained in:
590
docs/AUTH_SYSTEM.md
Normal file
590
docs/AUTH_SYSTEM.md
Normal file
@@ -0,0 +1,590 @@
|
||||
# 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
|
||||
|
||||
```typescript
|
||||
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
|
||||
```http
|
||||
POST /api/auth/register
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "John Doe",
|
||||
"email": "john@example.com",
|
||||
"password": "Password123!",
|
||||
"roles": ["user"] // Optional, defaults to ["user"]
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"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
|
||||
```http
|
||||
POST /api/auth/login
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"email": "admin@retailpos.com",
|
||||
"password": "Admin123!"
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"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
|
||||
```http
|
||||
GET /api/auth/profile
|
||||
Authorization: Bearer <access_token>
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "uuid",
|
||||
"email": "admin@retailpos.com",
|
||||
"name": "Admin User",
|
||||
"roles": ["admin"],
|
||||
"isActive": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 4. Refresh Access Token
|
||||
```http
|
||||
POST /api/auth/refresh
|
||||
Authorization: Bearer <access_token>
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"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)
|
||||
```http
|
||||
GET /api/users
|
||||
Authorization: Bearer <access_token>
|
||||
```
|
||||
|
||||
**Required Roles:** Admin, Manager
|
||||
|
||||
---
|
||||
|
||||
#### 2. Get User by ID (Admin/Manager)
|
||||
```http
|
||||
GET /api/users/:id
|
||||
Authorization: Bearer <access_token>
|
||||
```
|
||||
|
||||
**Required Roles:** Admin, Manager
|
||||
|
||||
---
|
||||
|
||||
#### 3. Create User (Admin Only)
|
||||
```http
|
||||
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)
|
||||
```http
|
||||
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)
|
||||
```http
|
||||
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):
|
||||
|
||||
```typescript
|
||||
@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:
|
||||
|
||||
```typescript
|
||||
@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:
|
||||
|
||||
```typescript
|
||||
@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
|
||||
```sql
|
||||
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
|
||||
|
||||
```bash
|
||||
# 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
|
||||
```bash
|
||||
npm run migration:run
|
||||
```
|
||||
|
||||
### 3. Seed Default Users
|
||||
```bash
|
||||
npm run seed:run
|
||||
```
|
||||
|
||||
This creates three default users:
|
||||
- **Admin:** admin@retailpos.com / Admin123!
|
||||
- **Manager:** manager@retailpos.com / Manager123!
|
||||
- **Cashier:** cashier@retailpos.com / Cashier123!
|
||||
|
||||
### 4. Start Development Server
|
||||
```bash
|
||||
npm run start:dev
|
||||
```
|
||||
|
||||
### 5. Access Swagger Documentation
|
||||
Open browser: http://localhost:3000/api/docs
|
||||
|
||||
---
|
||||
|
||||
## Testing the Authentication System
|
||||
|
||||
### 1. Test Login
|
||||
```bash
|
||||
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
|
||||
```bash
|
||||
# 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
|
||||
```bash
|
||||
# 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
|
||||
```json
|
||||
{
|
||||
"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
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": {
|
||||
"statusCode": 401,
|
||||
"message": "Invalid credentials"
|
||||
},
|
||||
"timestamp": "2025-01-15T10:00:00.000Z",
|
||||
"path": "/api/auth/login"
|
||||
}
|
||||
```
|
||||
|
||||
### 403 Forbidden
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": {
|
||||
"statusCode": 403,
|
||||
"message": "Insufficient permissions"
|
||||
},
|
||||
"timestamp": "2025-01-15T10:00:00.000Z",
|
||||
"path": "/api/users"
|
||||
}
|
||||
```
|
||||
|
||||
### 409 Conflict
|
||||
```json
|
||||
{
|
||||
"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:
|
||||
- GitHub Issues: [Create an issue](https://github.com/yourusername/retail-pos)
|
||||
- Email: support@retailpos.com
|
||||
- Documentation: http://localhost:3000/api/docs
|
||||
Reference in New Issue
Block a user