Fix UDM-SE authentication error by skipping meta.rc check for auth endpoint

The authentication endpoint /api/auth/login returns HTTP 200 with a different
JSON response format than data endpoints. UDM-SE (UniFi OS) does not include
the meta.rc field in authentication responses, causing false authentication
failures.

Changes:
- Added skip_meta_check parameter to _request_json() method
- Updated _login() to skip meta.rc validation for auth endpoint
- Added debug logging to show actual API responses for troubleshooting

Fixes authentication error: "UniFi API error: Unknown error"

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Mike Geppert 2026-01-04 06:12:39 -06:00
parent d42fd83d5d
commit c157595ee3

View File

@ -47,24 +47,25 @@ class UnifiClient:
# Login to get session token # Login to get session token
self._login() self._login()
def _request_json(self, endpoint: str, method: str = 'GET', def _request_json(self, endpoint: str, method: str = 'GET',
data: Optional[dict] = None) -> dict: data: Optional[dict] = None, skip_meta_check: bool = False) -> dict:
""" """
Make a request to the UniFi API and return JSON response. Make a request to the UniFi API and return JSON response.
Args: Args:
endpoint: API endpoint path endpoint: API endpoint path
method: HTTP method (GET, POST, etc.) method: HTTP method (GET, POST, etc.)
data: Optional data for POST requests data: Optional data for POST requests
skip_meta_check: Skip meta.rc validation (for auth endpoints)
Returns: Returns:
dict: JSON response dict: JSON response
Raises: Raises:
UniFiDataError: If request fails or returns invalid data UniFiDataError: If request fails or returns invalid data
""" """
url = f"{self.base_url}{endpoint}" url = f"{self.base_url}{endpoint}"
try: try:
if method == 'GET': if method == 'GET':
response = self.session.get(url, verify=self.verify_ssl, timeout=30) response = self.session.get(url, verify=self.verify_ssl, timeout=30)
@ -72,22 +73,25 @@ class UnifiClient:
response = self.session.post(url, json=data, verify=self.verify_ssl, timeout=30) response = self.session.post(url, json=data, verify=self.verify_ssl, timeout=30)
else: else:
raise ValueError(f"Unsupported HTTP method: {method}") raise ValueError(f"Unsupported HTTP method: {method}")
response.raise_for_status() response.raise_for_status()
try: try:
json_response = response.json() json_response = response.json()
except ValueError: except ValueError:
raise UniFiDataError(f"Invalid JSON response from {endpoint}") raise UniFiDataError(f"Invalid JSON response from {endpoint}")
# Check for UniFi API error response self.logger.debug(f"Response from {endpoint}: {json_response}")
if isinstance(json_response, dict):
# Check for UniFi API error response (skip for authentication endpoints)
if not skip_meta_check and isinstance(json_response, dict):
if json_response.get('meta', {}).get('rc') != 'ok': if json_response.get('meta', {}).get('rc') != 'ok':
error_msg = json_response.get('meta', {}).get('msg', 'Unknown error') error_msg = json_response.get('meta', {}).get('msg', 'Unknown error')
self.logger.debug(f"Meta check failed. Response: {json_response}")
raise UniFiDataError(f"UniFi API error: {error_msg}") raise UniFiDataError(f"UniFi API error: {error_msg}")
return json_response return json_response
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
self.logger.error(f"Request to {endpoint} failed: {e}") self.logger.error(f"Request to {endpoint} failed: {e}")
raise UniFiDataError(f"Request failed: {e}") raise UniFiDataError(f"Request failed: {e}")
@ -95,7 +99,7 @@ class UnifiClient:
def _login(self): def _login(self):
""" """
Authenticate with the UniFi controller. Authenticate with the UniFi controller.
Raises: Raises:
AuthenticationError: If authentication fails AuthenticationError: If authentication fails
""" """
@ -103,11 +107,14 @@ class UnifiClient:
'username': self.username, 'username': self.username,
'password': self.password 'password': self.password
} }
try: try:
response = self._request_json('/api/auth/login', method='POST', data=login_data) # Skip meta.rc check for auth endpoint - UDM-SE uses different response format
self.logger.debug("Successfully authenticated with UniFi controller") response = self._request_json('/api/auth/login', method='POST',
data=login_data, skip_meta_check=True)
self.logger.debug(f"Successfully authenticated with UniFi controller")
self.logger.debug(f"Authentication response: {response}")
except UniFiDataError as e: except UniFiDataError as e:
self.logger.error(f"Authentication failed: {e}") self.logger.error(f"Authentication failed: {e}")
raise AuthenticationError(f"Failed to authenticate: {e}") raise AuthenticationError(f"Failed to authenticate: {e}")