From 08077476f331753a02e4ecbaadd343c0c7b47061 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Malfait?= Date: Fri, 27 Mar 2026 09:21:26 +0100 Subject: [PATCH] fix: remove remaining direct cookie writes that make tokenPair a session cookie on renewal (#19031) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - Removes two remaining direct `cookieStorage.setItem('tokenPair', ...)` calls that were overwriting the Jotai-managed cookie (180-day expiry) with a session cookie (no expiry) during **token renewal** - Followup to #18795 which fixed the same issue in `handleSetAuthTokens` but missed the renewal code paths ## Root cause Two token renewal paths still had direct cookie writes without `expires`: 1. **`apollo.factory.ts`** — `attemptTokenRenewal()` fires on every `UNAUTHENTICATED` GraphQL error after a successful token refresh 2. **`useAgentChat.ts`** — `retryFetchWithRenewedToken()` fires on 401 from the AI chat endpoint Both called `cookieStorage.setItem('tokenPair', JSON.stringify(tokens))` without an `expires` attribute, creating a session cookie that overwrote the Jotai-managed one. This is why the bug was **intermittent after #18795**: it only appeared after a token renewal, not on fresh login. The `onTokenPairChange` / `setTokenPair` calls already write through Jotai's `atomWithStorage` → `createJotaiCookieStorage`, which always sets `expires: 180 days`. ## Test plan - Log in to the app - Wait for a token renewal to occur (or force one by letting the access token expire) - Inspect the `tokenPair` cookie in DevTools → Application → Cookies - Verify the cookie retains an expiration date ~180 days from now (not "Session") - Close and reopen the browser — confirm you remain logged in Made with [Cursor](https://cursor.com) --- packages/twenty-front/src/modules/ai/hooks/useAgentChat.ts | 3 --- .../twenty-front/src/modules/apollo/services/apollo.factory.ts | 2 -- 2 files changed, 5 deletions(-) diff --git a/packages/twenty-front/src/modules/ai/hooks/useAgentChat.ts b/packages/twenty-front/src/modules/ai/hooks/useAgentChat.ts index 393f176a5a..6117afd30f 100644 --- a/packages/twenty-front/src/modules/ai/hooks/useAgentChat.ts +++ b/packages/twenty-front/src/modules/ai/hooks/useAgentChat.ts @@ -31,8 +31,6 @@ import { useCallback, useState } from 'react'; import { type ExtendedUIMessage } from 'twenty-shared/ai'; import { isDefined } from 'twenty-shared/utils'; import { REACT_APP_SERVER_BASE_URL } from '~/config'; -import { cookieStorage } from '~/utils/cookie-storage'; - export const useAgentChat = ( uiMessages: ExtendedUIMessage[], ensureThreadIdForSend: () => Promise, @@ -94,7 +92,6 @@ export const useAgentChat = ( return null; } - cookieStorage.setItem('tokenPair', JSON.stringify(renewedTokens)); setTokenPair(renewedTokens); const updatedHeaders = new Headers(init?.headers ?? {}); diff --git a/packages/twenty-front/src/modules/apollo/services/apollo.factory.ts b/packages/twenty-front/src/modules/apollo/services/apollo.factory.ts index d278bea5ce..657d6adf72 100644 --- a/packages/twenty-front/src/modules/apollo/services/apollo.factory.ts +++ b/packages/twenty-front/src/modules/apollo/services/apollo.factory.ts @@ -34,7 +34,6 @@ import { import isEmpty from 'lodash.isempty'; import { getGenericOperationName, isDefined } from 'twenty-shared/utils'; import { REACT_APP_SERVER_BASE_URL } from '~/config'; -import { cookieStorage } from '~/utils/cookie-storage'; import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull'; const logger = loggerLink(() => 'Twenty'); @@ -175,7 +174,6 @@ export class ApolloFactory implements ApolloManager { if (isDefined(tokens)) { onTokenPairChange?.(tokens); - cookieStorage.setItem('tokenPair', JSON.stringify(tokens)); } };