diff --git a/src/Controllers/AuthenticationController.cs b/src/Controllers/AuthenticationController.cs index 0009f65c..eabaa04a 100644 --- a/src/Controllers/AuthenticationController.cs +++ b/src/Controllers/AuthenticationController.cs @@ -50,22 +50,31 @@ public async Task GetEnvironmentsAsync( /// Attempts to authenticate and acquire an access token for the account to access the PowerBI cloud services /// /// Status200OK - Success + /// Status204NoContent - Sign-in was canceled by the user [HttpPost] [ActionName("SignIn")] [Produces(MediaTypeNames.Application.Json)] [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(SignInResponse))] + [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesDefaultResponseType] public async Task SignInAsync( SignInRequest request, CancellationToken cancellationToken) { - var session = await _authenticationService.SignInAsync( - request.Email, - request.Environment.ToModel(), - cancellationToken); + try + { + var session = await _authenticationService.SignInAsync( + request.Email, + request.Environment.ToModel(), + cancellationToken); - var response = new SignInResponse(session.AuthenticationResult); - return Ok(response); + var response = new SignInResponse(session.AuthenticationResult); + return Ok(response); + } + catch (OperationCanceledException) + { + return NoContent(); + } } /// diff --git a/src/Infrastructure/AppExceptions.cs b/src/Infrastructure/AppExceptions.cs index d8d6757b..ea4fbf2e 100644 --- a/src/Infrastructure/AppExceptions.cs +++ b/src/Infrastructure/AppExceptions.cs @@ -115,8 +115,8 @@ public enum BravoProblem None = 0, /// - /// A or was thrown, an HTTP request was aborted or the user cancelled a long-running operation. - /// This BravoProblem is a placehoder since the response message is never sent back to the user/UI due to the aborted request. + /// An was thrown (request aborted or user-cancelled operation). + /// Response message is not sent to the user/UI due to the aborted request. /// [JsonPropertyName("OperationCancelled")] OperationCancelled = 1, @@ -172,15 +172,9 @@ public enum BravoProblem [JsonPropertyName("SignInMsalExceptionOccurred")] SignInMsalExceptionOccurred = 400, - /// - /// Sign-in request was canceled because the configured timeout period elapsed prior to completion of the operation - /// - [JsonPropertyName("SignInMsalTimeoutExpired")] - SignInMsalTimeoutExpired = 401, - /// /// An error occurred while importing the VPAX file - /// + /// [JsonPropertyName("VpaxFileImportError")] VpaxFileImportError = 500, diff --git a/src/Infrastructure/Extensions/MsalExceptionExtensions.cs b/src/Infrastructure/Extensions/MsalExceptionExtensions.cs new file mode 100644 index 00000000..46e9a636 --- /dev/null +++ b/src/Infrastructure/Extensions/MsalExceptionExtensions.cs @@ -0,0 +1,14 @@ +namespace Sqlbi.Bravo.Infrastructure.Extensions +{ + using Microsoft.Identity.Client; + + internal static class MsalExceptionExtensions + { + /// + /// Returns if the exception represents the user canceling + /// an interactive authentication prompt (e.g. closing the sign-in window). + /// + public static bool IsAuthenticationCanceled(this MsalException exception) + => exception.ErrorCode == MsalError.AuthenticationCanceledError; + } +} diff --git a/src/Infrastructure/PowerBI/Cloud/Authentication/CloudAuthenticationClient.cs b/src/Infrastructure/PowerBI/Cloud/Authentication/CloudAuthenticationClient.cs index 0c11f2cb..385ed257 100644 --- a/src/Infrastructure/PowerBI/Cloud/Authentication/CloudAuthenticationClient.cs +++ b/src/Infrastructure/PowerBI/Cloud/Authentication/CloudAuthenticationClient.cs @@ -3,6 +3,7 @@ namespace Sqlbi.Bravo.Infrastructure.PowerBI.Cloud.Authentication using Microsoft.Identity.Client; using Microsoft.Identity.Client.Desktop; using Sqlbi.Bravo.Infrastructure.Configuration; + using Sqlbi.Bravo.Infrastructure.Extensions; using Sqlbi.Bravo.Infrastructure.Helpers; using Sqlbi.Bravo.Infrastructure.PowerBI.Cloud; using Msal = Microsoft.Identity.Client; @@ -89,13 +90,30 @@ public async Task ClearTokenCacheAsync(CloudEnvironment environment) .WithPrompt(prompt) .WithClaims(claims); + using var cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + if (useEmbeddedBrowser) { var windowHandle = ProcessHelper.GetCurrentProcessMainWindowHandle(); builder.WithParentActivityOrWindow(windowHandle); } + else // use system browser + { + // The system browser is a separate, untracked OS process: there is no way to detect the user closing + // it, so a hard ceiling is the only safeguard against an indefinitely pending sign-in. + cancellationTokenSource.CancelAfter(TimeSpan.FromMinutes(2)); + } - return await builder.ExecuteAsync(cancellationToken).ConfigureAwait(false); + try + { + return await builder.ExecuteAsync(cancellationTokenSource.Token).ConfigureAwait(false); + } + catch (MsalException ex) when (ex.IsAuthenticationCanceled()) + { + // The user canceled the sign-in prompt, either by closing the embedded browser or the system browser. + // Normalize the exception to OperationCanceledException, like the other cancellation paths + throw new OperationCanceledException("Authentication was canceled by the user.", ex); + } } private static IPublicClientApplication CreatePublicClient(CloudEnvironment environment) diff --git a/src/Infrastructure/PowerBI/Cloud/Authentication/CloudAuthenticationService.cs b/src/Infrastructure/PowerBI/Cloud/Authentication/CloudAuthenticationService.cs index a4f50684..df47508d 100644 --- a/src/Infrastructure/PowerBI/Cloud/Authentication/CloudAuthenticationService.cs +++ b/src/Infrastructure/PowerBI/Cloud/Authentication/CloudAuthenticationService.cs @@ -35,11 +35,8 @@ public async Task SignInAsync(string email, CloudEnvironme if (AppEnvironment.IsDiagnosticLevelVerbose) AppEnvironment.AddDiagnostics(DiagnosticMessageType.Json, name: $"{nameof(CloudAuthenticationService)}.{nameof(SignInAsync)}", JsonSerializer.Serialize(environment)); - using var cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); - cancellationTokenSource.CancelAfter(TimeSpan.FromMinutes(2)); - - var authenticationResult = await _cloudAuthenticationClient.AcquireTokenAsync(environment, email, cancellationTokenSource.Token); - var clusterUri = await _cloudConfigurationService.ResolveTenantClusterUriAsync(environment, authenticationResult.AccessToken, cancellationTokenSource.Token); + var authenticationResult = await _cloudAuthenticationClient.AcquireTokenAsync(environment, email, cancellationToken); + var clusterUri = await _cloudConfigurationService.ResolveTenantClusterUriAsync(environment, authenticationResult.AccessToken, cancellationToken); var newEnvironment = environment with { ClusterUri = clusterUri }; var newSession = new AuthenticatedSession(authenticationResult, newEnvironment); diff --git a/src/Scripts/controllers/host.ts b/src/Scripts/controllers/host.ts index 8b4c0e10..b4499e18 100644 --- a/src/Scripts/controllers/host.ts +++ b/src/Scripts/controllers/host.ts @@ -398,7 +398,7 @@ export class Host extends Dispatchable { const logSettings: ApiLogSettings = {}; return (>this.apiCall("auth/SignIn", request || {}, { method: "POST" }, false, logSettings)) - .then(response => response.account); + .then(response => response?.account); } /*signIn(email?: string) { diff --git a/src/Scripts/model/exceptions.ts b/src/Scripts/model/exceptions.ts index b276d97b..04792bbd 100644 --- a/src/Scripts/model/exceptions.ts +++ b/src/Scripts/model/exceptions.ts @@ -22,7 +22,6 @@ export enum AppProblem { ConnectionUnsupported = 200, UserSettingsSaveError = 300, SignInMsalExceptionOccurred = 400, - SignInMsalTimeoutExpired = 401, VpaxFileImportError = 500, VpaxFileExportError = 501, NetworkError = 600, diff --git a/src/Scripts/model/i18n/cz.ts b/src/Scripts/model/i18n/cz.ts index eedd1794..57011d96 100644 --- a/src/Scripts/model/i18n/cz.ts +++ b/src/Scripts/model/i18n/cz.ts @@ -156,7 +156,6 @@ const locale: Locale = { [_.errorReportsEmptyListing]: "Nejsou k dispozici žádné neotevřené chyby.", [_.errorRetry]: "Opakovat", [_.errorSignInMsalExceptionOccurred]: "Neočekávaná chyba v rámci přihlášení.", - [_.errorSignInMsalTimeoutExpired]: "Požadavek na přihlášení byl zrušen, protože časový limit vypršel před dokončením operace.", [_.errorTemplateAlreadyExists]: "Další šablona se stejnou cestou již existuje:
{name}", [_.errorTimeout]: "Časový limit požadavku vypršel.", [_.errorTitle]: "Whoops...", diff --git a/src/Scripts/model/i18n/da.ts b/src/Scripts/model/i18n/da.ts index 9c1d5e38..57816158 100644 --- a/src/Scripts/model/i18n/da.ts +++ b/src/Scripts/model/i18n/da.ts @@ -156,7 +156,6 @@ const locale: Locale = { [_.errorReportsEmptyListing]: "Ingen uåbnede rapporter tilgængelige.", [_.errorRetry]: "Forsøg igen", [_.errorSignInMsalExceptionOccurred]: "Ukendt fejl opstod i forbindelse med lo- ind.", - [_.errorSignInMsalTimeoutExpired]: "Log-ind blev afbrudt fordi timeout perioden udløb inden processen blev gennemført.", [_.errorTemplateAlreadyExists]: "En anden skabelon med den samme sti findes allerede:
{name}", [_.errorTimeout]: "Forespørgselstimeout.", [_.errorTitle]: "Ups...", diff --git a/src/Scripts/model/i18n/de.ts b/src/Scripts/model/i18n/de.ts index 24d93d77..7ac52ffe 100644 --- a/src/Scripts/model/i18n/de.ts +++ b/src/Scripts/model/i18n/de.ts @@ -158,7 +158,6 @@ const locale: Locale = { [_.errorReportsEmptyListing]: "Keine ungeöffneten Berichte verfügbar.", [_.errorRetry]: "Wiederholen", [_.errorSignInMsalExceptionOccurred]: "Unerwarteter Fehler in der Anmeldeanforderung.", - [_.errorSignInMsalTimeoutExpired]: "Die Anmeldeanforderung wurde abgebrochen, weil die Zeitüberschreitung abgelaufen ist, bevor der Vorgang abgeschlossen werden konnte.", [_.errorTemplateAlreadyExists]: "Eine weitere Vorlage mit dem gleichen Weg gibt es bereits:
{name}", [_.errorTimeout]: "Zeitüberschreitung der Anfrage.", [_.errorTitle]: "Ups...", diff --git a/src/Scripts/model/i18n/en.ts b/src/Scripts/model/i18n/en.ts index 7bc23acd..d9900152 100644 --- a/src/Scripts/model/i18n/en.ts +++ b/src/Scripts/model/i18n/en.ts @@ -157,7 +157,6 @@ const locale: Locale = { [_.errorReportsEmptyListing]: "No unopened reports available.", [_.errorRetry]: "Retry", [_.errorSignInMsalExceptionOccurred]: "Unexpected error in the sign-in request.", - [_.errorSignInMsalTimeoutExpired]: "The sign-in request was canceled because the timeout period expired before the operation was completed.", [_.errorTemplateAlreadyExists]: "Another template with the same path/name already exists:
{name}", [_.errorTimeout]: "Request timeout.", [_.errorTitle]: "Whoops...", diff --git a/src/Scripts/model/i18n/es.ts b/src/Scripts/model/i18n/es.ts index ed5e143d..57404afe 100644 --- a/src/Scripts/model/i18n/es.ts +++ b/src/Scripts/model/i18n/es.ts @@ -156,7 +156,6 @@ const locale: Locale = { [_.errorReportsEmptyListing]: "No hay informes sin abrir disponibles.", [_.errorRetry]: "Reintentar", [_.errorSignInMsalExceptionOccurred]: "Error inesperado en la solicitud de inicio de sesión.", - [_.errorSignInMsalTimeoutExpired]: "La solicitud de inicio de sesión se canceló porque el tiempo de espera expiró antes de que se completara la operación.", [_.errorTemplateAlreadyExists]: "¡Ya existe otra plantilla con el mismo camino:
{name}", [_.errorTimeout]: "Su solicitud ha expirado", [_.errorTitle]: "Ups…", diff --git a/src/Scripts/model/i18n/fa.ts b/src/Scripts/model/i18n/fa.ts index 9ce51e0a..32fbf7b2 100644 --- a/src/Scripts/model/i18n/fa.ts +++ b/src/Scripts/model/i18n/fa.ts @@ -160,7 +160,6 @@ const locale: Locale = { [_.errorReportsEmptyListing]: "هیچ گزارش باز نشده ای موجود نیست.", [_.errorRetry]: "تلاش مجدد", [_.errorSignInMsalExceptionOccurred]: "خطای غیرمنتظره در درخواست ورود به سیستم.", - [_.errorSignInMsalTimeoutExpired]: "درخواست ورود به سیستم لغو شد زیرا دوره وقفه قبل از تکمیل عملیات منقضی شده بود.", [_.errorTemplateAlreadyExists]: "الگوی دیگری با همان مسیر/نام از قبل وجود دارد:
{name}", [_.errorTimeout]: "زمان پایان درخواست.", [_.errorTitle]: "اوه...", diff --git a/src/Scripts/model/i18n/fr.ts b/src/Scripts/model/i18n/fr.ts index c451d72a..b28b6f07 100644 --- a/src/Scripts/model/i18n/fr.ts +++ b/src/Scripts/model/i18n/fr.ts @@ -158,7 +158,6 @@ const locale: Locale = { [_.errorReportsEmptyListing]: "Aucun rapport disponible.", [_.errorRetry]: "Réessayer", [_.errorSignInMsalExceptionOccurred]: "Erreur inattendue lors de l'authentification.", - [_.errorSignInMsalTimeoutExpired]: "La demande d'authentification a été annulée suite à l'expiration du timeout.", [_.errorTemplateAlreadyExists]: "Un autre modèle avec le même chemin existe déjà:
{name}", [_.errorTimeout]: "Timeout sur la demande.", [_.errorTitle]: "Oops...", diff --git a/src/Scripts/model/i18n/gr.ts b/src/Scripts/model/i18n/gr.ts index b592566d..91f08cbc 100644 --- a/src/Scripts/model/i18n/gr.ts +++ b/src/Scripts/model/i18n/gr.ts @@ -156,7 +156,6 @@ const locale: Locale = { [_.errorReportsEmptyListing]: "Δεν υπάρχουν διαθέσιμες ανοικτές αναφορές.", [_.errorRetry]: "Εκ νέου προσπάθεια", [_.errorSignInMsalExceptionOccurred]: "Αναπάντεχο σφάλμα κατά την προσπάθεια σύνδεσης.", - [_.errorSignInMsalTimeoutExpired]: "Η προσπάθεια σύνδεσης διακόπηκε επειδή πέρασε ο χρόνος εξαιτίας λήξης του χρόνου που έχει οριστεί για την ολοκλήρωση της(timeout).", [_.errorTemplateAlreadyExists]: "Ένα άλλο πρότυπο με το ίδιο μονοπάτι υπάρχει ήδη:
{name}", [_.errorTimeout]: "Λήξη χρόνου(timeout).", [_.errorTitle]: "Όυπςςς", diff --git a/src/Scripts/model/i18n/it.ts b/src/Scripts/model/i18n/it.ts index af213343..a2929158 100644 --- a/src/Scripts/model/i18n/it.ts +++ b/src/Scripts/model/i18n/it.ts @@ -156,7 +156,6 @@ const locale: Locale = { [_.errorReportsEmptyListing]: "Nessun altro report aperto.", [_.errorRetry]: "Riprova", [_.errorSignInMsalExceptionOccurred]: "Errore inaspettato durante l'accesso.", - [_.errorSignInMsalTimeoutExpired]: "La richiesta di accesso è stata annullata perché è passato troppo tempo prima che l'operazione venisse completata.", [_.errorTemplateAlreadyExists]: "Un altro template con lo stesso percorso/nome esiste già:
{name}", [_.errorTimeout]: "Timeout.", [_.errorTitle]: "Oops...", diff --git a/src/Scripts/model/i18n/nl.ts b/src/Scripts/model/i18n/nl.ts index f23b51ab..63d76b7e 100644 --- a/src/Scripts/model/i18n/nl.ts +++ b/src/Scripts/model/i18n/nl.ts @@ -156,7 +156,6 @@ const locale: Locale = { [_.errorReportsEmptyListing]: "Geen ongeopende rapporten beschikbaar.", [_.errorRetry]: "Opnieuw proberen", [_.errorSignInMsalExceptionOccurred]: "Onverwachte fout in het inlogverzoek.", - [_.errorSignInMsalTimeoutExpired]: "Het inlogverzoek is geannuleerd vanwege een onderbreking die optrad voordat het verzoek voltooid kon worden.", [_.errorTemplateAlreadyExists]: "Er bestaat al een ander sjabloon met hetzelfde pad:
{name}", [_.errorTimeout]: "Verzoek onderbroken.", [_.errorTitle]: "Oeps...", diff --git a/src/Scripts/model/i18n/pl.ts b/src/Scripts/model/i18n/pl.ts index ff6b7031..70b4eaff 100644 --- a/src/Scripts/model/i18n/pl.ts +++ b/src/Scripts/model/i18n/pl.ts @@ -156,7 +156,6 @@ const locale: Locale = { [_.errorReportsEmptyListing]: "Brak dostępnych, nieotwartych raportów.", [_.errorRetry]: "Spróbuj ponownie", [_.errorSignInMsalExceptionOccurred]: "Nieoczekiwany błąd w trakcie logowania.", - [_.errorSignInMsalTimeoutExpired]: "Logowanie zostało anulowane ponieważ czas opracji przekroczył dozwolony.", [_.errorTemplateAlreadyExists]: "Kolejny szablon z tą samą ścieżką już istnieje:
{name}", [_.errorTimeout]: "Limit czasu żądania.", [_.errorTitle]: "Whoops...", diff --git a/src/Scripts/model/i18n/pt.ts b/src/Scripts/model/i18n/pt.ts index 0f94294d..9197a461 100644 --- a/src/Scripts/model/i18n/pt.ts +++ b/src/Scripts/model/i18n/pt.ts @@ -156,7 +156,6 @@ const locale: Locale = { [_.errorReportsEmptyListing]: "Nenhum relatório por abrir disponível.", [_.errorRetry]: "Tente novamente", [_.errorSignInMsalExceptionOccurred]: "Erro inesperado no pedido de início de sessão.", - [_.errorSignInMsalTimeoutExpired]: "O pedido de início de sessão foi cancelado porque o tempo limite foi excedido antes da operação estar terminada.", [_.errorTemplateAlreadyExists]: "Outro modelo com o mesmo caminho já existe:
{name}", [_.errorTimeout]: "Tempo limite excedido.", [_.errorTitle]: "Opa...", diff --git a/src/Scripts/model/i18n/ru.ts b/src/Scripts/model/i18n/ru.ts index 0124415c..546adaba 100644 --- a/src/Scripts/model/i18n/ru.ts +++ b/src/Scripts/model/i18n/ru.ts @@ -156,7 +156,6 @@ const locale: Locale = { [_.errorReportsEmptyListing]: "No unopened reports available.", [_.errorRetry]: "Повторить", [_.errorSignInMsalExceptionOccurred]: "Непредвиденная ошибка в запросе на вход.", - [_.errorSignInMsalTimeoutExpired]: "Запрос на вход был отменен, так как время ожидания истекло до завершения операции", [_.errorTemplateAlreadyExists]: "Еще один шаблон с тем же путем уже существует:
{name}", [_.errorTimeout]: "Истекло время ожидания запроса", [_.errorTitle]: "Ой...", diff --git a/src/Scripts/model/i18n/tr.ts b/src/Scripts/model/i18n/tr.ts index 458b14ac..07520a88 100644 --- a/src/Scripts/model/i18n/tr.ts +++ b/src/Scripts/model/i18n/tr.ts @@ -158,7 +158,6 @@ const locale: Locale = { [_.errorReportsEmptyListing]: "Açılmamış rapor yok.", [_.errorRetry]: "Yeniden Dene", [_.errorSignInMsalExceptionOccurred]: "Oturum açma isteğinde beklenmeyen hata.", - [_.errorSignInMsalTimeoutExpired]: "İşlem tamamlanmadan önce zaman aşımı süresi sona erdiği için oturum açma isteği iptal edildi.", [_.errorTemplateAlreadyExists]: "Aynı yola sahip başka bir şablon zaten var:
{name}", [_.errorTimeout]: "İstek zaman aşımı.", [_.errorTitle]: "Hata...", diff --git a/src/Scripts/model/i18n/uk.ts b/src/Scripts/model/i18n/uk.ts index a72be4ae..1c60eb8d 100644 --- a/src/Scripts/model/i18n/uk.ts +++ b/src/Scripts/model/i18n/uk.ts @@ -157,7 +157,6 @@ const locale: Locale = { [_.errorReportsEmptyListing]: "Невідкритих звітів немає.", [_.errorRetry]: "Спробувати повторно", [_.errorSignInMsalExceptionOccurred]: "Неочікувана помилка в запиті на вхід.", - [_.errorSignInMsalTimeoutExpired]: "Запит на вхід було скасовано, оскільки час очікування закінчився до завершення операції.", [_.errorTemplateAlreadyExists]: "Інший шаблон з таким самим шляхом/назвою вже існує:
{name}", [_.errorTimeout]: "Запросити перерву", [_.errorTitle]: "Упс...", diff --git a/src/Scripts/model/i18n/zh.ts b/src/Scripts/model/i18n/zh.ts index 766bc09f..6fade7f1 100644 --- a/src/Scripts/model/i18n/zh.ts +++ b/src/Scripts/model/i18n/zh.ts @@ -156,7 +156,6 @@ const locale: Locale = { [_.errorReportsEmptyListing]: "没找到未打开的报告", [_.errorRetry]: "再试一次", [_.errorSignInMsalExceptionOccurred]: "登录请求出现异常", - [_.errorSignInMsalTimeoutExpired]: "操作超时, 登录请求被取消", [_.errorTemplateAlreadyExists]: "另一个具有相同路径的模板已经存在:
{name}", [_.errorTimeout]: "请求超时", [_.errorTitle]: "Whoops...", diff --git a/src/Scripts/model/strings.ts b/src/Scripts/model/strings.ts index d5e661da..e78f2929 100644 --- a/src/Scripts/model/strings.ts +++ b/src/Scripts/model/strings.ts @@ -149,7 +149,6 @@ export enum strings { errorReportsEmptyListing, errorRetry, errorSignInMsalExceptionOccurred, - errorSignInMsalTimeoutExpired, errorTemplateAlreadyExists, errorTimeout, errorTitle, diff --git a/src/Scripts/view/powerbi-signin.ts b/src/Scripts/view/powerbi-signin.ts index e2eac72f..2f79f774 100644 --- a/src/Scripts/view/powerbi-signin.ts +++ b/src/Scripts/view/powerbi-signin.ts @@ -146,7 +146,7 @@ export class PowerBISignin extends Dialog { onAction(action: string, resolve: any, reject: any) { if (action == "signin") { auth.signIn(this.data) - .then(() => { + .then(() => { super.onAction(action, resolve, reject); }) .catch(ignore => { @@ -154,6 +154,9 @@ export class PowerBISignin extends Dialog { this.element.classList.remove("wait"); }); } else { + if (action == "cancel" && this.element.classList.contains("wait")) + host.apiAbortByAction("auth/SignIn"); + super.onAction(action, resolve, reject); } } diff --git a/src/Services/AuthenticationService.cs b/src/Services/AuthenticationService.cs index 49d0a304..a22d7a50 100644 --- a/src/Services/AuthenticationService.cs +++ b/src/Services/AuthenticationService.cs @@ -1,6 +1,5 @@ namespace Sqlbi.Bravo.Services { - using Sqlbi.Bravo.Infrastructure; using Sqlbi.Bravo.Infrastructure.PowerBI.Cloud; using Sqlbi.Bravo.Infrastructure.PowerBI.Cloud.Authentication; using Sqlbi.Bravo.Infrastructure.PowerBI.Cloud.Configuration; @@ -30,26 +29,12 @@ public async Task> GetEnvironmentsAsync(string e public async Task EnsureSignedInAsync(CancellationToken cancellationToken) { - try - { - return await _cloudAuthenticationService.EnsureSignedInAsync(cancellationToken); - } - catch (OperationCanceledException) - { - throw new BravoException(BravoProblem.SignInMsalTimeoutExpired); - } + return await _cloudAuthenticationService.EnsureSignedInAsync(cancellationToken); } public async Task SignInAsync(string email, CloudEnvironment environment, CancellationToken cancellationToken) { - try - { - return await _cloudAuthenticationService.SignInAsync(email, environment, cancellationToken); - } - catch (OperationCanceledException) - { - throw new BravoException(BravoProblem.SignInMsalTimeoutExpired); - } + return await _cloudAuthenticationService.SignInAsync(email, environment, cancellationToken); } public async Task SignOutAsync(CancellationToken cancellationToken)