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).
This commit is contained in:
Shreyas 2025-11-10 23:10:43 +05:30 committed by GitHub
parent f8cb75895f
commit eee92bbeeb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 14 additions and 20 deletions

View file

@ -143,8 +143,6 @@ declare module 'vue' {
HoppSmartProgressRing: typeof import('@hoppscotch/ui')['HoppSmartProgressRing'] HoppSmartProgressRing: typeof import('@hoppscotch/ui')['HoppSmartProgressRing']
HoppSmartRadio: typeof import('@hoppscotch/ui')['HoppSmartRadio'] HoppSmartRadio: typeof import('@hoppscotch/ui')['HoppSmartRadio']
HoppSmartRadioGroup: typeof import('@hoppscotch/ui')['HoppSmartRadioGroup'] HoppSmartRadioGroup: typeof import('@hoppscotch/ui')['HoppSmartRadioGroup']
HoppSmartSelect: typeof import('@hoppscotch/ui')['HoppSmartSelect']
HoppSmartSelectOption: typeof import('@hoppscotch/ui')['HoppSmartSelectOption']
HoppSmartSelectWrapper: typeof import('@hoppscotch/ui')['HoppSmartSelectWrapper'] HoppSmartSelectWrapper: typeof import('@hoppscotch/ui')['HoppSmartSelectWrapper']
HoppSmartSlideOver: typeof import('@hoppscotch/ui')['HoppSmartSlideOver'] HoppSmartSlideOver: typeof import('@hoppscotch/ui')['HoppSmartSlideOver']
HoppSmartSpinner: typeof import('@hoppscotch/ui')['HoppSmartSpinner'] HoppSmartSpinner: typeof import('@hoppscotch/ui')['HoppSmartSpinner']
@ -214,11 +212,9 @@ declare module 'vue' {
IconLucideArrowLeft: typeof import('~icons/lucide/arrow-left')['default'] IconLucideArrowLeft: typeof import('~icons/lucide/arrow-left')['default']
IconLucideArrowUpRight: typeof import('~icons/lucide/arrow-up-right')['default'] IconLucideArrowUpRight: typeof import('~icons/lucide/arrow-up-right')['default']
IconLucideBrush: typeof import('~icons/lucide/brush')['default'] IconLucideBrush: typeof import('~icons/lucide/brush')['default']
IconLucideCheck: typeof import('~icons/lucide/check')['default']
IconLucideCheckCircle: typeof import('~icons/lucide/check-circle')['default'] IconLucideCheckCircle: typeof import('~icons/lucide/check-circle')['default']
IconLucideChevronRight: typeof import('~icons/lucide/chevron-right')['default'] IconLucideChevronRight: typeof import('~icons/lucide/chevron-right')['default']
IconLucideCircleCheck: typeof import('~icons/lucide/circle-check')['default'] IconLucideCircleCheck: typeof import('~icons/lucide/circle-check')['default']
IconLucideCopy: typeof import('~icons/lucide/copy')['default']
IconLucideGlobe: typeof import('~icons/lucide/globe')['default'] IconLucideGlobe: typeof import('~icons/lucide/globe')['default']
IconLucideHelpCircle: typeof import('~icons/lucide/help-circle')['default'] IconLucideHelpCircle: typeof import('~icons/lucide/help-circle')['default']
IconLucideInbox: typeof import('~icons/lucide/inbox')['default'] IconLucideInbox: typeof import('~icons/lucide/inbox')['default']

View file

@ -171,9 +171,6 @@ export async function setInitialUser() {
isGettingInitialUser.value = true isGettingInitialUser.value = true
const res = await getInitialUserDetails() 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) { if ("error" in res) {
await setUser(null) await setUser(null)
isGettingInitialUser.value = false isGettingInitialUser.value = false
@ -291,19 +288,16 @@ async function sendMagicLink(email: string) {
async function setAuthCookies(headers: Headers) { async function setAuthCookies(headers: Headers) {
const cookieHeader = headers.get("set-cookie") const cookieHeader = headers.get("set-cookie")
const cookies = cookieHeader ? cookieHeader.split(",") : [] if (!cookieHeader) return
const accessTokenMatch = cookies.join(",").match(/access_token=([^;]+)/) const accessTMatch = cookieHeader.match(/access_token=([^;,\s]+)/)
const refreshTokenMatch = cookies.join(",").match(/refresh_token=([^;]+)/) const refreshTMatch = cookieHeader.match(/refresh_token=([^;,\s]+)/)
if (accessTokenMatch) { if (accessTMatch) {
const accessToken = accessTokenMatch[1] await persistenceService.setLocalConfig("access_token", accessTMatch[1])
await persistenceService.setLocalConfig("access_token", accessToken)
} }
if (refreshTMatch) {
if (refreshTokenMatch) { await persistenceService.setLocalConfig("refresh_token", refreshTMatch[1])
const refreshToken = refreshTokenMatch[1]
await persistenceService.setLocalConfig("refresh_token", refreshToken)
} }
} }
@ -551,10 +545,14 @@ export const def: AuthPlatformDef = {
}) })
const res = await response const res = await response
if (E.isLeft(res)) { if (E.isLeft(res)) return false
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
}, },
} }