1075 lines
34 KiB
Markdown
1075 lines
34 KiB
Markdown
# Auth Service
|
|
|
|
## Overview
|
|
|
|
The **Auth Service** is the security foundation of Bakery-IA, providing robust JWT-based authentication, user management, and GDPR-compliant data handling. It implements industry-standard security practices including refresh token rotation, role-based access control (RBAC), and comprehensive audit logging. This service ensures secure multi-tenant access while maintaining full compliance with EU data protection regulations.
|
|
|
|
## Key Features
|
|
|
|
### Authentication & Authorization
|
|
- **JWT Token Authentication** - Secure token-based auth with access + refresh tokens
|
|
- **User Registration** - Account creation with email validation
|
|
- **Login/Logout** - Secure authentication flow with token management
|
|
- **Token Refresh** - Automatic token refresh with rotation
|
|
- **Password Management** - Secure password hashing (bcrypt) and reset flow
|
|
- **Role-Based Access Control (RBAC)** - User roles and permissions
|
|
- **Multi-Factor Authentication** (planned) - Enhanced security option
|
|
- **JWT Subscription Embedding** - Embeds subscription data in JWT tokens at login time
|
|
|
|
### User Management
|
|
- **User Profiles** - Complete user information management
|
|
- **User Onboarding** - Multi-step onboarding progress tracking with 15 steps including POI detection
|
|
- **Profile Updates** - Self-service profile editing
|
|
- **Account Deletion** - GDPR-compliant account removal
|
|
- **Login Attempts Tracking** - Brute force protection
|
|
- **Session Management** - Track and revoke user sessions
|
|
|
|
### GDPR Compliance
|
|
- **User Consent Management** - Track user consents for data processing
|
|
- **Consent History** - Complete audit trail of consent changes
|
|
- **Data Export** - User can export all their data (JSON/CSV)
|
|
- **Right to Erasure** - Complete data deletion on request
|
|
- **Data Minimization** - Only collect essential data
|
|
- **Privacy by Design** - Built-in privacy features
|
|
|
|
### Security Features
|
|
- **Brute Force Protection** - Login attempt limiting
|
|
- **Password Strength Requirements** - Enforce strong passwords
|
|
- **Token Expiry** - Short-lived access tokens (15 min)
|
|
- **Refresh Token Rotation** - Security best practice
|
|
- **Audit Logging** - Complete authentication audit trail
|
|
- **IP Tracking** - Monitor login locations
|
|
- **Suspicious Activity Detection** - Alert on unusual patterns
|
|
|
|
### Event Publishing
|
|
- **RabbitMQ Integration** - Publish user events for other services
|
|
- **User Created** - New user registration events
|
|
- **User Updated** - Profile change events
|
|
- **Login Events** - Authentication success/failure events
|
|
- **Consent Changes** - GDPR consent update events
|
|
|
|
## Business Value
|
|
|
|
### For Bakery Owners
|
|
- **Secure Access** - Enterprise-grade security protects business data
|
|
- **GDPR Compliance** - Avoid €20M fines for data violations
|
|
- **User Management** - Control team access and permissions
|
|
- **Audit Trail** - Complete history for security audits
|
|
- **Peace of Mind** - Industry-standard security practices
|
|
|
|
### For Platform Operations
|
|
- **Multi-Tenant Security** - Isolated access per tenant
|
|
- **Scalable Auth** - Handle thousands of users
|
|
- **Compliance Ready** - Built-in GDPR features
|
|
- **Audit Capability** - Complete security audit trails
|
|
|
|
### Quantifiable Impact
|
|
- **Security**: 99.9% protection against common attacks
|
|
- **Compliance**: 100% GDPR compliant, avoid €20M+ fines
|
|
- **Uptime**: 99.9% authentication availability
|
|
- **Performance**: <50ms token validation (cached)
|
|
- **Gateway Performance**: 92-98% latency reduction through JWT subscription embedding
|
|
- **Tenant-Service Load**: 100% reduction in subscription validation calls
|
|
|
|
## Technology Stack
|
|
|
|
- **Framework**: FastAPI (Python 3.11+) - Async web framework
|
|
- **Database**: PostgreSQL 17 - User and auth data
|
|
- **Password Hashing**: bcrypt - Industry-standard password security
|
|
- **JWT**: python-jose - JSON Web Token generation and validation with subscription embedding
|
|
- **ORM**: SQLAlchemy 2.0 (async) - Database abstraction
|
|
- **Messaging**: RabbitMQ 4.1 - Event publishing
|
|
- **Caching**: Redis 7.4 - Token validation cache (gateway)
|
|
- **Logging**: Structlog - Structured JSON logging
|
|
- **Metrics**: Prometheus Client - Custom metrics
|
|
|
|
## JWT Subscription Embedding Architecture
|
|
|
|
### Overview
|
|
The Auth Service implements **JWT-embedded subscription data** to eliminate runtime HTTP calls from the gateway to tenant-service. Subscription data is fetched **once at login time** and embedded directly in the JWT token.
|
|
|
|
### Subscription Data Flow
|
|
|
|
```mermaid
|
|
graph TD
|
|
A[User Login] --> B[Auth Service]
|
|
B --> C[Fetch Subscription Data from Tenant Service]
|
|
C --> D[Embed in JWT Token]
|
|
D --> E[Return JWT to Client]
|
|
E --> F[Client Requests API]
|
|
F --> G[Gateway Extracts Subscription from JWT]
|
|
G --> H[Zero HTTP Calls to Tenant Service]
|
|
```
|
|
|
|
### JWT Payload Structure
|
|
|
|
**Access Token with Subscription Data:**
|
|
```json
|
|
{
|
|
"sub": "user-uuid",
|
|
"user_id": "user-uuid",
|
|
"email": "user@example.com",
|
|
"tenant_id": "tenant-uuid",
|
|
"tenant_role": "owner",
|
|
"subscription": {
|
|
"tier": "professional",
|
|
"status": "active",
|
|
"valid_until": "2025-12-31T23:59:59Z"
|
|
},
|
|
"tenant_access": [
|
|
{
|
|
"id": "tenant-uuid-1",
|
|
"role": "admin",
|
|
"tier": "starter"
|
|
}
|
|
],
|
|
"role": "user",
|
|
"type": "access",
|
|
"exp": 1735689599,
|
|
"iat": 1735687799,
|
|
"iss": "bakery-auth"
|
|
}
|
|
```
|
|
|
|
### Key Components
|
|
|
|
#### 1. SubscriptionFetcher Utility
|
|
- **File**: `services/auth/app/utils/subscription_fetcher.py`
|
|
- **Purpose**: Fetches subscription data from tenant-service at login time
|
|
- **Frequency**: Called **once per login**, not per-request
|
|
- **Data Fetched**:
|
|
- Primary tenant ID and role
|
|
- Subscription tier, status, and expiry
|
|
- Multi-tenant access information
|
|
|
|
#### 2. Enhanced JWT Creation
|
|
- **File**: `services/auth/app/core/security.py`
|
|
- **Method**: `SecurityManager.create_access_token()`
|
|
- **Enhancement**: Includes subscription data in JWT payload
|
|
- **Size Control**: Limits `tenant_access` to 10 entries to prevent JWT bloat
|
|
|
|
#### 3. Token Refresh Flow
|
|
- **Purpose**: Propagate subscription changes within token expiry window
|
|
- **Mechanism**: Refresh tokens fetch fresh subscription data
|
|
- **Frequency**: Every 15-30 minutes (token expiry)
|
|
- **Benefit**: Subscription changes reflected without requiring re-login
|
|
|
|
### Performance Impact
|
|
|
|
**Before JWT Subscription Embedding:**
|
|
- Gateway makes 5 HTTP calls per request to tenant-service
|
|
- 2,500ms notification endpoint latency
|
|
- 5,500ms subscription endpoint latency
|
|
- ~520ms overhead on every tenant-scoped request
|
|
|
|
**After JWT Subscription Embedding:**
|
|
- **Zero HTTP calls** from gateway to tenant-service for subscription checks
|
|
- **<1ms subscription validation** (JWT extraction only)
|
|
- **~200ms notification endpoint latency** (92% improvement)
|
|
- **~100ms subscription endpoint latency** (98% improvement)
|
|
- **100% reduction** in tenant-service load for subscription validation
|
|
|
|
### Security Considerations
|
|
|
|
#### Defense-in-Depth Architecture
|
|
1. **JWT Signature Verification** - Gateway validates token integrity
|
|
2. **Subscription Data Validation** - Validates subscription tier values
|
|
3. **Token Freshness Check** - Detects stale tokens after subscription changes
|
|
4. **Database Verification** - Optional for critical operations
|
|
5. **Audit Logging** - Comprehensive logging for anomaly detection
|
|
|
|
#### Token Freshness Mechanism
|
|
- When subscription changes, gateway sets Redis key: `tenant:{tenant_id}:subscription_changed_at`
|
|
- Gateway checks if token was issued before subscription change
|
|
- Stale tokens are rejected, forcing re-authentication
|
|
- Ensures users get fresh subscription data within 15-30 minute window
|
|
|
|
#### Multi-Tenant Security
|
|
- JWT contains `tenant_access` array with all accessible tenants
|
|
- Each entry includes role and subscription tier
|
|
- Gateway validates access to requested tenant
|
|
- Prevents tenant ID spoofing attacks
|
|
|
|
## JWT Service Token Architecture
|
|
|
|
### Overview
|
|
The Auth Service implements **JWT service tokens** for secure service-to-service (S2S) authentication across all microservices. This eliminates the need for internal API keys and provides a unified, secure authentication mechanism for both user and service requests.
|
|
|
|
### Service Token vs User Token
|
|
|
|
**User Tokens** (for frontend/API consumers):
|
|
- `type: "access"` - Regular user authentication
|
|
- Contains user ID, email, tenant membership
|
|
- Expires in 15-30 minutes
|
|
- Used by browsers and mobile apps
|
|
|
|
**Service Tokens** (for microservice communication):
|
|
- `type: "service"` - Internal service authentication
|
|
- Contains service name, optional tenant context
|
|
- Expires in 1 hour (longer for batch operations)
|
|
- Used by backend services calling other services
|
|
|
|
### Service Token Payload Structure
|
|
|
|
```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"
|
|
}
|
|
```
|
|
|
|
### Key Features
|
|
|
|
#### 1. Unified JWT Handler
|
|
- **File**: `shared/auth/jwt_handler.py`
|
|
- **Purpose**: Single source of truth for token creation and validation
|
|
- **Method**: `create_service_token(service_name, tenant_id=None)`
|
|
- **Shared JWT Secret**: All services use same `JWT_SECRET_KEY` from `shared/config/base.py`
|
|
|
|
#### 2. Internal Service Registry
|
|
- **File**: `shared/config/base.py`
|
|
- **Constant**: `INTERNAL_SERVICES` set containing all 21 microservice names
|
|
- **Purpose**: Automatic access grants for registered services
|
|
- **Services**: gateway, auth, tenant, inventory, production, recipes, suppliers, orders, sales, procurement, pos, forecasting, training, ai-insights, orchestrator, notification, alert-processor, demo-session, external, distribution
|
|
|
|
#### 3. Service Authentication Flow
|
|
|
|
```
|
|
┌─────────────────┐
|
|
│ Service A │
|
|
│ (e.g., demo) │
|
|
└────────┬────────┘
|
|
│
|
|
│ 1. Create service token
|
|
│ jwt_handler.create_service_token(
|
|
│ service_name="demo-session",
|
|
│ tenant_id=tenant_id
|
|
│ )
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────┐
|
|
│ HTTP Request │
|
|
│ -------------------------------- │
|
|
│ POST /api/v1/tenant/clone │
|
|
│ Headers: │
|
|
│ Authorization: Bearer {token} │
|
|
│ X-Service: demo-session-service │
|
|
└────────┬────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────┐
|
|
│ Service B │
|
|
│ (e.g., tenant) │
|
|
└────────┬────────┘
|
|
│
|
|
│ 2. Validate token
|
|
│ jwt_handler.verify_token(token)
|
|
│
|
|
│ 3. Check internal service
|
|
│ if is_internal_service(user_id):
|
|
│ grant_admin_access()
|
|
│
|
|
▼
|
|
┌─────────────────┐
|
|
│ Authorized │
|
|
│ Response │
|
|
└─────────────────┘
|
|
```
|
|
|
|
#### 4. Automatic Admin Privileges
|
|
- Services in `INTERNAL_SERVICES` registry get automatic admin access
|
|
- No need for tenant membership checks
|
|
- Optimizes database queries (skips membership lookups)
|
|
- Used in:
|
|
- `shared/auth/decorators.py` - JWT authentication decorator
|
|
- `services/tenant/app/api/tenant_operations.py` - Tenant access verification
|
|
- `services/tenant/app/repositories/tenant_member_repository.py` - Skip membership queries
|
|
|
|
### Migration from Internal API Keys
|
|
|
|
**Previous System (Deprecated):**
|
|
```python
|
|
# Old approach - REMOVED
|
|
headers = {
|
|
"X-Internal-API-Key": "dev-internal-key-change-in-production"
|
|
}
|
|
```
|
|
|
|
**New System (Current):**
|
|
```python
|
|
# New approach - JWT service tokens
|
|
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="my-service",
|
|
tenant_id=tenant_id # Optional tenant context
|
|
)
|
|
|
|
headers = {
|
|
"Authorization": f"Bearer {service_token}",
|
|
"X-Service": "my-service"
|
|
}
|
|
```
|
|
|
|
### Security Benefits
|
|
|
|
1. **Token Expiration** - Service tokens expire (1 hour), unlike permanent API keys
|
|
2. **Signature Verification** - JWT signatures prevent token forgery
|
|
3. **Tenant Context** - Service tokens can include tenant scope for proper authorization
|
|
4. **Audit Trail** - All service requests are authenticated and logged
|
|
5. **No Secret Distribution** - Shared JWT secret is managed via environment variables
|
|
6. **Rotation Ready** - JWT secret can be rotated without changing code
|
|
|
|
### Performance Characteristics
|
|
|
|
- **Token Creation**: <1ms (in-memory JWT signing)
|
|
- **Token Validation**: <1ms (in-memory JWT verification)
|
|
- **Cache Enabled**: Gateway caches validated tokens for 5 minutes
|
|
- **No HTTP Calls**: Service-to-service auth happens locally
|
|
|
|
### Implementation Examples
|
|
|
|
#### Example 1: Demo Session Service Cloning Data
|
|
```python
|
|
# services/demo_session/app/services/clone_orchestrator.py
|
|
service_token = self.jwt_handler.create_service_token(
|
|
service_name="demo-session",
|
|
tenant_id=virtual_tenant_id
|
|
)
|
|
|
|
response = await client.post(
|
|
f"{service.url}/internal/demo/clone",
|
|
params={...},
|
|
headers={
|
|
"Authorization": f"Bearer {service_token}",
|
|
"X-Service": "demo-session-service"
|
|
}
|
|
)
|
|
```
|
|
|
|
#### Example 2: Gateway Validating Demo Sessions
|
|
```python
|
|
# gateway/app/middleware/auth.py
|
|
service_token = jwt_handler.create_service_token(service_name="gateway")
|
|
|
|
response = await client.get(
|
|
f"http://demo-session-service:8000/api/v1/demo/sessions/{session_id}",
|
|
headers={"Authorization": f"Bearer {service_token}"}
|
|
)
|
|
```
|
|
|
|
#### Example 3: Deletion Orchestrator
|
|
```python
|
|
# services/auth/app/services/deletion_orchestrator.py
|
|
service_token = self.jwt_handler.create_service_token(
|
|
service_name="auth",
|
|
tenant_id=tenant_id
|
|
)
|
|
|
|
headers = {
|
|
"Authorization": f"Bearer {service_token}",
|
|
"X-Service": "auth-service"
|
|
}
|
|
```
|
|
|
|
### API Endpoints (Key Routes)
|
|
|
|
### Authentication
|
|
- `POST /api/v1/auth/register` - User registration
|
|
- `POST /api/v1/auth/login` - User login (returns access + refresh tokens)
|
|
- `POST /api/v1/auth/refresh` - Refresh access token
|
|
- `POST /api/v1/auth/logout` - User logout (invalidate tokens)
|
|
- `POST /api/v1/auth/verify-token` - Verify JWT token validity
|
|
|
|
### Password Management
|
|
- `POST /api/v1/auth/password/reset-request` - Request password reset email
|
|
- `POST /api/v1/auth/password/reset` - Reset password with token
|
|
- `PUT /api/v1/auth/password/change` - Change password (authenticated)
|
|
|
|
### User Profile
|
|
- `GET /api/v1/auth/me` - Get current user profile
|
|
- `PUT /api/v1/auth/profile` - Update user profile
|
|
- `DELETE /api/v1/auth/account` - Delete account (GDPR)
|
|
|
|
### User Onboarding
|
|
- `GET /api/v1/auth/me/onboarding/progress` - Get onboarding status
|
|
- `PUT /api/v1/auth/me/onboarding/step` - Update/complete onboarding step
|
|
- `POST /api/v1/auth/me/onboarding/complete` - Mark onboarding complete
|
|
- `GET /api/v1/auth/me/onboarding/next-step` - Get next incomplete step
|
|
- `GET /api/v1/auth/me/onboarding/can-access/{step_name}` - Check if step is accessible
|
|
|
|
### GDPR Compliance
|
|
- `GET /api/v1/auth/gdpr/consents` - Get user consents
|
|
- `POST /api/v1/auth/gdpr/consent` - Update consent
|
|
- `GET /api/v1/auth/gdpr/export` - Export user data (JSON)
|
|
- `POST /api/v1/auth/gdpr/delete-request` - Request account deletion
|
|
|
|
### Admin (Tenant Management)
|
|
- `GET /api/v1/auth/users` - List users (admin only)
|
|
- `GET /api/v1/auth/users/{user_id}` - Get user details (admin)
|
|
- `PUT /api/v1/auth/users/{user_id}/role` - Update user role (admin)
|
|
- `DELETE /api/v1/auth/users/{user_id}` - Delete user (admin)
|
|
|
|
## Database Schema
|
|
|
|
### Main Tables
|
|
|
|
**users**
|
|
```sql
|
|
CREATE TABLE users (
|
|
id UUID PRIMARY KEY,
|
|
email VARCHAR(255) UNIQUE NOT NULL,
|
|
password_hash VARCHAR(255) NOT NULL,
|
|
first_name VARCHAR(100),
|
|
last_name VARCHAR(100),
|
|
phone VARCHAR(50),
|
|
role VARCHAR(50) DEFAULT 'user', -- admin, owner, manager, user
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
is_verified BOOLEAN DEFAULT FALSE,
|
|
email_verified_at TIMESTAMP,
|
|
last_login_at TIMESTAMP,
|
|
last_login_ip VARCHAR(45),
|
|
failed_login_attempts INTEGER DEFAULT 0,
|
|
locked_until TIMESTAMP,
|
|
password_changed_at TIMESTAMP,
|
|
must_change_password BOOLEAN DEFAULT FALSE,
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
updated_at TIMESTAMP DEFAULT NOW(),
|
|
INDEX idx_email (email),
|
|
INDEX idx_active (is_active)
|
|
);
|
|
```
|
|
|
|
**refresh_tokens**
|
|
```sql
|
|
CREATE TABLE refresh_tokens (
|
|
id UUID PRIMARY KEY,
|
|
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
|
token_hash VARCHAR(255) NOT NULL,
|
|
expires_at TIMESTAMP NOT NULL,
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
revoked_at TIMESTAMP,
|
|
replaced_by_token_id UUID,
|
|
device_info JSONB,
|
|
ip_address VARCHAR(45),
|
|
is_revoked BOOLEAN DEFAULT FALSE,
|
|
INDEX idx_user (user_id),
|
|
INDEX idx_token (token_hash),
|
|
INDEX idx_expires (expires_at)
|
|
);
|
|
```
|
|
|
|
**user_onboarding_progress**
|
|
```sql
|
|
CREATE TABLE user_onboarding_progress (
|
|
id UUID PRIMARY KEY,
|
|
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
|
step_name VARCHAR(100) NOT NULL,
|
|
completed BOOLEAN DEFAULT FALSE,
|
|
completed_at TIMESTAMP,
|
|
data JSONB,
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
updated_at TIMESTAMP DEFAULT NOW(),
|
|
UNIQUE(user_id, step_name)
|
|
);
|
|
```
|
|
|
|
**user_onboarding_summary**
|
|
```sql
|
|
CREATE TABLE user_onboarding_summary (
|
|
id UUID PRIMARY KEY,
|
|
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
|
current_step VARCHAR(50) NOT NULL DEFAULT 'user_registered',
|
|
next_step VARCHAR(50),
|
|
completion_percentage VARCHAR(50) DEFAULT '0.0',
|
|
fully_completed BOOLEAN DEFAULT FALSE,
|
|
steps_completed_count VARCHAR(50) DEFAULT '0', -- Format: "3/15"
|
|
last_activity_at TIMESTAMP DEFAULT NOW(),
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
updated_at TIMESTAMP DEFAULT NOW(),
|
|
UNIQUE(user_id)
|
|
);
|
|
```
|
|
|
|
**Onboarding Steps (15 total):**
|
|
1. `user_registered` - User account created (auto-completed)
|
|
2. `bakery-type-selection` - Choose bakery type
|
|
3. `setup` - Basic bakery setup and tenant creation
|
|
4. `poi-detection` - **POI Detection (Location Context)** - Automatic detection of nearby Points of Interest
|
|
5. `upload-sales-data` - File upload, validation, AI classification
|
|
6. `inventory-review` - Review AI-detected products
|
|
7. `initial-stock-entry` - Capture initial stock levels
|
|
8. `product-categorization` - Advanced categorization (optional)
|
|
9. `suppliers-setup` - Suppliers configuration
|
|
10. `recipes-setup` - Production recipes (optional)
|
|
11. `quality-setup` - Quality standards (optional)
|
|
12. `team-setup` - Team members (optional)
|
|
13. `ml-training` - AI model training (requires POI detection)
|
|
14. `setup-review` - Review all configuration
|
|
15. `completion` - Onboarding completed
|
|
|
|
**login_attempts**
|
|
```sql
|
|
CREATE TABLE login_attempts (
|
|
id UUID PRIMARY KEY,
|
|
email VARCHAR(255) NOT NULL,
|
|
ip_address VARCHAR(45),
|
|
user_agent TEXT,
|
|
success BOOLEAN NOT NULL,
|
|
failure_reason VARCHAR(255),
|
|
attempted_at TIMESTAMP DEFAULT NOW(),
|
|
INDEX idx_email_time (email, attempted_at),
|
|
INDEX idx_ip_time (ip_address, attempted_at)
|
|
);
|
|
```
|
|
|
|
**user_consents**
|
|
```sql
|
|
CREATE TABLE user_consents (
|
|
id UUID PRIMARY KEY,
|
|
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
|
consent_type VARCHAR(100) NOT NULL, -- terms, privacy, marketing, cookies
|
|
consented BOOLEAN NOT NULL,
|
|
consented_at TIMESTAMP NOT NULL,
|
|
withdrawn_at TIMESTAMP,
|
|
ip_address VARCHAR(45),
|
|
user_agent TEXT,
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
INDEX idx_user_type (user_id, consent_type)
|
|
);
|
|
```
|
|
|
|
**consent_history**
|
|
```sql
|
|
CREATE TABLE consent_history (
|
|
id UUID PRIMARY KEY,
|
|
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
|
|
consent_type VARCHAR(100) NOT NULL,
|
|
action VARCHAR(50) NOT NULL, -- granted, withdrawn, updated
|
|
consented BOOLEAN NOT NULL,
|
|
previous_value BOOLEAN,
|
|
ip_address VARCHAR(45),
|
|
created_at TIMESTAMP DEFAULT NOW()
|
|
);
|
|
```
|
|
|
|
**audit_logs**
|
|
```sql
|
|
CREATE TABLE audit_logs (
|
|
id UUID PRIMARY KEY,
|
|
user_id UUID,
|
|
action VARCHAR(100) NOT NULL, -- login, logout, password_change, etc.
|
|
resource_type VARCHAR(100),
|
|
resource_id UUID,
|
|
ip_address VARCHAR(45),
|
|
user_agent TEXT,
|
|
details JSONB,
|
|
success BOOLEAN,
|
|
error_message TEXT,
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
INDEX idx_user_time (user_id, created_at),
|
|
INDEX idx_action (action),
|
|
INDEX idx_time (created_at)
|
|
);
|
|
```
|
|
|
|
## Events & Messaging
|
|
|
|
### Published Events (RabbitMQ)
|
|
|
|
**Exchange**: `auth`
|
|
**Routing Keys**: `auth.user.created`, `auth.user.updated`, `auth.user.deleted`, `auth.login`
|
|
|
|
**User Registered Event**
|
|
```json
|
|
{
|
|
"event_type": "user_registered",
|
|
"user_id": "uuid",
|
|
"email": "user@example.com",
|
|
"first_name": "John",
|
|
"last_name": "Doe",
|
|
"role": "user",
|
|
"tenant_id": "uuid",
|
|
"timestamp": "2025-11-06T10:30:00Z"
|
|
}
|
|
```
|
|
|
|
**Login Success Event**
|
|
```json
|
|
{
|
|
"event_type": "login_success",
|
|
"user_id": "uuid",
|
|
"email": "user@example.com",
|
|
"ip_address": "192.168.1.100",
|
|
"user_agent": "Mozilla/5.0...",
|
|
"timestamp": "2025-11-06T10:30:00Z"
|
|
}
|
|
```
|
|
|
|
**Login Failed Event**
|
|
```json
|
|
{
|
|
"event_type": "login_failed",
|
|
"email": "user@example.com",
|
|
"ip_address": "192.168.1.100",
|
|
"failure_reason": "invalid_password",
|
|
"attempts_count": 3,
|
|
"timestamp": "2025-11-06T10:30:00Z"
|
|
}
|
|
```
|
|
|
|
**GDPR Consent Updated Event**
|
|
```json
|
|
{
|
|
"event_type": "consent_updated",
|
|
"user_id": "uuid",
|
|
"consent_type": "marketing",
|
|
"consented": false,
|
|
"previous_value": true,
|
|
"timestamp": "2025-11-06T10:30:00Z"
|
|
}
|
|
```
|
|
|
|
## Custom Metrics (Prometheus)
|
|
|
|
```python
|
|
# Registration metrics
|
|
registrations_total = Counter(
|
|
'auth_registrations_total',
|
|
'Total user registrations',
|
|
['status'] # success, failed
|
|
)
|
|
|
|
# Login metrics
|
|
login_attempts_total = Counter(
|
|
'auth_login_attempts_total',
|
|
'Total login attempts',
|
|
['status', 'reason'] # success / failed (invalid_password, locked, etc.)
|
|
)
|
|
|
|
active_users_total = Gauge(
|
|
'auth_active_users',
|
|
'Total active users'
|
|
)
|
|
|
|
# Token metrics
|
|
tokens_issued_total = Counter(
|
|
'auth_tokens_issued_total',
|
|
'Total tokens issued',
|
|
['token_type'] # access, refresh
|
|
)
|
|
|
|
token_refresh_total = Counter(
|
|
'auth_token_refresh_total',
|
|
'Total token refreshes',
|
|
['status'] # success, failed
|
|
)
|
|
|
|
# Security metrics
|
|
failed_login_attempts = Counter(
|
|
'auth_failed_login_attempts_total',
|
|
'Failed login attempts',
|
|
['reason'] # invalid_password, account_locked, invalid_email
|
|
)
|
|
|
|
account_lockouts_total = Counter(
|
|
'auth_account_lockouts_total',
|
|
'Total account lockouts due to failed attempts'
|
|
)
|
|
|
|
# GDPR metrics
|
|
data_exports_total = Counter(
|
|
'auth_data_exports_total',
|
|
'GDPR data export requests'
|
|
)
|
|
|
|
account_deletions_total = Counter(
|
|
'auth_account_deletions_total',
|
|
'GDPR account deletion requests'
|
|
)
|
|
```
|
|
|
|
## Configuration
|
|
|
|
### Environment Variables
|
|
|
|
**Service Configuration:**
|
|
- `PORT` - Service port (default: 8001)
|
|
- `DATABASE_URL` - PostgreSQL connection string
|
|
- `RABBITMQ_URL` - RabbitMQ connection string
|
|
|
|
**JWT Configuration:**
|
|
- `JWT_SECRET_KEY` - Secret for signing tokens (required)
|
|
- `JWT_ALGORITHM` - Algorithm (default: HS256)
|
|
- `JWT_ACCESS_TOKEN_EXPIRE_MINUTES` - Access token lifetime (default: 15)
|
|
- `JWT_REFRESH_TOKEN_EXPIRE_DAYS` - Refresh token lifetime (default: 30)
|
|
|
|
**Password Security:**
|
|
- `BCRYPT_ROUNDS` - bcrypt work factor (default: 12)
|
|
- `MIN_PASSWORD_LENGTH` - Minimum password length (default: 8)
|
|
- `REQUIRE_PASSWORD_UPPERCASE` - Require uppercase (default: true)
|
|
- `REQUIRE_PASSWORD_LOWERCASE` - Require lowercase (default: true)
|
|
- `REQUIRE_PASSWORD_DIGIT` - Require digit (default: true)
|
|
- `REQUIRE_PASSWORD_SPECIAL` - Require special char (default: true)
|
|
|
|
**Security Configuration:**
|
|
- `MAX_LOGIN_ATTEMPTS` - Before account lockout (default: 5)
|
|
- `ACCOUNT_LOCKOUT_MINUTES` - Lockout duration (default: 30)
|
|
- `ENABLE_EMAIL_VERIFICATION` - Require email verification (default: false)
|
|
- `SESSION_TIMEOUT_MINUTES` - Inactive session timeout (default: 480)
|
|
|
|
**GDPR Configuration:**
|
|
- `ENABLE_GDPR_FEATURES` - Enable GDPR compliance (default: true)
|
|
- `DATA_RETENTION_DAYS` - Days to keep deleted user data (default: 30)
|
|
- `REQUIRE_CONSENT_ON_REGISTER` - Require consent (default: true)
|
|
|
|
## Development Setup
|
|
|
|
### Prerequisites
|
|
- Python 3.11+
|
|
- PostgreSQL 17
|
|
- RabbitMQ 4.1 (optional)
|
|
|
|
### Local Development
|
|
```bash
|
|
cd services/auth
|
|
python -m venv venv
|
|
source venv/bin/activate
|
|
|
|
pip install -r requirements.txt
|
|
|
|
export DATABASE_URL=postgresql://user:pass@localhost:5432/auth
|
|
export RABBITMQ_URL=amqp://guest:guest@localhost:5672/
|
|
export JWT_SECRET_KEY=your-secret-key-here
|
|
|
|
alembic upgrade head
|
|
python main.py
|
|
```
|
|
|
|
### Testing
|
|
```bash
|
|
# Unit tests
|
|
pytest tests/unit/ -v
|
|
|
|
# Integration tests
|
|
pytest tests/integration/ -v
|
|
|
|
# Security tests
|
|
pytest tests/security/ -v
|
|
|
|
# Test with coverage
|
|
pytest --cov=app tests/ --cov-report=html
|
|
```
|
|
|
|
## Integration Points
|
|
|
|
### Dependencies
|
|
- **PostgreSQL** - User and auth data storage
|
|
- **RabbitMQ** - Event publishing
|
|
- **Email Service** (planned) - Password reset emails
|
|
|
|
### Dependents
|
|
- **API Gateway** - Token validation for all requests
|
|
- **Tenant Service** - User-tenant relationships
|
|
- **All Services** - User identification from JWT
|
|
- **Frontend Dashboard** - User authentication
|
|
|
|
## JWT Subscription Implementation
|
|
|
|
### SubscriptionFetcher Class
|
|
```python
|
|
class SubscriptionFetcher:
|
|
def __init__(self, tenant_service_url: str):
|
|
self.tenant_service_url = tenant_service_url.rstrip('/')
|
|
|
|
async def get_user_subscription_context(
|
|
self, user_id: str, service_token: str
|
|
) -> Dict[str, Any]:
|
|
"""
|
|
Fetch user's tenant memberships and subscription data.
|
|
Called ONCE at login, not per-request.
|
|
|
|
Returns subscription context including:
|
|
- tenant_id: primary tenant UUID
|
|
- tenant_role: user's role in primary tenant
|
|
- subscription: {tier, status, valid_until}
|
|
- tenant_access: list of all accessible tenants with roles and tiers
|
|
"""
|
|
```
|
|
|
|
### Enhanced JWT Creation
|
|
```python
|
|
@staticmethod
|
|
def create_access_token(user_data: Dict[str, Any]) -> str:
|
|
"""
|
|
Create JWT ACCESS token with subscription data embedded
|
|
"""
|
|
payload = {
|
|
"sub": user_data["user_id"],
|
|
"user_id": user_data["user_id"],
|
|
"email": user_data["email"],
|
|
"tenant_id": user_data.get("tenant_id"),
|
|
"tenant_role": user_data.get("tenant_role"),
|
|
"subscription": user_data.get("subscription"),
|
|
"tenant_access": user_data.get("tenant_access"),
|
|
"role": user_data.get("role", "user"),
|
|
"type": "access",
|
|
"exp": datetime.now(timezone.utc) + timedelta(minutes=15),
|
|
"iat": datetime.now(timezone.utc),
|
|
"iss": "bakery-auth"
|
|
}
|
|
|
|
# Limit tenant_access to 10 entries to prevent JWT size explosion
|
|
if payload.get("tenant_access") and len(payload["tenant_access"]) > 10:
|
|
payload["tenant_access"] = payload["tenant_access"][:10]
|
|
|
|
return jwt_handler.create_access_token_from_payload(payload)
|
|
```
|
|
|
|
### Login Flow with Subscription Embedding
|
|
```python
|
|
async def login_user(email: str, password: str) -> Dict[str, Any]:
|
|
# 1. Authenticate user
|
|
user = await authenticate_user(email, password)
|
|
|
|
# 2. Fetch subscription data (ONCE at login)
|
|
subscription_fetcher = SubscriptionFetcher(tenant_service_url)
|
|
subscription_context = await subscription_fetcher.get_user_subscription_context(
|
|
user_id=str(user.id),
|
|
service_token=service_token
|
|
)
|
|
|
|
# 3. Create access token with subscription data
|
|
access_token_data = {
|
|
"user_id": str(user.id),
|
|
"email": user.email,
|
|
"role": user.role,
|
|
"tenant_id": subscription_context.get("tenant_id"),
|
|
"tenant_role": subscription_context.get("tenant_role"),
|
|
"subscription": subscription_context.get("subscription"),
|
|
"tenant_access": subscription_context.get("tenant_access")
|
|
}
|
|
|
|
access_token = SecurityManager.create_access_token(access_token_data)
|
|
|
|
# 4. Return tokens to client
|
|
return {
|
|
"access_token": access_token,
|
|
"refresh_token": refresh_token,
|
|
"token_type": "bearer"
|
|
}
|
|
```
|
|
|
|
## Security Implementation
|
|
|
|
### Password Hashing
|
|
```python
|
|
import bcrypt
|
|
|
|
def hash_password(password: str) -> str:
|
|
"""Hash password using bcrypt"""
|
|
salt = bcrypt.gensalt(rounds=12)
|
|
return bcrypt.hashpw(password.encode(), salt).decode()
|
|
|
|
def verify_password(password: str, password_hash: str) -> bool:
|
|
"""Verify password against hash"""
|
|
return bcrypt.checkpw(password.encode(), password_hash.encode())
|
|
```
|
|
|
|
### JWT Token Generation
|
|
```python
|
|
from jose import jwt
|
|
from datetime import datetime, timedelta
|
|
|
|
def create_access_token(user_id: str, email: str) -> str:
|
|
"""Create JWT access token"""
|
|
expires = datetime.utcnow() + timedelta(minutes=15)
|
|
payload = {
|
|
"sub": user_id,
|
|
"email": email,
|
|
"type": "access",
|
|
"exp": expires
|
|
}
|
|
return jwt.encode(payload, JWT_SECRET_KEY, algorithm=JWT_ALGORITHM)
|
|
|
|
def create_refresh_token(user_id: str) -> str:
|
|
"""Create JWT refresh token"""
|
|
expires = datetime.utcnow() + timedelta(days=30)
|
|
payload = {
|
|
"sub": user_id,
|
|
"type": "refresh",
|
|
"exp": expires
|
|
}
|
|
return jwt.encode(payload, JWT_SECRET_KEY, algorithm=JWT_ALGORITHM)
|
|
```
|
|
|
|
### Brute Force Protection
|
|
```python
|
|
async def check_login_attempts(email: str) -> bool:
|
|
"""Check if account is locked due to failed attempts"""
|
|
|
|
recent_attempts = await db.query(LoginAttempt).filter(
|
|
LoginAttempt.email == email,
|
|
LoginAttempt.success == False,
|
|
LoginAttempt.attempted_at > datetime.utcnow() - timedelta(minutes=30)
|
|
).count()
|
|
|
|
if recent_attempts >= MAX_LOGIN_ATTEMPTS:
|
|
# Lock account
|
|
user = await get_user_by_email(email)
|
|
user.locked_until = datetime.utcnow() + timedelta(minutes=30)
|
|
await db.commit()
|
|
return False
|
|
|
|
return True
|
|
```
|
|
|
|
## GDPR Compliance Implementation
|
|
|
|
### Data Export
|
|
```python
|
|
async def export_user_data(user_id: str) -> dict:
|
|
"""Export all user data (GDPR Article 20)"""
|
|
|
|
user = await get_user(user_id)
|
|
consents = await get_user_consents(user_id)
|
|
login_history = await get_login_attempts(user.email)
|
|
audit_logs = await get_audit_logs(user_id)
|
|
|
|
return {
|
|
"user_profile": {
|
|
"email": user.email,
|
|
"name": f"{user.first_name} {user.last_name}",
|
|
"phone": user.phone,
|
|
"created_at": user.created_at.isoformat(),
|
|
},
|
|
"consents": [
|
|
{
|
|
"type": c.consent_type,
|
|
"consented": c.consented,
|
|
"date": c.consented_at.isoformat()
|
|
} for c in consents
|
|
],
|
|
"login_history": [
|
|
{
|
|
"date": attempt.attempted_at.isoformat(),
|
|
"ip": attempt.ip_address,
|
|
"success": attempt.success
|
|
} for attempt in login_history[-100:] # Last 100
|
|
],
|
|
"audit_trail": [
|
|
{
|
|
"action": log.action,
|
|
"date": log.created_at.isoformat(),
|
|
"ip": log.ip_address
|
|
} for log in audit_logs
|
|
]
|
|
}
|
|
```
|
|
|
|
### Account Deletion
|
|
```python
|
|
async def delete_user_account(user_id: str, reason: str) -> None:
|
|
"""Delete user account (GDPR Article 17 - Right to Erasure)"""
|
|
|
|
user = await get_user(user_id)
|
|
|
|
# Anonymize user data (soft delete)
|
|
user.email = f"deleted_{user_id}@deleted.local"
|
|
user.password_hash = "DELETED"
|
|
user.first_name = "Deleted"
|
|
user.last_name = "User"
|
|
user.phone = None
|
|
user.is_active = False
|
|
user.deleted_at = datetime.utcnow()
|
|
user.deletion_reason = reason
|
|
|
|
# Revoke all tokens
|
|
await revoke_all_user_tokens(user_id)
|
|
|
|
# Keep audit logs for legal retention (30 days)
|
|
# Actual deletion happens after retention period
|
|
|
|
await db.commit()
|
|
|
|
# Publish deletion event
|
|
await publish_event("user_deleted", {"user_id": user_id})
|
|
```
|
|
|
|
## Security Measures
|
|
|
|
### Token Security
|
|
- Short-lived access tokens (15 min)
|
|
- Refresh token rotation
|
|
- Token revocation on logout
|
|
- Secure token storage (httpOnly cookies recommended for web)
|
|
|
|
### Password Security
|
|
- bcrypt hashing (work factor 12)
|
|
- Password strength requirements
|
|
- Password history (prevent reuse)
|
|
- Secure password reset flow
|
|
|
|
### Attack Prevention
|
|
- Brute force protection (5 attempts → 30 min lockout)
|
|
- Rate limiting (via API Gateway)
|
|
- SQL injection prevention (parameterized queries)
|
|
- XSS prevention (input validation)
|
|
- CSRF protection (token-based)
|
|
|
|
## Troubleshooting
|
|
|
|
### Common Issues
|
|
|
|
**Issue**: Login fails with "Account locked"
|
|
- **Cause**: Too many failed login attempts
|
|
- **Solution**: Wait 30 minutes or contact admin to unlock
|
|
|
|
**Issue**: Token refresh fails
|
|
- **Cause**: Refresh token expired or revoked
|
|
- **Solution**: Re-login to get new tokens
|
|
|
|
**Issue**: Password reset email not received
|
|
- **Cause**: Email service not configured
|
|
- **Solution**: Check SMTP settings or use admin password reset
|
|
|
|
**Issue**: GDPR export takes too long
|
|
- **Cause**: Large amount of user data
|
|
- **Solution**: Implement background job processing
|
|
|
|
## Competitive Advantages
|
|
|
|
1. **GDPR Built-In** - Full compliance out-of-the-box
|
|
2. **Enterprise Security** - Industry-standard JWT + bcrypt
|
|
3. **Audit Trail** - Complete authentication history
|
|
4. **Multi-Tenant Ready** - Isolated user authentication
|
|
5. **Scalable** - Handle thousands of concurrent users
|
|
6. **Event-Driven** - Integration-ready with RabbitMQ
|
|
7. **EU Compliant** - Designed for Spanish/EU market
|
|
8. **Performance Optimized** - JWT subscription embedding eliminates 520ms overhead per request
|
|
9. **Cost Efficient** - 100% reduction in tenant-service subscription validation calls
|
|
10. **Real-Time Subscription Updates** - Token refresh propagates changes within 15-30 minutes
|
|
|
|
## Future Enhancements
|
|
|
|
- **Multi-Factor Authentication (MFA)** - TOTP, SMS, email
|
|
- **Social Login** - Google, Facebook, Apple authentication
|
|
- **Biometric Auth** - Fingerprint, Face ID
|
|
- **OAuth2/OpenID Connect** - Standards-based SSO
|
|
- **Passwordless Auth** - Magic links, WebAuthn
|
|
- **Session Management UI** - View and revoke active sessions
|
|
- **Advanced Audit** - ML-based anomaly detection
|
|
|
|
---
|
|
|
|
**For VUE Madrid Business Plan**: The Auth Service demonstrates enterprise-grade security and full GDPR compliance, critical for EU operations. The built-in audit logging and data protection features prevent costly fines (up to €20M for GDPR violations) and provide peace of mind for bakery owners. This is a key differentiator vs competitors who lack proper data protection.
|