Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ JWT_EXPIRES_IN=15m
JWT_REFRESH_SECRET=your-super-secret-refresh-key-change-this-in-production
JWT_REFRESH_EXPIRES_IN=7d

# Security Configuration
BCRYPT_ROUNDS=10

# Redis Configuration (for Bull Queue)
REDIS_HOST=localhost
REDIS_PORT=6379
Expand Down
72 changes: 71 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,22 @@ curl -H "X-API-Version: 1" http://localhost:3000/users

## πŸ“Š Architecture

## βš™οΈ Tech Stack

| Layer | Technology |
| ------------- | -------------------------- |
| Framework | NestJS |
| Database | PostgreSQL + TypeORM |
| Blockchain | Starknet + Starknet.js |
| Realtime | WebSockets (Gateway) |
| Queues/Async | BullMQ + Redis (optional) |
| File Uploads | Cloudinary |
| Config Mgmt | @nestjs/config |
| Testing | Jest + Supertest |
| Auth | JWT + Wallet Sign-In |
| Deployment | Docker, Railway, or Fly.io |
| File Upload | Cloudinary |
| Security | Helmet + bcrypt | Security headers and password hashing |
### System Overview

TeachLink Backend follows a **modular microservices architecture** built on NestJS, designed for scalability and maintainability. The system uses a layered approach with clear separation of concerns:
Expand Down Expand Up @@ -233,7 +249,61 @@ TeachLink Backend follows a **modular microservices architecture** built on Nest
| **Validation** | class-validator + class-transformer | DTO validation |
| **Security** | Helmet + bcrypt | Security headers and password hashing |

## �️ Database
## πŸ” Security

### Password Hashing Configuration

The application uses **bcrypt** for password hashing with configurable rounds via the `BCRYPT_ROUNDS` environment variable.

#### Recommended Bcrypt Rounds by Environment

| Environment | Recommended Rounds | Hash Time (ms) | Security Level | Performance Impact |
| ----------- | ----------------- | -------------- | -------------- | ------------------ |
| **Development** | 8-10 | 50-100 | Good | Low |
| **Staging** | 10-12 | 100-300 | High | Medium |
| **Production** | 12-14 | 300-1000 | Very High | High |

#### Security vs Performance Tradeoffs

**Lower Rounds (4-8):**
- βœ… Faster authentication
- βœ… Lower CPU usage
- ⚠️ Reduced security against brute force attacks
- ⚠️ May be vulnerable to GPU-based cracking

**Higher Rounds (12-15):**
- βœ… Strong resistance against brute force attacks
- βœ… Future-proof against computational advances
- ❌ Slower authentication (may impact user experience)
- ❌ Higher CPU usage (may affect scalability)

#### Configuration Example

```env
# Development (faster, less secure)
BCRYPT_ROUNDS=8

# Production (slower, more secure)
BCRYPT_ROUNDS=12
```

#### Security Best Practices

1. **Minimum 10 rounds** for production environments
2. **Monitor authentication performance** when increasing rounds
3. **Consider rate limiting** to prevent brute force attacks
4. **Use hardware security modules** for high-security applications
5. **Regular security audits** to assess adequate protection levels

#### Migration Considerations

When changing `BCRYPT_ROUNDS`:
- Existing passwords remain valid until users change them
- New passwords will use the configured rounds
- Consider forcing password reset for sensitive accounts
- Gradually increase rounds to monitor performance impact

## πŸ—„οΈ Database

### Index Strategy

Expand Down
3 changes: 3 additions & 0 deletions src/config/env.validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ export const envValidationSchema = Joi.object({
// Encryption
ENCRYPTION_SECRET: Joi.string().min(32).required(),

// Security Configuration
BCRYPT_ROUNDS: Joi.number().integer().min(4).max(15).default(10),

// Stripe Configuration
STRIPE_SECRET_KEY: Joi.string().required(),
STRIPE_WEBHOOK_SECRET: Joi.string().required(),
Expand Down
10 changes: 7 additions & 3 deletions src/users/users.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { GetUsersDto } from './dto/get-users.dto';
import { CachingService } from '../caching/caching.service';
import { CACHE_TTL, CACHE_PREFIXES, CACHE_EVENTS } from '../caching/caching.constants';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class UsersService {
Expand All @@ -21,7 +22,8 @@ export class UsersService {
private readonly userRepository: Repository<User>,
private readonly cachingService: CachingService,
private readonly eventEmitter: EventEmitter2,
) {}
private readonly configService: ConfigService,
) { }

async create(createUserDto: CreateUserDto): Promise<User> {
// Check if user already exists
Expand All @@ -32,7 +34,8 @@ export class UsersService {
ensureUserDoesNotExist(existingUser, 'User with this email already exists');

// Hash password
const hashedPassword = await bcrypt.hash(createUserDto.password, 10);
const bcryptRounds = this.configService.get<number>('BCRYPT_ROUNDS') || 10;
const hashedPassword = await bcrypt.hash(createUserDto.password, bcryptRounds);

// Create user
const user = this.userRepository.create({
Expand Down Expand Up @@ -141,7 +144,8 @@ export class UsersService {
// Append current, maintain last 5 entries
user.passwordHistory = [...recentPasswords, user.password].slice(-5);

updateUserDto.password = await bcrypt.hash(plainPassword, 10);
const bcryptRounds = this.configService.get<number>('BCRYPT_ROUNDS') || 10;
updateUserDto.password = await bcrypt.hash(plainPassword, bcryptRounds);
}

Object.assign(user, updateUserDto);
Expand Down
Loading