Improve auth process 2

This commit is contained in:
Urtzi Alfaro
2025-07-20 08:33:23 +02:00
parent 8486d1db7c
commit 305f31223e
3 changed files with 360 additions and 97 deletions

View File

@@ -1,48 +1,109 @@
# services/auth/app/schemas/auth.py
# ================================================================
# services/auth/app/schemas/auth.py - COMPLETE SCHEMAS
# ================================================================
"""
Authentication schemas
Pydantic schemas for authentication service
"""
from pydantic import BaseModel, EmailStr, Field, validator
from typing import Optional
from pydantic import BaseModel, EmailStr, validator
from typing import Optional, List, Dict, Any
from datetime import datetime
import re
# ================================================================
# REQUEST SCHEMAS
# ================================================================
class UserRegistration(BaseModel):
"""User registration schema"""
email: EmailStr
password: str = Field(..., min_length=8)
full_name: str = Field(..., min_length=2, max_length=100)
phone: Optional[str] = None
language: str = Field(default="es", pattern="^(es|en)$")
password: str
full_name: str
@validator('password')
def validate_password(cls, v):
"""Basic password validation"""
if len(v) < 8:
raise ValueError('Password must be at least 8 characters')
if not any(c.isupper() for c in v):
raise ValueError('Password must contain uppercase letter')
if not any(c.islower() for c in v):
raise ValueError('Password must contain lowercase letter')
if not any(c.isdigit() for c in v):
raise ValueError('Password must contain number')
raise ValueError('Password must be at least 8 characters long')
if not re.search(r'[A-Z]', v):
raise ValueError('Password must contain at least one uppercase letter')
if not re.search(r'[a-z]', v):
raise ValueError('Password must contain at least one lowercase letter')
if not re.search(r'\d', v):
raise ValueError('Password must contain at least one number')
return v
@validator('full_name')
def validate_full_name(cls, v):
if len(v.strip()) < 2:
raise ValueError('Full name must be at least 2 characters long')
return v.strip()
class UserLogin(BaseModel):
"""User login schema"""
email: EmailStr
password: str
class RefreshTokenRequest(BaseModel):
"""Refresh token request schema"""
refresh_token: str
class PasswordChange(BaseModel):
"""Password change schema"""
current_password: str
new_password: str
@validator('new_password')
def validate_new_password(cls, v):
if len(v) < 8:
raise ValueError('New password must be at least 8 characters long')
if not re.search(r'[A-Z]', v):
raise ValueError('New password must contain at least one uppercase letter')
if not re.search(r'[a-z]', v):
raise ValueError('New password must contain at least one lowercase letter')
if not re.search(r'\d', v):
raise ValueError('New password must contain at least one number')
return v
class PasswordReset(BaseModel):
"""Password reset request schema"""
email: EmailStr
class PasswordResetConfirm(BaseModel):
"""Password reset confirmation schema"""
token: str
new_password: str
@validator('new_password')
def validate_new_password(cls, v):
if len(v) < 8:
raise ValueError('New password must be at least 8 characters long')
if not re.search(r'[A-Z]', v):
raise ValueError('New password must contain at least one uppercase letter')
if not re.search(r'[a-z]', v):
raise ValueError('New password must contain at least one lowercase letter')
if not re.search(r'\d', v):
raise ValueError('New password must contain at least one number')
return v
# ================================================================
# RESPONSE SCHEMAS
# ================================================================
class TenantMembership(BaseModel):
"""Tenant membership information"""
tenant_id: str
tenant_name: str
role: str
is_active: bool
class TokenResponse(BaseModel):
"""Token response schema"""
access_token: str
refresh_token: str
token_type: str = "bearer"
expires_in: int
class RefreshTokenRequest(BaseModel):
"""Refresh token request schema"""
refresh_token: str
expires_in: int = 1800 # 30 minutes
user: Dict[str, Any]
tenants: List[TenantMembership] = []
class UserResponse(BaseModel):
"""User response schema"""
@@ -51,29 +112,85 @@ class UserResponse(BaseModel):
full_name: str
is_active: bool
is_verified: bool
phone: Optional[str]
language: str
created_at: datetime
last_login: Optional[datetime]
last_login: Optional[datetime] = None
class Config:
from_attributes = True
class PasswordChangeRequest(BaseModel):
"""Password change request schema"""
current_password: str
new_password: str = Field(..., min_length=8)
@validator('new_password')
def validate_new_password(cls, v):
"""Validate new password strength"""
if len(v) < 8:
raise ValueError('Password must be at least 8 characters')
return v
class TokenVerification(BaseModel):
"""Token verification response"""
valid: bool
user_id: Optional[str] = None
email: Optional[str] = None
full_name: Optional[str] = None
tenants: List[Dict[str, Any]] = []
expires_at: Optional[datetime] = None
class TokenVerificationResponse(BaseModel):
"""Token verification response for other services"""
user_id: str
class UserProfile(BaseModel):
"""Extended user profile"""
id: str
email: str
full_name: str
is_active: bool
expires_at: datetime
is_verified: bool
created_at: datetime
last_login: Optional[datetime] = None
tenants: List[TenantMembership] = []
preferences: Dict[str, Any] = {}
class Config:
from_attributes = True
# ================================================================
# UPDATE SCHEMAS
# ================================================================
class UserProfileUpdate(BaseModel):
"""User profile update schema"""
full_name: Optional[str] = None
preferences: Optional[Dict[str, Any]] = None
@validator('full_name')
def validate_full_name(cls, v):
if v is not None and len(v.strip()) < 2:
raise ValueError('Full name must be at least 2 characters long')
return v.strip() if v else v
# ================================================================
# ERROR SCHEMAS
# ================================================================
class ErrorResponse(BaseModel):
"""Error response schema"""
error: str
detail: str
status_code: int
class ValidationErrorResponse(BaseModel):
"""Validation error response schema"""
error: str = "validation_error"
detail: str
status_code: int = 422
errors: List[Dict[str, Any]] = []
# ================================================================
# INTERNAL SCHEMAS (for service-to-service communication)
# ================================================================
class UserServiceRequest(BaseModel):
"""Internal user service request"""
user_id: str
action: str
data: Optional[Dict[str, Any]] = None
class TenantAccessRequest(BaseModel):
"""Tenant access verification request"""
user_id: str
tenant_id: str
class TenantAccessResponse(BaseModel):
"""Tenant access verification response"""
has_access: bool
role: Optional[str] = None
tenant_name: Optional[str] = None