@@ -81,17 +81,32 @@ async def _async_test_connection(
8181 try :
8282 session = async_get_clientsession (self .hass , verify_ssl = verify_ssl )
8383
84- _LOGGER .debug ("Testing ntopng connection: %s" , info_url )
84+ _LOGGER .debug (
85+ "Testing ntopng connection: %s (user: %s)" , info_url , username
86+ )
8587
86- # Test basic connectivity
88+ # Test basic connectivity (don't follow redirects - detect auth failures)
8789 async with session .get (
8890 info_url ,
8991 auth = auth ,
9092 ssl = verify_ssl if verify_ssl else False ,
9193 timeout = aiohttp .ClientTimeout (total = 15 ),
94+ allow_redirects = False ,
9295 ) as response :
9396 _LOGGER .debug ("ntopng info response status: %s" , response .status )
9497
98+ # 302 redirect to login page means auth failed
99+ if response .status == 302 :
100+ location = response .headers .get ("Location" , "" )
101+ if "login" in location .lower ():
102+ return (
103+ False ,
104+ "Authentication failed - redirected to login page. "
105+ "Check username and password." ,
106+ [],
107+ )
108+ return False , f"Unexpected redirect to { location } " , []
109+
95110 if response .status == 401 :
96111 return False , "Invalid username or password (401 Unauthorized)" , []
97112
@@ -155,7 +170,7 @@ async def async_step_user(
155170 ) -> ConfigFlowResult :
156171 """Handle the initial step - server configuration."""
157172 errors : dict [str , str ] = {}
158- description_placeholders : dict [str , str ] = {}
173+ description_placeholders : dict [str , str ] = {"error_detail" : "" }
159174
160175 if user_input is not None :
161176 url = user_input .get (CONF_URL , "" ).strip ()
@@ -303,7 +318,7 @@ async def async_step_reconfigure(
303318 return self .async_abort (reason = "reconfigure_failed" )
304319
305320 errors : dict [str , str ] = {}
306- description_placeholders : dict [str , str ] = {}
321+ description_placeholders : dict [str , str ] = {"error_detail" : "" }
307322
308323 if user_input is not None :
309324 url = user_input .get (CONF_URL , "" ).strip ()
0 commit comments