# services/auth/app/schemas/auth.py - UPDATED WITH UNIFIED TOKEN RESPONSE """ Authentication schemas - Updated with unified token response format Following industry best practices from Firebase, Cognito, etc. """ from pydantic import BaseModel, EmailStr, Field from typing import Optional, Dict, Any from datetime import datetime # ================================================================ # REQUEST SCHEMAS # ================================================================ class UserRegistration(BaseModel): """User registration request""" email: EmailStr 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("user", pattern=r'^(user|admin|manager)$') class UserLogin(BaseModel): """User login request""" email: EmailStr password: str class RefreshTokenRequest(BaseModel): """Refresh token request""" refresh_token: str class PasswordChange(BaseModel): """Password change request""" current_password: str new_password: str = Field(..., min_length=8, max_length=128) class PasswordReset(BaseModel): """Password reset request""" email: EmailStr class PasswordResetConfirm(BaseModel): """Password reset confirmation""" token: str new_password: str = Field(..., min_length=8, max_length=128) # ================================================================ # RESPONSE SCHEMAS # ================================================================ class UserData(BaseModel): """User data embedded in token responses""" id: str email: str full_name: str is_active: bool is_verified: bool created_at: str # ISO format datetime string tenant_id: Optional[str] = None role: Optional[str] = "user" class TokenResponse(BaseModel): """ Unified token response for both registration and login Follows industry standards (Firebase, AWS Cognito, etc.) """ access_token: str refresh_token: Optional[str] = None token_type: str = "bearer" 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): """User response for user management endpoints - FIXED""" id: str email: str full_name: str is_active: bool is_verified: bool 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 tenant_id: Optional[str] = None role: Optional[str] = "user" class Config: from_attributes = True # ✅ Enable ORM mode for SQLAlchemy objects 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 class TokenVerification(BaseModel): """Token verification response""" valid: bool user_id: Optional[str] = None email: Optional[str] = None exp: Optional[int] = None message: Optional[str] = None class PasswordResetResponse(BaseModel): """Password reset response""" message: str reset_token: Optional[str] = None class LogoutResponse(BaseModel): """Logout response""" message: str success: bool = True # ================================================================ # ERROR SCHEMAS # ================================================================ 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" } } # ================================================================ # VALIDATION SCHEMAS # ================================================================ 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 # ================================================================ # INTERNAL SCHEMAS (for service communication) # ================================================================ class UserContext(BaseModel): """User context for internal service communication""" user_id: str email: str tenant_id: Optional[str] = None roles: list[str] = ["user"] is_verified: bool = False class TokenClaims(BaseModel): """JWT token claims structure""" sub: str # subject (user_id) email: str full_name: str user_id: str is_verified: bool tenant_id: Optional[str] = None iat: int # issued at exp: int # expires at iss: str = "bakery-auth" # issuer