Files
bakery-ia/services/auth/app/schemas/auth.py

208 lines
6.4 KiB
Python
Raw Normal View History

2025-07-22 13:46:05 +02:00
# services/auth/app/schemas/auth.py - UPDATED WITH UNIFIED TOKEN RESPONSE
"""
2025-07-22 13:46:05 +02:00
Authentication schemas - Updated with unified token response format
Following industry best practices from Firebase, Cognito, etc.
"""
2025-07-22 13:46:05 +02:00
from pydantic import BaseModel, EmailStr, Field
from typing import Optional, Dict, Any
from datetime import datetime
2025-07-20 08:33:23 +02:00
# ================================================================
# REQUEST SCHEMAS
# ================================================================
class UserRegistration(BaseModel):
2025-07-22 13:46:05 +02:00
"""User registration request"""
email: EmailStr
2025-07-22 13:46:05 +02:00
password: str = Field(..., min_length=8, max_length=128)
full_name: str = Field(..., min_length=1, max_length=255)
tenant_name: Optional[str] = Field(None, max_length=255)
role: Optional[str] = Field("admin", pattern=r'^(user|admin|manager|super_admin)$')
subscription_plan: Optional[str] = Field("starter", description="Selected subscription plan (starter, professional, enterprise)")
use_trial: Optional[bool] = Field(False, description="Whether to use trial period")
payment_method_id: Optional[str] = Field(None, description="Stripe payment method ID")
class UserLogin(BaseModel):
2025-07-22 13:46:05 +02:00
"""User login request"""
email: EmailStr
password: str
2025-07-20 08:33:23 +02:00
class RefreshTokenRequest(BaseModel):
2025-07-22 13:46:05 +02:00
"""Refresh token request"""
2025-07-20 08:33:23 +02:00
refresh_token: str
class PasswordChange(BaseModel):
2025-07-22 13:46:05 +02:00
"""Password change request"""
2025-07-20 08:33:23 +02:00
current_password: str
2025-07-22 13:46:05 +02:00
new_password: str = Field(..., min_length=8, max_length=128)
2025-07-20 08:33:23 +02:00
class PasswordReset(BaseModel):
2025-07-22 13:46:05 +02:00
"""Password reset request"""
2025-07-20 08:33:23 +02:00
email: EmailStr
class PasswordResetConfirm(BaseModel):
2025-07-22 13:46:05 +02:00
"""Password reset confirmation"""
2025-07-20 08:33:23 +02:00
token: str
2025-07-22 13:46:05 +02:00
new_password: str = Field(..., min_length=8, max_length=128)
2025-07-20 08:33:23 +02:00
# ================================================================
# RESPONSE SCHEMAS
# ================================================================
2025-07-22 13:46:05 +02:00
class UserData(BaseModel):
"""User data embedded in token responses"""
id: str
email: str
full_name: str
2025-07-20 08:33:23 +02:00
is_active: bool
2025-07-22 13:46:05 +02:00
is_verified: bool
created_at: str # ISO format datetime string
tenant_id: Optional[str] = None
role: Optional[str] = "admin"
2025-07-20 08:33:23 +02:00
class TokenResponse(BaseModel):
2025-07-22 13:46:05 +02:00
"""
Unified token response for both registration and login
Follows industry standards (Firebase, AWS Cognito, etc.)
"""
access_token: str
2025-07-22 13:46:05 +02:00
refresh_token: Optional[str] = None
token_type: str = "bearer"
2025-07-22 13:46:05 +02:00
expires_in: int = 3600 # seconds
user: Optional[UserData] = None
class Config:
schema_extra = {
"example": {
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "def502004b8b7f8f...",
"token_type": "bearer",
"expires_in": 3600,
"user": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"email": "user@example.com",
"full_name": "John Doe",
"is_active": True,
"is_verified": False,
"created_at": "2025-07-22T10:00:00Z",
"role": "user"
}
}
}
class UserResponse(BaseModel):
2025-07-26 20:24:21 +02:00
"""User response for user management endpoints - FIXED"""
id: str
email: str
full_name: str
is_active: bool
is_verified: bool
2025-07-26 20:24:21 +02:00
created_at: datetime # ✅ Changed from str to datetime
last_login: Optional[datetime] = None # ✅ Added missing field
phone: Optional[str] = None # ✅ Added missing field
language: Optional[str] = None # ✅ Added missing field
timezone: Optional[str] = None # ✅ Added missing field
2025-07-22 13:46:05 +02:00
tenant_id: Optional[str] = None
role: Optional[str] = "admin"
2025-07-20 08:33:23 +02:00
2025-07-26 20:24:21 +02:00
class Config:
from_attributes = True # ✅ Enable ORM mode for SQLAlchemy objects
2025-08-08 09:08:41 +02:00
class UserUpdate(BaseModel):
"""User update schema"""
full_name: Optional[str] = None
phone: Optional[str] = None
language: Optional[str] = None
timezone: Optional[str] = None
class Config:
from_attributes = True
2025-07-20 08:33:23 +02:00
class TokenVerification(BaseModel):
"""Token verification response"""
valid: bool
user_id: Optional[str] = None
email: Optional[str] = None
2025-07-22 13:46:05 +02:00
exp: Optional[int] = None
message: Optional[str] = None
2025-07-22 13:46:05 +02:00
class PasswordResetResponse(BaseModel):
"""Password reset response"""
message: str
reset_token: Optional[str] = None
class LogoutResponse(BaseModel):
"""Logout response"""
message: str
success: bool = True
2025-07-19 17:49:03 +02:00
2025-07-20 08:33:23 +02:00
# ================================================================
2025-07-22 13:46:05 +02:00
# ERROR SCHEMAS
2025-07-20 08:33:23 +02:00
# ================================================================
2025-07-22 13:46:05 +02:00
class ErrorDetail(BaseModel):
"""Error detail for API responses"""
message: str
code: Optional[str] = None
field: Optional[str] = None
class ErrorResponse(BaseModel):
"""Standardized error response"""
success: bool = False
error: ErrorDetail
timestamp: str
class Config:
schema_extra = {
"example": {
"success": False,
"error": {
"message": "Invalid credentials",
"code": "AUTH_001"
},
"timestamp": "2025-07-22T10:00:00Z"
}
}
2025-07-20 08:33:23 +02:00
# ================================================================
2025-07-22 13:46:05 +02:00
# VALIDATION SCHEMAS
2025-07-20 08:33:23 +02:00
# ================================================================
2025-07-22 13:46:05 +02:00
class EmailVerificationRequest(BaseModel):
"""Email verification request"""
email: EmailStr
class EmailVerificationConfirm(BaseModel):
"""Email verification confirmation"""
token: str
class ProfileUpdate(BaseModel):
"""Profile update request"""
full_name: Optional[str] = Field(None, min_length=1, max_length=255)
email: Optional[EmailStr] = None
2025-07-20 08:33:23 +02:00
# ================================================================
2025-07-22 13:46:05 +02:00
# INTERNAL SCHEMAS (for service communication)
2025-07-20 08:33:23 +02:00
# ================================================================
2025-07-22 13:46:05 +02:00
class UserContext(BaseModel):
"""User context for internal service communication"""
2025-07-19 17:49:03 +02:00
user_id: str
2025-07-22 13:46:05 +02:00
email: str
tenant_id: Optional[str] = None
roles: list[str] = ["admin"]
2025-07-22 13:46:05 +02:00
is_verified: bool = False
class TokenClaims(BaseModel):
"""JWT token claims structure"""
sub: str # subject (user_id)
email: str
full_name: str
2025-07-20 08:33:23 +02:00
user_id: str
2025-07-22 13:46:05 +02:00
is_verified: bool
tenant_id: Optional[str] = None
iat: int # issued at
exp: int # expires at
iss: str = "bakery-auth" # issuer