From ff05387628e8f73e2c0515e492d89169aeea6bdd Mon Sep 17 00:00:00 2001 From: Johannes Millan Date: Fri, 6 Mar 2026 11:16:14 +0100 Subject: [PATCH] fix(sync): improve error messages for auth code exchange failures Differentiate between token exchange errors (HTTP 400 = invalid auth code) and PKCE generation failures, showing appropriate messages for each. Previously all auth errors showed a misleading "requires HTTPS" message. Also log Dropbox response body on token exchange errors for better diagnostics. Closes #6746 --- src/app/imex/sync/sync-wrapper.service.ts | 10 +++++++--- .../sync-providers/file-based/dropbox/dropbox-api.ts | 10 ++++++++-- src/app/t.const.ts | 2 ++ src/assets/i18n/en.json | 2 ++ 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/app/imex/sync/sync-wrapper.service.ts b/src/app/imex/sync/sync-wrapper.service.ts index 8c45cb3c47..6571004473 100644 --- a/src/app/imex/sync/sync-wrapper.service.ts +++ b/src/app/imex/sync/sync-wrapper.service.ts @@ -17,6 +17,7 @@ import { LocalDataConflictError, WebCryptoNotAvailableError, MissingRefreshTokenAPIError, + HttpNotOkAPIError, } from '../../op-log/core/errors/sync-errors'; import { MAX_LWW_REUPLOAD_RETRIES } from '../../op-log/core/operation-log.const'; import { SyncConfig } from '../../features/config/global-config.model'; @@ -679,11 +680,14 @@ export class SyncWrapperService { } } catch (error) { SyncLog.err(`Failed to configure auth for provider ${providerId}:`, error); + const isTokenExchangeError = + error instanceof HttpNotOkAPIError && error.response?.status === 400; this._snackService.open({ - // TODO don't limit snack to dropbox - msg: T.F.DROPBOX.S.UNABLE_TO_GENERATE_PKCE_CHALLENGE, + msg: isTokenExchangeError + ? T.F.SYNC.S.INVALID_AUTH_CODE + : T.F.SYNC.S.AUTH_SETUP_FAILED, type: 'ERROR', - config: { duration: 0 }, // Stay visible until dismissed for critical setup errors + config: { duration: 0 }, }); return { wasConfigured: false }; } diff --git a/src/app/op-log/sync-providers/file-based/dropbox/dropbox-api.ts b/src/app/op-log/sync-providers/file-based/dropbox/dropbox-api.ts index fd5f295fba..b796d2e1bf 100644 --- a/src/app/op-log/sync-providers/file-based/dropbox/dropbox-api.ts +++ b/src/app/op-log/sync-providers/file-based/dropbox/dropbox-api.ts @@ -454,8 +454,10 @@ export class DropboxApi { }); if (response.status < 200 || response.status >= 300) { + const bodyStr = JSON.stringify(response.data); throw new HttpNotOkAPIError( - new Response(JSON.stringify(response.data), { status: response.status }), + new Response(bodyStr, { status: response.status }), + bodyStr, ); } @@ -471,7 +473,11 @@ export class DropboxApi { }); if (!response.ok) { - throw new HttpNotOkAPIError(response); + const bodyStr = await response.text(); + throw new HttpNotOkAPIError( + new Response(bodyStr, { status: response.status }), + bodyStr, + ); } data = (await response.json()) as TokenResponse; diff --git a/src/app/t.const.ts b/src/app/t.const.ts index 62d5aaedc7..2bd5ddc605 100644 --- a/src/app/t.const.ts +++ b/src/app/t.const.ts @@ -1288,6 +1288,7 @@ const T = { }, }, S: { + AUTH_SETUP_FAILED: 'F.SYNC.S.AUTH_SETUP_FAILED', ALREADY_IN_SYNC: 'F.SYNC.S.ALREADY_IN_SYNC', BTN_CONFIGURE: 'F.SYNC.S.BTN_CONFIGURE', BTN_FORCE_OVERWRITE: 'F.SYNC.S.BTN_FORCE_OVERWRITE', @@ -1312,6 +1313,7 @@ const T = { INCOMPLETE_OP_SYNC: 'F.SYNC.S.INCOMPLETE_OP_SYNC', TOO_MANY_OPS_TO_DOWNLOAD: 'F.SYNC.S.TOO_MANY_OPS_TO_DOWNLOAD', INITIAL_SYNC_ERROR: 'F.SYNC.S.INITIAL_SYNC_ERROR', + INVALID_AUTH_CODE: 'F.SYNC.S.INVALID_AUTH_CODE', INVALID_OPERATION_PAYLOAD: 'F.SYNC.S.INVALID_OPERATION_PAYLOAD', DECRYPTION_FAILED: 'F.SYNC.S.DECRYPTION_FAILED', ENCRYPTION_PASSWORD_REQUIRED: 'F.SYNC.S.ENCRYPTION_PASSWORD_REQUIRED', diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index e281f54243..c2ec8b1b07 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -1272,7 +1272,9 @@ "AUTH_TOKEN_REJECTED": "Sync token was rejected by the server: {{reason}}. Please get a new token.", "INCOMPLETE_OP_SYNC": "Sync incomplete: {{count}} operation file(s) failed to download", "TOO_MANY_OPS_TO_DOWNLOAD": "Too many sync operations to download. Some changes may be missing.", + "AUTH_SETUP_FAILED": "Failed to set up authentication. If you're using the web app, make sure you're accessing it over HTTPS. Otherwise, please try the stable version or report this as a bug.", "INITIAL_SYNC_ERROR": "Initial Sync failed", + "INVALID_AUTH_CODE": "The authorization code was rejected. Please try authenticating again and make sure to copy the code exactly.", "INVALID_OPERATION_PAYLOAD": "Invalid operation detected. Changes may not be saved - please reload.", "DECRYPTION_FAILED": "Failed to decrypt synced data. Please check your encryption password.", "ENCRYPTION_PASSWORD_REQUIRED": "Encrypted data received but no encryption password is configured. Please set your encryption password in sync settings.",