Skip to content

Refactor cookie-mode anonymous history hand-off - #144

Open
akshatmalik-bruh wants to merge 1 commit into
extra-org:mainfrom
akshatmalik-bruh:feat/anonymous-history-handoff
Open

akshatmalik-bruh wants to merge 1 commit into
extra-org:mainfrom
akshatmalik-bruh:feat/anonymous-history-handoff

Conversation

@akshatmalik-bruh

Copy link
Copy Markdown
Contributor

Context

Fixes the issue where the frontend was forced to guess when an anonymous visitor logged into a cookie-authenticated host app by repeatedly polling POST /auth/link and receiving 401 Unauthorized responses. This guessing game resulted in unnecessary network spam, latency, and a fragile dependency on window/cookie events.
fixes #131

What Changed

Implemented Server-Side Opportunistic Hand-off. The server is now the authoritative source of truth for when an authenticated session begins.

Backend (deps.py)

  • The frontend now attaches the anonymous visitor pass to every request via the X-Extra-Visitor-Pass header.
  • get_principal intercepts this header. If the request has an authenticated session cookie and a visitor pass header, the backend opportunistically calls link_anonymous before serving the request.
  • The hand-off is instant, atomic, and idempotent.
  • Invalid or already-consumed visitor passes are silently ignored, ensuring authenticated requests are never blocked.

Frontend (tokenSource.ts & AgentChatClient.ts)

  • Deleted all cookie-mode identity guessing machinery (identityCheckDirty, cookieSnapshotChanged, forceCheck, and focus/visibility/storage event listeners).
  • The widget no longer issues speculative POST /auth/link probes in cookie mode.
  • AgentChatClient simply attaches the X-Extra-Visitor-Pass header to every fetch.

Verification

  • Added 8 comprehensive regression tests in test_api.py covering all edge cases (immediate login, already-adopted pass, invalid pass, concurrent requests, bearer mode preservation).
  • make check passes perfectly (linting, typechecking, and all 57 tests).

@Asaf-prog
Asaf-prog self-requested a review October 10, 2026 19:34
@Asaf-prog

Copy link
Copy Markdown
Collaborator

Thanks for the PR. I reviewed the current HEAD and I still see a few blockers before this is ready to merge.

  1. Visitor pass lifecycle

After a successful server-side adoption, the visitor pass is never cleared from localStorage.

AgentChatClient now sends X-Extra-Visitor-Pass on every request, and TokenSource.visitorPass keeps returning the stored pass indefinitely.

That means the flow becomes:

adoption succeeds
→ pass remains stored
→ every future request sends the same pass
→ server keeps attempting adoption again

We need an explicit acknowledgement/consumption mechanism so the widget can clear the pass after a successful hand-off. Invalid/expired passes should also not be sent forever.

  1. Keep the opportunistic flow scoped to cookie mode

get_principal() currently triggers adoption for any non-anonymous principal when the header is present.

That means bearer/token-authenticated requests can also enter the new implicit hand-off path, while #131 was specifically about fixing zero-code cookie mode without changing the existing bearer flow.

Please either:

  • restrict opportunistic hand-off to cookie-authenticated requests, or
  • explicitly redesign the contract as a generic hand-off mechanism and update the tests/docs accordingly.

The current test_bearer_mode_auth_link_still_works_after_refactor only proves that /auth/link still exists; it does not prove that the widget's bearer flow remains unchanged.

  1. Update the frontend regression tests and generated widget bundle

The TypeScript implementation changed substantially, but widget.test.mjs and the committed widget.js bundle have not been updated.

The existing widget tests still expect the old /auth/link behavior, including clearing the visitor pass after the merge.

Please update the frontend tests to cover the new contract and rebuild/commit widget.js.

  1. The concurrency test is not actually concurrent

test_concurrent_authenticated_requests_adopt_history_exactly_once() currently performs two requests sequentially.

Since concurrency is an explicit acceptance criterion in #131, please add a real concurrent/racing test that proves two first authenticated requests cannot adopt the same visitor history twice or produce inconsistent ownership.

  1. Remove the unrelated starter model change

This PR also changes:

examples/starter/agents.yaml

from:

qwen2.5:14b

to:

openai/gpt-oss-20b

That is unrelated to anonymous-history hand-off and should be removed from this PR.

The overall server-side opportunistic hand-off direction is good and matches the architecture proposed in #131. I don't think the core approach needs to be redesigned — these are mainly lifecycle, auth-boundary, and regression-coverage issues.

One final repo-state point: the branch is currently behind main, so please update it against the latest main before the final review and rerun CI.

This branch has not been deployed

No deployments
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.

Refactor Cookie-Mode Anonymous History Hand-off to Avoid Repeated /auth/link Probes

2 participants