19 KiB
19 KiB
🏗️ API-First Zero-Trust Architecture - Complete Guide
📋 Table of Contents
- Architecture Overview
- Security Model
- Authentication Flows
- RBAC & Permissions
- API Endpoints
- Telegram Bot Integration
- Testing Strategy
- Deployment
- 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:
- Check timestamp freshness (±30 seconds)
- Reconstruct base_string
- Compute HMAC with client secret
- Compare with X-Signature
- 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