Files
finance_bot/.history/docs/ARCHITECTURE_20251210210601.md
2025-12-10 22:09:31 +09:00

19 KiB

🏗️ API-First Zero-Trust Architecture - Complete Guide

📋 Table of Contents

  1. Architecture Overview
  2. Security Model
  3. Authentication Flows
  4. RBAC & Permissions
  5. API Endpoints
  6. Telegram Bot Integration
  7. Testing Strategy
  8. Deployment
  9. Production Checklist

🏗️ Architecture Overview

System Components

EXTERNAL CLIENTS (Web, Mobile, Bot)
           ↓
    API GATEWAY (FastAPI)
           ↓
  MIDDLEWARE STACK
  ├─ Security Headers
  ├─ Rate Limiting
  ├─ HMAC Verification
  ├─ JWT Authentication
  ├─ RBAC Authorization
  └─ Request Logging
           ↓
    DOMAIN SERVICES
  ├─ AuthService
  ├─ TransactionService
  ├─ WalletService
  ├─ BudgetService
  └─ NotificationService
           ↓
    REPOSITORY LAYER (SQLAlchemy)
           ↓
    DATABASE + REDIS + MESSAGE QUEUE

Key Principles

Principle Implementation
Zero-Trust Every request requires JWT + HMAC verification
Immutability No direct record deletion; use reversals + audit logs
Isolation Family-level data isolation at service layer
Observability Every action logged to event_log + access_log
Stateless API calls don't depend on session state

🔐 Security Model

Token Types

1. Access Token (JWT)

  • Purpose: Authenticate API requests
  • Lifetime: 15 minutes
  • Scope: Contains family_ids user can access
  • Usage:
    Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
    

2. Refresh Token

  • Purpose: Issue new access tokens without re-login
  • Lifetime: 30 days
  • Usage:
    POST /api/v1/auth/refresh
    { "refresh_token": "..." }
    

3. Service Token

  • Purpose: Telegram bot → API communication
  • Lifetime: 1 year
  • Scope: "telegram_bot" service
  • Note: Different from user tokens; issued separately

HMAC Signature Verification

Base String Format:

METHOD:ENDPOINT:TIMESTAMP:BODY_HASH
POST:/api/v1/transactions:1702237800:a3f5d8c2e1b9...

Headers Required:

X-Signature: HMAC_SHA256(base_string, client_secret)
X-Timestamp: 1702237800
X-Client-Id: telegram_bot|web_frontend|ios_app

Verification Steps:

  1. Check timestamp freshness (±30 seconds)
  2. Reconstruct base_string
  3. Compute HMAC with client secret
  4. Compare with X-Signature
  5. Check signature nonce (prevent replay)

MVP Default: HMAC disabled (require_hmac_verification=false)

Encryption Strategy

Data Encryption Notes
Password Hash bcrypt Never store plain passwords
Phone Number AES-256 At rest, logged as masked
Notes/Descriptions None (MVP) Can add AES-256 for sensitive notes
Transit HTTPS TLS 1.2+ Enforced in production

🔑 Authentication Flows

User Login Flow

┌─────────────────────────────────────────────────────────┐
│ 1. User submits credentials                              │
│    POST /api/v1/auth/login                              │
│    { "email": "user@example.com", "password": "..." }   │
└─────────────────────────────────────────────────────────┘
                     ↓
┌─────────────────────────────────────────────────────────┐
│ 2. Server verifies password hash with bcrypt            │
│    Load user → Load family_ids → Create tokens          │
└─────────────────────────────────────────────────────────┘
                     ↓
┌─────────────────────────────────────────────────────────┐
│ 3. Response                                              │
│    {                                                     │
│      "access_token": "eyJhbGc...",                      │
│      "refresh_token": "eyJhbGc...",                     │
│      "user_id": 123,                                    │
│      "expires_in": 900                                  │
│    }                                                     │
└─────────────────────────────────────────────────────────┘

Telegram Binding Flow

┌─────────────────────────────────────────────────────────┐
│ STEP 1: User sends /start                               │
│ Bot chat_id: 12345                                      │
└─────────────────────────────────────────────────────────┘
                     ↓
┌─────────────────────────────────────────────────────────┐
│ STEP 2: Bot generates binding code                      │
│ POST /api/v1/auth/telegram/start                        │
│ Response: { "code": "ABC123XYZ...", "expires_in": 600 } │
└─────────────────────────────────────────────────────────┘
                     ↓
┌─────────────────────────────────────────────────────────┐
│ STEP 3: Bot sends link to user                          │
│ "Click: https://app.com/auth/telegram?code=ABC123&...  │
└─────────────────────────────────────────────────────────┘
                     ↓
┌─────────────────────────────────────────────────────────┐
│ STEP 4: User clicks link                                │
│ (User must be logged in or create account)              │
│ POST /api/v1/auth/telegram/confirm                      │
│ { "code": "ABC123", "chat_id": 12345 }                  │
│ Headers: Authorization: Bearer <user_jwt>               │
└─────────────────────────────────────────────────────────┘
                     ↓
┌─────────────────────────────────────────────────────────┐
│ STEP 5: Server creates TelegramIdentity record          │
│ TelegramIdentity {                                       │
│   user_id: 123,                                         │
│   chat_id: 12345,                                       │
│   verified_at: now                                      │
│ }                                                        │
│                                                          │
│ Generate JWT for bot usage                              │
│ Response: { "jwt_token": "..." }                        │
└─────────────────────────────────────────────────────────┘
                     ↓
┌─────────────────────────────────────────────────────────┐
│ STEP 6: Bot stores JWT in Redis                         │
│ Redis key: chat_id:12345:jwt                            │
│ Redis key: chat_id:12345:jwt:exp                        │
│ TTL: 30 days                                            │
└─────────────────────────────────────────────────────────┘
                     ↓
┌─────────────────────────────────────────────────────────┐
│ STEP 7: Bot can now make API calls                      │
│ POST /api/v1/transactions                               │
│ Authorization: Bearer <jwt_from_redis>                  │
│ X-Client-Id: telegram_bot                               │
│ X-Signature: HMAC_SHA256(...)                          │
│ X-Timestamp: unixtime                                   │
└─────────────────────────────────────────────────────────┘

👥 RBAC & Permissions

Role Hierarchy

OWNER (Full Control)
  └─ Can manage everything
  └─ Can approve/reject large transactions
  └─ Can manage members
  └─ Can delete family

ADULT
  └─ Can create/edit transactions
  └─ Can approve transactions (with owner)
  └─ Can create budgets/goals
  └─ Can invite members

MEMBER
  └─ Can create/view own transactions
  └─ Can view budgets/goals
  └─ Can view shared reports

CHILD
  └─ Can create/view limited transactions
  └─ Can view their allowance
  └─ Very restricted permissions

READ_ONLY
  └─ Can view reports only
  └─ Audit/observer role

Permission Examples

Action Owner Adult Member Child Read-Only
Create Transaction ✓ (limited)
Edit Own Transaction
Edit Any Transaction
Delete Transaction
Approve Transaction
Create Budget
Manage Members
View Audit Log
Delete Family

RBAC Implementation

In Code:

# Check permission
RBACEngine.check_permission(user_context, Permission.CREATE_TRANSACTION)

# Check family access
RBACEngine.check_family_access(user_context, family_id=1)

# Check resource ownership
RBACEngine.check_resource_ownership(user_context, owner_id=123)

In Endpoint:

@router.post("/transactions")
async def create_transaction(
    request: TransactionCreateRequest,
    user_context: UserContext = Depends(get_user_context),
):
    # Middleware already verified JWT
    # RBAC middleware already checked family access
    # Now just check specific permission
    
    RBACEngine.check_permission(
        user_context,
        Permission.CREATE_TRANSACTION
    )
    
    # Proceed with business logic
    ...

📡 API Endpoints

Authentication

Method Endpoint Purpose
POST /api/v1/auth/login User login
POST /api/v1/auth/refresh Refresh access token
POST /api/v1/auth/logout Revoke session
POST /api/v1/auth/telegram/start Generate binding code
POST /api/v1/auth/telegram/confirm Confirm Telegram binding

Transactions

Method Endpoint Purpose Auth
POST /api/v1/transactions Create transaction JWT + HMAC
GET /api/v1/transactions List transactions JWT
GET /api/v1/transactions/{id} Get transaction JWT
POST /api/v1/transactions/{id}/confirm Approve pending JWT + HMAC
DELETE /api/v1/transactions/{id} Reverse transaction JWT + HMAC

Wallets

Method Endpoint Purpose
POST /api/v1/wallets Create wallet
GET /api/v1/wallets List wallets
GET /api/v1/wallets/summary Balance summary
PUT /api/v1/wallets/{id} Update wallet

Budgets & Goals

Method Endpoint Purpose
POST /api/v1/budgets Create budget
GET /api/v1/budgets List budgets
POST /api/v1/goals Create goal
GET /api/v1/goals List goals

Events & Webhooks

Method Endpoint Purpose
GET /api/v1/events/stream/{family_id} WebSocket event stream
POST /api/v1/events/webhook/telegram-notification Send Telegram notification

🤖 Telegram Bot Integration

Bot Commands

/start   - Begin account binding
/help    - Show available commands
/balance - View wallet balances
/add     - Add new transaction
/reports - View financial reports

Bot API Communication Pattern

# Get user JWT from Redis
jwt_token = redis.get(f"chat_id:{chat_id}:jwt")

# Make API request
async with aiohttp.ClientSession() as session:
    async with session.post(
        "https://api.example.com/api/v1/transactions",
        headers={
            "Authorization": f"Bearer {jwt_token}",
            "X-Client-Id": "telegram_bot",
            "X-Signature": hmac_signature,
            "X-Timestamp": str(timestamp),
        },
        json=payload,
    ) as response:
        result = await response.json()

Event Handling

Bot listens to Redis Streams:
  - transaction.created
  - budget.alert
  - goal.completed
  - member.joined

Bot processes events → Sends Telegram messages

🧪 Testing Strategy

Unit Tests

  • JWT token generation/verification
  • HMAC signature creation/verification
  • RBAC permission checks
  • Service business logic

Integration Tests

  • Full request → response cycles
  • Authentication flows
  • RBAC in middleware
  • Database transactions

Security Tests

  • Invalid token rejection
  • HMAC signature verification
  • Timestamp freshness
  • Signature replay prevention
  • Family isolation

Load Testing Example (k6)

import http from 'k6/http';
import { check } from 'k6';

export let options = {
  vus: 10,
  duration: '30s',
};

export default function() {
  let url = 'http://localhost:8000/api/v1/wallets';
  let params = {
    headers: {
      'Authorization': `Bearer ${__ENV.JWT_TOKEN}`,
      'X-Client-Id': 'k6_test',
    },
  };
  
  let res = http.get(url, params);
  check(res, {
    'status is 200': (r) => r.status === 200,
    'response time < 500ms': (r) => r.timings.duration < 500,
  });
}

🚀 Deployment

Docker Compose (MVP)

version: '3.9'

services:
  api:
    image: finance-bot:latest
    ports:
      - "8000:8000"
    environment:
      - DATABASE_URL=postgresql://...
      - REDIS_URL=redis://redis:6379
      - JWT_SECRET_KEY=...
      - HMAC_SECRET_KEY=...
    depends_on:
      - postgres
      - redis
    networks:
      - finance

  bot:
    image: finance-bot-bot:latest
    environment:
      - BOT_TOKEN=...
      - API_BASE_URL=http://api:8000
      - REDIS_URL=redis://redis:6379
    depends_on:
      - api
      - redis
    networks:
      - finance

  worker:
    image: finance-bot-worker:latest
    environment:
      - REDIS_URL=redis://redis:6379
      - DATABASE_URL=postgresql://...
    depends_on:
      - postgres
      - redis
    networks:
      - finance

  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: finance_db
      POSTGRES_USER: trevor
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - pgdata:/var/lib/postgresql/data
    networks:
      - finance

  redis:
    image: redis:7-alpine
    networks:
      - finance

volumes:
  pgdata:

networks:
  finance:

Kubernetes Deployment (Future)

apiVersion: v1
kind: ConfigMap
metadata:
  name: api-config
data:
  JWT_SECRET_KEY: <secret>
  HMAC_SECRET_KEY: <secret>
  DATABASE_URL: postgresql://postgres/finance_db

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-server
spec:
  replicas: 3
  template:
    spec:
      containers:
      - name: api
        image: finance-bot:latest
        ports:
        - containerPort: 8000
        envFrom:
        - configMapRef:
            name: api-config
        livenessProbe:
          httpGet:
            path: /health
            port: 8000
          initialDelaySeconds: 10
          periodSeconds: 5

Production Checklist

Security

  • Change JWT_SECRET_KEY from default
  • Change HMAC_SECRET_KEY from default
  • Enable HTTPS/TLS (Nginx reverse proxy)
  • Enable CORS only for approved origins
  • Set require_hmac_verification=true
  • Implement password hashing (bcrypt)
  • Implement token blacklisting
  • Add rate limiting (current: 100 req/min)
  • Enable audit logging (EventLog)
  • Encrypt sensitive data at rest

Deployment

  • Run migrations in production
  • Set app_env="production"
  • Disable debug mode
  • Configure proper logging
  • Set up monitoring/alerts
  • Configure backup strategy
  • Test failover procedures
  • Document runbooks

Testing

  • Unit tests coverage > 80%
  • Integration tests for critical flows
  • Security testing (OWASP Top 10)
  • Load testing (identify bottlenecks)
  • Penetration testing
  • API contract testing

Operations

  • Set up CI/CD pipeline
  • Configure health check endpoints
  • Set up application monitoring
  • Configure database backups
  • Document API in OpenAPI/Swagger
  • Set up error tracking (Sentry)
  • Implement graceful shutdown

📚 Additional Resources

OpenAPI Specification

# Auto-generated from FastAPI
GET /docs              # Swagger UI
GET /redoc             # ReDoc documentation
GET /openapi.json      # OpenAPI spec

Architecture Decision Records (ADR)

  • ADR-001: JWT + HMAC for authentication (not just JWT)
  • ADR-002: Redis Streams for event bus (vs RabbitMQ)
  • ADR-003: Compensation transactions for reversals
  • ADR-004: Family-level isolation in all queries

Performance Targets

  • API response time: < 200ms (p95)
  • Transaction creation: < 100ms
  • List queries: < 500ms (for 1000 items)
  • HMAC verification: < 5ms
  • JWT verification: < 2ms

🔄 Roadmap (Post-MVP)

Phase 2: Enhanced Features

  • Web Frontend (React/Vue)
  • Mobile App (React Native/Flutter)
  • Advanced reporting (PDF export, charts)
  • Recurring transactions
  • Currency conversion
  • Multi-family support
  • User notifications preferences

Phase 3: Enterprise Features

  • Kubernetes deployment
  • Multi-region failover
  • Advanced RBAC (custom roles)
  • Audit webhook integrations
  • API rate limiting (per-user)
  • Data export (GDPR compliance)
  • SSO integration (OAuth2)

Document Version: 1.0
Last Updated: 2025-12-10
Status: MVP Complete