Skip to content

fix(client): discard the urql client on logout - #4444

Open
gilgardosh wants to merge 1 commit into
mainfrom
claude/urql-step-4-logout-reset
Open

fix(client): discard the urql client on logout#4444
gilgardosh wants to merge 1 commit into
mainfrom
claude/urql-step-4-logout-reset

Conversation

@gilgardosh

Copy link
Copy Markdown
Collaborator

Step 4 of 10 in the urql quick-wins sequence — tracking doc in #4437.

⚠️ Stacked on #4443 (step 3) → #4442#4440. Retarget as the stack merges.

The problem

useLogout cleared sessionStorage and handed off to Auth0, but left the urql client alone. The call that would have fixed it was commented out:

// TODO: clear URQL cache
// urqlClient.resetStore?.();

resetStore() is an Apollo method. urql does not implement it — so uncommenting that line would have silently done nothing, and the optional call would have swallowed the mistake. Its test was parked as it.skip for the same reason.

The fix

useLogout now calls resetUrqlClientAndNotify()before logout(), not after, because logout() triggers a full-page redirect and work queued behind it may never run.

Why this matters later rather than now

Be clear about the severity: this has no visible effect today. The redirect tears down module state anyway, and the client currently holds no cache to leak.

It stops being harmless the moment a cache exists. A normalized cache surviving a logout-then-login inside a single page lifetime would serve the previous user's entities to the next one — and this is a multi-tenant app. Landing it now, before @urql/exchange-graphcache, means that window never opens.

The extraction

resetUrqlClient() alone only nulls the singleton. Everything already holding the old Client through the Provider — every mounted useQuery — keeps using it, along with its bearer token.

setBusinessScope already knew this and paired the reset with the onClientReset swap. That pair is now resetUrqlClientAndNotify(), shared by both callers rather than each remembering to make the second call.

Testing

Test-first: both new cases confirmed failing before the implementation, with the pre-existing Auth0 case still passing.

  • the skipped test un-skipped and rewritten against the real API → asserts resetUrqlClientAndNotify is called once
  • a new ordering case → asserts ['reset', 'logout'], locking in the redirect constraint above

urql-client.test.ts's 14 auth cases exercise setBusinessScope and still pass through the refactored helper.

yarn test:client   47 files, 371 passed, 0 skipped
yarn lint          0 errors, 909 warnings (all pre-existing)
tsc --noEmit       clean

The suite has no skipped tests for the first time — that it.skip was the last one.

🤖 Generated with Claude Code

https://claude.ai/code/session_01TbrGL3NndzRiEwJkwHxnbm


Generated by Claude Code

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The refactor is small and cohesive, updates both call sites and tests, and the new behavior matches the stated logout safety goal without introducing inconsistencies.

Pull request overview

This PR hardens the client-side logout flow by ensuring the app discards the current urql Client (and any in-memory auth/caching state tied to it) before triggering the Auth0 logout redirect, preventing stale client state from surviving into a subsequent session within the same page lifetime.

Changes:

  • Extracted resetUrqlClientAndNotify() to centralize the “reset singleton + swap Provider client” behavior.
  • Updated useLogout to call resetUrqlClientAndNotify() before invoking Auth0 logout().
  • Un-skipped and expanded useLogout tests to assert both the reset call and the reset → logout ordering; added a changeset entry.
File summaries
File Description
packages/client/src/providers/urql.tsx Extracts resetUrqlClientAndNotify() and uses it from setBusinessScope to consistently reset+swap the Provider client.
packages/client/src/hooks/use-logout.ts Resets urql client state before Auth0 logout redirect to avoid old client/token surviving logout.
packages/client/src/hooks/tests/use-logout.test.ts Replaces the skipped Apollo-style resetStore test with urql-specific reset+ordering assertions.
.changeset/urql-logout-client-reset.md Documents the behavior change as a patch release for @accounter/client.
Review details
  • Files reviewed: 4/4 changed files
  • Comments generated: 0
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@gilgardosh
gilgardosh force-pushed the claude/urql-step-3-error-toast-rollout branch from cd99684 to 86e9ee7 Compare September 10, 2026 16:31
@gilgardosh
gilgardosh force-pushed the claude/urql-step-4-logout-reset branch from 2d722a8 to 6d61443 Compare September 10, 2026 16:31
@gilgardosh
gilgardosh temporarily deployed to accounter-fullstack September 10, 2026 16:31 — with GitHub Actions Inactive
@gilgardosh
gilgardosh temporarily deployed to accounter-fullstack September 10, 2026 16:31 — with GitHub Actions Inactive
Base automatically changed from claude/urql-step-3-error-toast-rollout to claude/urql-step-2-query-error-toast September 10, 2026 17:11
Base automatically changed from claude/urql-step-2-query-error-toast to main September 10, 2026 17:14
`useLogout` cleared sessionStorage and handed off to Auth0 but left the urql
client alone. The call that would have fixed it was commented out behind a
`// TODO: clear URQL cache` and pointed at `urqlClient.resetStore?.()` — an
Apollo method urql does not implement, so uncommenting it would have silently
done nothing. Its test was parked as `it.skip` for the same reason.

`useLogout` now calls `resetUrqlClientAndNotify()`, before `logout()` rather than
after: `logout()` triggers a full-page redirect, so work queued behind it may
never run.

No visible effect today — the redirect tears down module state anyway, and the
client holds no cache to leak. It stops being harmless once one exists: a
normalized cache surviving a logout-then-login within a single page lifetime
would serve the previous user's entities to the next one. Landing this before
`@urql/exchange-graphcache` keeps that unreachable.

`resetUrqlClientAndNotify` is extracted from `setBusinessScope`, which already
paired `resetUrqlClient()` with the `onClientReset` swap. Clearing the singleton
alone is not enough — everything already holding the old `Client` through the
Provider keeps using it — and both callers need both halves, so they now share
one function rather than each remembering the second call.

The suite has no skipped tests for the first time: 371 passing, 0 skipped.

Step 4 of the urql quick-wins sequence.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TbrGL3NndzRiEwJkwHxnbm
@gilgardosh
gilgardosh force-pushed the claude/urql-step-4-logout-reset branch from 6d61443 to 30a1c27 Compare September 10, 2026 17:20
@gilgardosh
gilgardosh temporarily deployed to accounter-fullstack September 10, 2026 17:20 — with GitHub Actions Inactive
@gilgardosh
gilgardosh temporarily deployed to accounter-fullstack September 10, 2026 17:20 — with GitHub Actions Inactive
@gilgardosh
gilgardosh temporarily deployed to accounter-fullstack September 10, 2026 17:20 — with GitHub Actions Inactive
@gilgardosh
gilgardosh temporarily deployed to accounter-fullstack September 10, 2026 17:20 — with GitHub Actions Inactive
@github-actions

Copy link
Copy Markdown
Contributor

The latest changes of this PR are not available as alpha, since there are no linked changesets for this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants