From 4fe678b5e1efade690cbcfd6e2a3d3abe5a5f9aa Mon Sep 17 00:00:00 2001 From: Kenneth Skovhede Date: Mon, 7 Jul 2025 15:40:32 +0200 Subject: [PATCH] Improve OAuth error handling This PR updates OAuth handling to not retry if the request has been cancelled, as no further attempts will work, but the wait delay would still be applied. Now the cancellation triggers immediately, even if we are in the wait stage. If the server returns an error message, that message is now propagated to the client so the user can see what the problem might be. --- .../Backend/OAuthHelper/OAuthHelperHttpClient.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Duplicati/Library/Backend/OAuthHelper/OAuthHelperHttpClient.cs b/Duplicati/Library/Backend/OAuthHelper/OAuthHelperHttpClient.cs index 5624aa2e3..1a261f246 100644 --- a/Duplicati/Library/Backend/OAuthHelper/OAuthHelperHttpClient.cs +++ b/Duplicati/Library/Backend/OAuthHelper/OAuthHelperHttpClient.cs @@ -76,7 +76,7 @@ public class OAuthHelperHttpClient : JsonWebHelperHttpClient client.Timeout = Timeout.InfiniteTimeSpan; return client; } - + public OAuthHelperHttpClient(string authid, string servicename, string oauthurl, HttpClient httpClient = null, string useragent = null) : base(httpClient ?? CreateHttpClientWithInfiniteTimeout()) { @@ -115,6 +115,9 @@ public class OAuthHelperHttpClient : JsonWebHelperHttpClient HttpResponseMessage response = null; try { + if (cancellationToken.IsCancellationRequested) + throw new OperationCanceledException("Operation was cancelled", cancellationToken); + using var request = await CreateRequestAsync(_OAuthUrl, HttpMethod.Get, false, cancellationToken).ConfigureAwait(false); return await Utility.Utility.WithTimeout(AUTHENTICATION_TIMEOUT, cancellationToken, async ct => @@ -133,6 +136,9 @@ public class OAuthHelperHttpClient : JsonWebHelperHttpClient } catch (Exception ex) { + if (cancellationToken.IsCancellationRequested) + throw new OperationCanceledException("Operation was cancelled", ex, cancellationToken); + var clientError = false; try @@ -149,9 +155,10 @@ public class OAuthHelperHttpClient : JsonWebHelperHttpClient // ignored } + string msg = null; if (response != null && response.Headers.Contains("X-Reason")) { - var msg = response.Headers.GetValues("X-Reason").FirstOrDefault(); + msg = response.Headers.GetValues("X-Reason").FirstOrDefault(); if (string.IsNullOrWhiteSpace(msg)) msg = response.StatusCode.ToString(); @@ -174,10 +181,12 @@ public class OAuthHelperHttpClient : JsonWebHelperHttpClient if (retries >= (clientError ? 1 : MAX_AUTHORIZATION_RETRIES)) { await AttemptParseAndThrowExceptionAsync(ex, response, cancellationToken).ConfigureAwait(false); + if (!string.IsNullOrWhiteSpace(msg)) + throw new Interface.UserInformationException(Strings.OAuthHelper.AuthorizationFailure(msg, OAuthLoginUrl), "OAuthLoginError", ex); throw; } - Thread.Sleep(TimeSpan.FromSeconds(Math.Pow(2, retries))); + await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, retries)), cancellationToken).ConfigureAwait(false); retries++; } finally