From eee92bbeeb1ef657f7dc0212fd9ce85bb957bd8d Mon Sep 17 00:00:00 2001 From: Shreyas Date: Mon, 10 Nov 2025 23:10:43 +0530 Subject: [PATCH] fix(desktop): token validation and cookie parsing (#5569) This fixes desktop app auth failures where users encounter "Session expired" errors when creating environments or saving requests despite being logged in. The issue occurred because token verify/validation works on web (cookie-based auth) but fails on desktop (bearer token auth). The desktop implementation had flaky response parsing in `verifyAuthTokens()`. Includes some future proofing work around cookie parsing in `setAuthCookies()`, for Set-Cookie headers contain commas or are concatenated with newlines (see #5394). --- .../hoppscotch-common/src/components.d.ts | 4 --- .../src/platform/auth/desktop/index.ts | 30 +++++++++---------- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/packages/hoppscotch-common/src/components.d.ts b/packages/hoppscotch-common/src/components.d.ts index 5e2b21ea..b7460cbb 100644 --- a/packages/hoppscotch-common/src/components.d.ts +++ b/packages/hoppscotch-common/src/components.d.ts @@ -143,8 +143,6 @@ declare module 'vue' { HoppSmartProgressRing: typeof import('@hoppscotch/ui')['HoppSmartProgressRing'] HoppSmartRadio: typeof import('@hoppscotch/ui')['HoppSmartRadio'] HoppSmartRadioGroup: typeof import('@hoppscotch/ui')['HoppSmartRadioGroup'] - HoppSmartSelect: typeof import('@hoppscotch/ui')['HoppSmartSelect'] - HoppSmartSelectOption: typeof import('@hoppscotch/ui')['HoppSmartSelectOption'] HoppSmartSelectWrapper: typeof import('@hoppscotch/ui')['HoppSmartSelectWrapper'] HoppSmartSlideOver: typeof import('@hoppscotch/ui')['HoppSmartSlideOver'] HoppSmartSpinner: typeof import('@hoppscotch/ui')['HoppSmartSpinner'] @@ -214,11 +212,9 @@ declare module 'vue' { IconLucideArrowLeft: typeof import('~icons/lucide/arrow-left')['default'] IconLucideArrowUpRight: typeof import('~icons/lucide/arrow-up-right')['default'] IconLucideBrush: typeof import('~icons/lucide/brush')['default'] - IconLucideCheck: typeof import('~icons/lucide/check')['default'] IconLucideCheckCircle: typeof import('~icons/lucide/check-circle')['default'] IconLucideChevronRight: typeof import('~icons/lucide/chevron-right')['default'] IconLucideCircleCheck: typeof import('~icons/lucide/circle-check')['default'] - IconLucideCopy: typeof import('~icons/lucide/copy')['default'] IconLucideGlobe: typeof import('~icons/lucide/globe')['default'] IconLucideHelpCircle: typeof import('~icons/lucide/help-circle')['default'] IconLucideInbox: typeof import('~icons/lucide/inbox')['default'] diff --git a/packages/hoppscotch-selfhost-web/src/platform/auth/desktop/index.ts b/packages/hoppscotch-selfhost-web/src/platform/auth/desktop/index.ts index f375ad93..9a866a38 100644 --- a/packages/hoppscotch-selfhost-web/src/platform/auth/desktop/index.ts +++ b/packages/hoppscotch-selfhost-web/src/platform/auth/desktop/index.ts @@ -171,9 +171,6 @@ export async function setInitialUser() { isGettingInitialUser.value = true const res = await getInitialUserDetails() - // NOTE: This is required for further diagnosis, - // to be removed after patch confirmation. - console.info("Auth response structure:", JSON.stringify(res, null, 2)) if ("error" in res) { await setUser(null) isGettingInitialUser.value = false @@ -291,19 +288,16 @@ async function sendMagicLink(email: string) { async function setAuthCookies(headers: Headers) { const cookieHeader = headers.get("set-cookie") - const cookies = cookieHeader ? cookieHeader.split(",") : [] + if (!cookieHeader) return - const accessTokenMatch = cookies.join(",").match(/access_token=([^;]+)/) - const refreshTokenMatch = cookies.join(",").match(/refresh_token=([^;]+)/) + const accessTMatch = cookieHeader.match(/access_token=([^;,\s]+)/) + const refreshTMatch = cookieHeader.match(/refresh_token=([^;,\s]+)/) - if (accessTokenMatch) { - const accessToken = accessTokenMatch[1] - await persistenceService.setLocalConfig("access_token", accessToken) + if (accessTMatch) { + await persistenceService.setLocalConfig("access_token", accessTMatch[1]) } - - if (refreshTokenMatch) { - const refreshToken = refreshTokenMatch[1] - await persistenceService.setLocalConfig("refresh_token", refreshToken) + if (refreshTMatch) { + await persistenceService.setLocalConfig("refresh_token", refreshTMatch[1]) } } @@ -551,10 +545,14 @@ export const def: AuthPlatformDef = { }) const res = await response - if (E.isLeft(res)) { - return false + if (E.isLeft(res)) return false + + const parsed = parseBodyAsJSON<{ isValid: boolean }>(res.right.body) + if (parsed._tag === "Some" && parsed.value.isValid) { + return true } - return res.right.isValid + const refreshed = await refreshToken() + return refreshed ?? false }, }