Add improvements
This commit is contained in:
@@ -168,6 +168,228 @@ The architecture implements **defense-in-depth** with multiple validation layers
|
||||
- Gateway validates access to requested tenant
|
||||
- Supports hierarchical tenant access patterns
|
||||
|
||||
## JWT Service Token Authentication
|
||||
|
||||
### Overview
|
||||
The Gateway now supports **JWT service tokens** for secure service-to-service (S2S) communication. This replaces the deprecated internal API key system with a unified JWT-based authentication mechanism for both user and service requests.
|
||||
|
||||
### Service Token Support
|
||||
|
||||
**User Tokens** (frontend/API consumers):
|
||||
- `type: "access"` - Regular user authentication
|
||||
- Contains user ID, email, tenant membership, subscription data
|
||||
- Expires in 15-30 minutes
|
||||
- Validated and cached by gateway
|
||||
|
||||
**Service Tokens** (microservice communication):
|
||||
- `type: "service"` - Internal service authentication
|
||||
- Contains service name, admin role, optional tenant context
|
||||
- Expires in 1 hour
|
||||
- Automatically grants admin privileges to registered services
|
||||
|
||||
### Service Token Validation Flow
|
||||
|
||||
```
|
||||
┌─────────────────┐
|
||||
│ Calling Service│
|
||||
│ (e.g., demo) │
|
||||
└────────┬────────┘
|
||||
│
|
||||
│ 1. Create service token
|
||||
│ jwt_handler.create_service_token(
|
||||
│ service_name="demo-session",
|
||||
│ tenant_id=tenant_id
|
||||
│ )
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────┐
|
||||
│ HTTP Request to Gateway │
|
||||
│ -------------------------------- │
|
||||
│ POST /api/v1/tenant/clone │
|
||||
│ Headers: │
|
||||
│ Authorization: Bearer {service_token}│
|
||||
│ X-Service: demo-session-service │
|
||||
└────────┬────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ Gateway │
|
||||
│ Auth Middleware│
|
||||
└────────┬────────┘
|
||||
│
|
||||
│ 2. Extract and verify JWT
|
||||
│ jwt_handler.verify_token(token)
|
||||
│
|
||||
│ 3. Identify service token
|
||||
│ if token.type == "service":
|
||||
│
|
||||
│ 4. Check internal service registry
|
||||
│ if is_internal_service(service_name):
|
||||
│ grant_admin_access()
|
||||
│ skip_tenant_membership_check()
|
||||
│
|
||||
│ 5. Inject service context headers
|
||||
│ X-User-ID: demo-session-service
|
||||
│ X-User-Role: admin
|
||||
│ X-Service-Name: demo-session
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ Target Service │
|
||||
│ (e.g., tenant) │
|
||||
└─────────────────┘
|
||||
```
|
||||
|
||||
### Internal Service Registry
|
||||
|
||||
The gateway uses a centralized registry of all 21 microservices:
|
||||
- **File**: `shared/config/base.py`
|
||||
- **Constant**: `INTERNAL_SERVICES` set
|
||||
- **Services**: gateway, auth, tenant, inventory, production, recipes, suppliers, orders, sales, procurement, pos, forecasting, training, ai-insights, orchestrator, notification, alert-processor, demo-session, external, distribution
|
||||
|
||||
**Automatic Privileges for Registered Services:**
|
||||
- Admin role granted automatically
|
||||
- Skip tenant membership validation
|
||||
- Access to all tenants within scope
|
||||
- Optimized database queries
|
||||
|
||||
### Service Token Payload
|
||||
|
||||
```json
|
||||
{
|
||||
"sub": "demo-session",
|
||||
"user_id": "demo-session-service",
|
||||
"email": "demo-session-service@internal",
|
||||
"service": "demo-session",
|
||||
"type": "service",
|
||||
"role": "admin",
|
||||
"tenant_id": "optional-tenant-uuid",
|
||||
"exp": 1735693199,
|
||||
"iat": 1735689599,
|
||||
"iss": "bakery-auth"
|
||||
}
|
||||
```
|
||||
|
||||
### Gateway Processing
|
||||
|
||||
#### Token Validation (`_validate_token_payload`)
|
||||
```python
|
||||
# Validates token type and required fields
|
||||
token_type = payload.get("type")
|
||||
if token_type not in ["access", "service"]:
|
||||
return False
|
||||
|
||||
# Service tokens with tenant context are valid
|
||||
if token_type == "service" and payload.get("tenant_id"):
|
||||
logger.debug("Service token with tenant context validated")
|
||||
```
|
||||
|
||||
#### User Context Extraction (`_jwt_payload_to_user_context`)
|
||||
```python
|
||||
# Detect service tokens
|
||||
if payload.get("service"):
|
||||
service_name = payload["service"]
|
||||
base_context = {
|
||||
"user_id": f"{service_name}-service",
|
||||
"email": f"{service_name}-service@internal",
|
||||
"service": service_name,
|
||||
"type": "service",
|
||||
"role": "admin", # Services get admin privileges
|
||||
"tenant_id": payload.get("tenant_id") # Optional tenant context
|
||||
}
|
||||
```
|
||||
|
||||
#### Tenant Access Control
|
||||
```python
|
||||
# Skip tenant access verification for service tokens
|
||||
if user_context.get("type") != "service":
|
||||
# Verify user has access to tenant
|
||||
has_access = await tenant_access_manager.verify_basic_tenant_access(
|
||||
user_context["user_id"], tenant_id
|
||||
)
|
||||
else:
|
||||
# Services have automatic access
|
||||
logger.debug(f"Service token granted access to tenant {tenant_id}")
|
||||
```
|
||||
|
||||
### Migration from Internal API Keys
|
||||
|
||||
**Old System (Deprecated - Removed in 2026-01):**
|
||||
```python
|
||||
# REMOVED - No longer supported
|
||||
headers = {
|
||||
"X-Internal-API-Key": "dev-internal-key-change-in-production"
|
||||
}
|
||||
```
|
||||
|
||||
**New System (Current):**
|
||||
```python
|
||||
# Gateway creates service tokens for internal calls
|
||||
from shared.auth.jwt_handler import JWTHandler
|
||||
|
||||
jwt_handler = JWTHandler(settings.JWT_SECRET_KEY, settings.JWT_ALGORITHM)
|
||||
service_token = jwt_handler.create_service_token(service_name="gateway")
|
||||
|
||||
headers = {
|
||||
"Authorization": f"Bearer {service_token}"
|
||||
}
|
||||
```
|
||||
|
||||
### Security Benefits
|
||||
|
||||
1. **Token Expiration** - Service tokens expire (1 hour), preventing indefinite access
|
||||
2. **Signature Verification** - JWT signatures prevent token forgery and tampering
|
||||
3. **Tenant Scoping** - Service tokens can include tenant context for proper authorization
|
||||
4. **Unified Authentication** - Same JWT verification logic for user and service tokens
|
||||
5. **Audit Trail** - All service requests are authenticated and logged with service identity
|
||||
6. **No Shared Secrets** - Services don't share API keys; use shared JWT secret instead
|
||||
7. **Rotation Ready** - JWT secret can be rotated without code changes
|
||||
|
||||
### Performance Impact
|
||||
|
||||
- **Token Creation**: <1ms (in-memory JWT signing)
|
||||
- **Token Validation**: <1ms (in-memory JWT verification with shared secret)
|
||||
- **Caching**: Gateway caches validated service tokens for 5 minutes
|
||||
- **No Additional HTTP Calls**: Service auth happens locally at gateway
|
||||
|
||||
### Context Header Injection
|
||||
|
||||
When a service token is validated, the gateway injects these headers for downstream services:
|
||||
|
||||
```python
|
||||
X-User-ID: demo-session-service
|
||||
X-User-Email: demo-session-service@internal
|
||||
X-User-Role: admin
|
||||
X-User-Type: service
|
||||
X-Service-Name: demo-session
|
||||
X-Tenant-ID: {tenant_id} # If present in token
|
||||
```
|
||||
|
||||
### Gateway-to-Service Communication
|
||||
|
||||
The gateway itself creates service tokens when calling internal services:
|
||||
|
||||
#### Example: Demo Session Validation for SSE
|
||||
```python
|
||||
# gateway/app/middleware/auth.py
|
||||
service_token = jwt_handler.create_service_token(service_name="gateway")
|
||||
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.get(
|
||||
f"http://demo-session-service:8000/api/v1/demo/sessions/{session_id}",
|
||||
headers={"Authorization": f"Bearer {service_token}"}
|
||||
)
|
||||
```
|
||||
|
||||
### Shared JWT Secret
|
||||
|
||||
All services (including gateway) use the same JWT secret key:
|
||||
- **File**: `shared/config/base.py`
|
||||
- **Variable**: `JWT_SECRET_KEY`
|
||||
- **Default**: `usMHw9kQCQoyrc7wPmMi3bClr0lTY9wvzZmcTbADvL0=`
|
||||
- **Environment Override**: `JWT_SECRET_KEY` environment variable
|
||||
- **Production**: Must be set to a secure random value
|
||||
|
||||
## API Endpoints (Key Routes)
|
||||
|
||||
### Authentication Routes
|
||||
|
||||
Reference in New Issue
Block a user