Skip to content

Commit 5d9ddc5

Browse files
committed
Fix session expiry causing "No WAN Connections Detected" on SQM page (#522)
Several API methods (GetDevicesRawJsonAsync, GetSiteHealthAsync, GetSettingsRawAsync, GetCurrentChannelDataAsync, GetLegacyFirewallRulesRawAsync) made direct HTTP calls without re-authentication on 401/403, unlike ExecuteApiCallAsync which handles this. When the UniFi session expired, these methods silently returned null, causing the SQM page to show "No WAN Connections Detected" and lock the user out of configuration. Added the same re-auth-and-retry pattern used by ExecuteApiCallAsync to all five methods. Fixes #516
1 parent a951a30 commit 5d9ddc5

1 file changed

Lines changed: 82 additions & 0 deletions

File tree

src/NetworkOptimizer.UniFi/UniFiApiClient.cs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,23 @@ public async Task<List<UniFiDeviceResponse>> GetDevicesAsync(CancellationToken c
605605
{
606606
var response = await _httpClient!.GetAsync(BuildApiPath("stat/device"), cancellationToken);
607607

608+
// Handle authentication failures (session expired)
609+
if (response.StatusCode == HttpStatusCode.Unauthorized ||
610+
response.StatusCode == HttpStatusCode.Forbidden)
611+
{
612+
_logger.LogWarning("Got {StatusCode} fetching raw device JSON, re-authenticating...", response.StatusCode);
613+
_isAuthenticated = false;
614+
615+
if (!await LoginAsync(cancellationToken))
616+
{
617+
_logger.LogError("Re-authentication failed while fetching raw device JSON");
618+
return null;
619+
}
620+
621+
// Retry with new authentication
622+
response = await _httpClient!.GetAsync(BuildApiPath("stat/device"), cancellationToken);
623+
}
624+
608625
if (response.IsSuccessStatusCode)
609626
{
610627
var json = await response.Content.ReadAsStringAsync(cancellationToken);
@@ -934,6 +951,23 @@ public async Task<List<UniFiFirewallRule>> GetFirewallRulesAsync(CancellationTok
934951
try
935952
{
936953
var response = await _httpClient!.GetAsync(BuildApiPath("rest/firewallrule"), cancellationToken);
954+
955+
// Handle authentication failures (session expired)
956+
if (response.StatusCode == HttpStatusCode.Unauthorized ||
957+
response.StatusCode == HttpStatusCode.Forbidden)
958+
{
959+
_logger.LogWarning("Got {StatusCode} fetching legacy firewall rules, re-authenticating...", response.StatusCode);
960+
_isAuthenticated = false;
961+
962+
if (!await LoginAsync(cancellationToken))
963+
{
964+
_logger.LogError("Re-authentication failed while fetching legacy firewall rules");
965+
return null;
966+
}
967+
968+
response = await _httpClient!.GetAsync(BuildApiPath("rest/firewallrule"), cancellationToken);
969+
}
970+
937971
response.EnsureSuccessStatusCode();
938972

939973
var json = await response.Content.ReadAsStringAsync(cancellationToken);
@@ -1253,6 +1287,22 @@ public async Task<bool> UpdateNetworkConfigAsync(
12531287
{
12541288
var response = await _httpClient!.GetAsync(BuildApiPath("stat/health"), cancellationToken);
12551289

1290+
// Handle authentication failures (session expired)
1291+
if (response.StatusCode == HttpStatusCode.Unauthorized ||
1292+
response.StatusCode == HttpStatusCode.Forbidden)
1293+
{
1294+
_logger.LogWarning("Got {StatusCode} fetching site health, re-authenticating...", response.StatusCode);
1295+
_isAuthenticated = false;
1296+
1297+
if (!await LoginAsync(cancellationToken))
1298+
{
1299+
_logger.LogError("Re-authentication failed while fetching site health");
1300+
return null;
1301+
}
1302+
1303+
response = await _httpClient!.GetAsync(BuildApiPath("stat/health"), cancellationToken);
1304+
}
1305+
12561306
if (response.IsSuccessStatusCode)
12571307
{
12581308
var json = await response.Content.ReadAsStringAsync(cancellationToken);
@@ -1432,6 +1482,22 @@ public async Task<bool> UpdateTrafficRouteAsync(
14321482
{
14331483
var response = await _httpClient!.GetAsync(BuildApiPath("rest/setting"), cancellationToken);
14341484

1485+
// Handle authentication failures (session expired)
1486+
if (response.StatusCode == HttpStatusCode.Unauthorized ||
1487+
response.StatusCode == HttpStatusCode.Forbidden)
1488+
{
1489+
_logger.LogWarning("Got {StatusCode} fetching settings, re-authenticating...", response.StatusCode);
1490+
_isAuthenticated = false;
1491+
1492+
if (!await LoginAsync(cancellationToken))
1493+
{
1494+
_logger.LogError("Re-authentication failed while fetching settings");
1495+
return null;
1496+
}
1497+
1498+
response = await _httpClient!.GetAsync(BuildApiPath("rest/setting"), cancellationToken);
1499+
}
1500+
14351501
if (response.IsSuccessStatusCode)
14361502
{
14371503
var json = await response.Content.ReadAsStringAsync(cancellationToken);
@@ -1461,6 +1527,22 @@ public async Task<bool> UpdateTrafficRouteAsync(
14611527
{
14621528
var response = await _httpClient!.GetAsync(BuildApiPath("stat/current-channel"), cancellationToken);
14631529

1530+
// Handle authentication failures (session expired)
1531+
if (response.StatusCode == HttpStatusCode.Unauthorized ||
1532+
response.StatusCode == HttpStatusCode.Forbidden)
1533+
{
1534+
_logger.LogWarning("Got {StatusCode} fetching current channel data, re-authenticating...", response.StatusCode);
1535+
_isAuthenticated = false;
1536+
1537+
if (!await LoginAsync(cancellationToken))
1538+
{
1539+
_logger.LogError("Re-authentication failed while fetching current channel data");
1540+
return null;
1541+
}
1542+
1543+
response = await _httpClient!.GetAsync(BuildApiPath("stat/current-channel"), cancellationToken);
1544+
}
1545+
14641546
if (response.IsSuccessStatusCode)
14651547
{
14661548
var json = await response.Content.ReadAsStringAsync(cancellationToken);

0 commit comments

Comments
 (0)