REFACTOR API gateway fix 7
This commit is contained in:
@@ -66,39 +66,106 @@ class SecurityManager:
|
||||
|
||||
@staticmethod
|
||||
def create_access_token(user_data: Dict[str, Any], expires_delta: Optional[timedelta] = None) -> str:
|
||||
"""Create JWT access token"""
|
||||
if expires_delta:
|
||||
expire = datetime.now(timezone.utc) + expires_delta
|
||||
else:
|
||||
expire = datetime.now(timezone.utc) + timedelta(minutes=settings.JWT_ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||
"""Create JWT access token with PROPER validation"""
|
||||
|
||||
payload = {
|
||||
"sub": user_data["user_id"],
|
||||
"user_id": user_data["user_id"],
|
||||
"email": user_data["email"],
|
||||
"type": "access",
|
||||
"full_name": user_data.get("full_name", ""),
|
||||
"is_verified": user_data.get("is_verified", False),
|
||||
"exp": expire,
|
||||
"iat": datetime.now(timezone.utc)
|
||||
}
|
||||
# ✅ FIX 1: Validate required fields BEFORE token creation
|
||||
required_fields = ["user_id", "email"]
|
||||
missing_fields = [field for field in required_fields if field not in user_data]
|
||||
|
||||
return jwt_handler.create_access_token(payload)
|
||||
if missing_fields:
|
||||
error_msg = f"Missing required fields for token creation: {missing_fields}"
|
||||
logger.error(f"Token creation failed: {error_msg}")
|
||||
raise ValueError(error_msg)
|
||||
|
||||
# ✅ FIX 2: Validate that required fields are not None/empty
|
||||
if not user_data.get("user_id"):
|
||||
raise ValueError("user_id cannot be empty")
|
||||
if not user_data.get("email"):
|
||||
raise ValueError("email cannot be empty")
|
||||
|
||||
try:
|
||||
if expires_delta:
|
||||
expire = datetime.now(timezone.utc) + expires_delta
|
||||
else:
|
||||
expire = datetime.now(timezone.utc) + timedelta(minutes=settings.JWT_ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||
|
||||
# ✅ FIX 3: Build payload with SAFE access to user_data
|
||||
payload = {
|
||||
"sub": user_data["user_id"],
|
||||
"user_id": user_data["user_id"],
|
||||
"email": user_data["email"], # ✅ Guaranteed to exist now
|
||||
"type": "access",
|
||||
"full_name": user_data.get("full_name", ""), # Safe access with default
|
||||
"is_verified": user_data.get("is_verified", False), # Safe access with default
|
||||
"is_active": user_data.get("is_active", True), # Safe access with default
|
||||
"exp": expire,
|
||||
"iat": datetime.now(timezone.utc),
|
||||
"iss": "bakery-auth" # Token issuer
|
||||
}
|
||||
|
||||
logger.debug(f"Creating access token with payload keys: {list(payload.keys())}")
|
||||
|
||||
# ✅ FIX 4: Use jwt_handler with proper error handling
|
||||
token = jwt_handler.create_access_token(payload)
|
||||
logger.debug(f"Access token created successfully for user {user_data['email']}")
|
||||
return token
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Access token creation failed for {user_data.get('email', 'unknown')}: {e}")
|
||||
raise ValueError(f"Failed to create access token: {str(e)}")
|
||||
|
||||
@staticmethod
|
||||
def create_refresh_token(user_data: Dict[str, Any]) -> str:
|
||||
"""Create JWT refresh token"""
|
||||
expire = datetime.now(timezone.utc) + timedelta(days=settings.JWT_REFRESH_TOKEN_EXPIRE_DAYS)
|
||||
"""Create JWT refresh token with FLEXIBLE validation"""
|
||||
|
||||
payload = {
|
||||
"sub": user_data["user_id"],
|
||||
"user_id": user_data["user_id"],
|
||||
"type": "refresh",
|
||||
"exp": expire,
|
||||
"iat": datetime.now(timezone.utc)
|
||||
}
|
||||
# ✅ FIX 1: Validate only essential fields for refresh token
|
||||
if "user_id" not in user_data:
|
||||
error_msg = "user_id required for refresh token creation"
|
||||
logger.error(f"Refresh token creation failed: {error_msg}")
|
||||
raise ValueError(error_msg)
|
||||
|
||||
return jwt_handler.create_access_token(payload)
|
||||
if not user_data.get("user_id"):
|
||||
raise ValueError("user_id cannot be empty")
|
||||
|
||||
try:
|
||||
expire = datetime.now(timezone.utc) + timedelta(days=settings.JWT_REFRESH_TOKEN_EXPIRE_DAYS)
|
||||
|
||||
# ✅ FIX 2: Minimal payload for refresh token (email is optional)
|
||||
payload = {
|
||||
"sub": user_data["user_id"],
|
||||
"user_id": user_data["user_id"],
|
||||
"type": "refresh",
|
||||
"exp": expire,
|
||||
"iat": datetime.now(timezone.utc),
|
||||
"iss": "bakery-auth"
|
||||
}
|
||||
|
||||
# ✅ FIX 3: Include email only if available (no longer required)
|
||||
if "email" in user_data and user_data["email"]:
|
||||
payload["email"] = user_data["email"]
|
||||
|
||||
logger.debug(f"Creating refresh token with payload keys: {list(payload.keys())}")
|
||||
|
||||
# Use the same JWT handler method (it handles both access and refresh)
|
||||
token = jwt_handler.create_access_token(payload)
|
||||
logger.debug(f"Refresh token created successfully for user {user_data['user_id']}")
|
||||
return token
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Refresh token creation failed for {user_data.get('user_id', 'unknown')}: {e}")
|
||||
raise ValueError(f"Failed to create refresh token: {str(e)}")
|
||||
|
||||
@staticmethod
|
||||
def verify_token(token: str) -> Optional[Dict[str, Any]]:
|
||||
"""Verify JWT token with enhanced error handling"""
|
||||
try:
|
||||
payload = jwt_handler.verify_token(token)
|
||||
if payload:
|
||||
logger.debug(f"Token verified successfully for user: {payload.get('email', 'unknown')}")
|
||||
return payload
|
||||
except Exception as e:
|
||||
logger.warning(f"Token verification failed: {e}")
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
async def track_login_attempt(email: str, ip_address: str, success: bool) -> None:
|
||||
|
||||
Reference in New Issue
Block a user