Skip to content

feat: guest login credentials, stale-key handling, and sign-in link refresh#306

Draft
leggetter wants to merge 10 commits into
mainfrom
feat/cli-guest-tracking-fixes
Draft

feat: guest login credentials, stale-key handling, and sign-in link refresh#306
leggetter wants to merge 10 commits into
mainfrom
feat/cli-guest-tracking-fixes

Conversation

@leggetter

@leggetter leggetter commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

Companion PR

Coordinate with hookdeck/core #5233 — that PR owns API, Dashboard CliAuth, Console analytics, and L3 journeys. Merge core first, then this PR.


Why we're doing this

Same motivations as core #5233:

  1. Product / UX — default hookdeck login after listen claims and upgrades the same guest sandbox (signupGuest), so the developer keeps their Console work instead of landing on a disconnected account. The CLI sends guest attestation on POST /cli-authguest_user_id + guest_api_key so the server can prove the caller is that guest — which enables that in-place upgrade. Keeping one PostHog distinct_id through guest → signup is the analytics consequence, not the primary user benefit.
  2. Product — long listen sessions must not show an expired Console sign-in link; refresh the 1h /signin/guest?token=… URL automatically.

Measurement note: only Journey A (default claim) gets a clean, linked conversion. Journeys B and C leave the guest sandbox behind; those conversions are not linked to the guest person (see core #5233).


CLI behaviour (no flags — follows CLI profile)

Behaviour is driven by what's stored in the profile after listen / logout. There is no auth_intent flag and no TTY prompt.

Path When POST /cli-auth body Browser opens Outcome
A — Claim sandbox (default) Guest profile present (guest_url set) guest_user_id + guest_api_key (guest attestation) Signup → CliAuth Same user upgraded; Console sandbox kept → Console
B — Existing Platform account After hookdeck logout (guest fields cleared) device_name only Signin → CliAuth team picker CLI on existing Platform org; guest sandbox left on the original guest account, not carried over
C — Accidental new signup Same as B, user clicks Sign up on signin page (none — signin ticket) Signup + abandon banner → onboarding New Platform org; guest sandbox left on the original guest account, not carried over; CLI Guest Sandbox Abandoned on core
hookdeck listen
hookdeck login              # A: claim guest sandbox (default)

hookdeck logout             # required for B — clears guest creds so login uses sign-in, not signup
hookdeck login              # B: sign in to existing Platform account
# If you then click "Sign up" in the browser → C

Path B note: use this when you already have a Hookdeck Platform (Dashboard) account and want the CLI on that org. Without hookdeck logout first, the CLI still has guest credentials and the default sends you to signup (path A), not sign-in.

Claim path details (A)

  • Valid guest API key does not skip the browser — CLI still calls StartLogin so the user can complete signup / signupGuest.
  • Stale guest API key (401 on validate): preserve credentials for the POST, clear the stored key, continue browser login with guest attestation (guest_user_id + guest_api_key so the server can prove the caller is that guest).

Existing Platform account (B)

  • hookdeck logout clears guest_user_id, guest_api_key, and guest_url from the profile.
  • Next hookdeck login sends device name only → Dashboard signin URL.

Listen guest URL (separate from hookdeck login)

The TUI link to open Console in a browser (/signin/guest?token=…). This is not the hookdeck login claim flow.

  • RefreshGuestSigninLink on listen start and ~50m TUI tick → POST /cli/guest/signin-link (core endpoint).
  • Mints a fresh token for the same URL shape; destination after auth is unchanged (Console).

MCP hookdeck_login follows the same guest vs logged-out rules.


Code changes

  • pkg/login/client_login.go — guest credentials on POST; stale-key handling; logout clears guest fields
  • pkg/login/guest_link.go — refresh helper + listen integration
  • pkg/login/guest_link_test.go — refresh success, API error fallback, missing profile no-op
  • test/acceptance/guest_login_acceptance_test.go — guest POST body vs after logout

Test plan

  • go test ./...
  • go test -tags=guest ./test/acceptance/... — guest profile POST body vs after logout
  • go test ./test/acceptance/... — stale validate 401 → browser flow (login_auth_acceptance_test.go)
  • pkg/login/guest_link_test.go
  • Pair with core staging / L3 Playwright (core #5233 test plan)

Depends on

core #5233: origin_guest_user_id, guest attestation (guest_user_id + guest_api_key on POST /cli-auth so the server can prove the caller is that guest), POST /cli/guest/signin-link, CliAuth guest-claim completion.

leggetter and others added 8 commits June 22, 2026 16:29
Store guest_user_id, attest guest credentials on cli-auth login, and
refresh guest sign-in URLs on listen start and via a TUI ticker.

Co-authored-by: Cursor <cursoragent@cursor.com>
Add claim_guest, login, and create_new intents with TTY prompt, Cobra flags,
and auth_intent on POST /cli-auth. MCP login defaults to claim_guest with
guest credentials preserved on stale 401.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Add guest-tagged acceptance tests and CI matrix slice for auth_intent on
POST /cli-auth.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Guest profiles always claim the Console sandbox on login. Use hookdeck
logout then hookdeck login to attach the CLI to an existing Platform
account. Remove the TTY three-way prompt.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Guest profile presence alone selects claim_guest; drop Options and intent
flags from the login command surface.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
When guest_url is set, a valid API key no longer short-circuits login.
Preserve stale guest key on validate 401 and send guest credentials on
POST /cli-auth. Remove auth_intent from StartLogin payload.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Add unit test for guest profile with valid key reaching signup claim flow.
Update guest acceptance test to assert guest_user_id/guest_api_key on POST
after validate 401 without auth_intent.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Cover profile update on success, fallback to saved URL on API error,
and no-op when guest profile fields are missing.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@leggetter leggetter changed the title feat(cli): persist guest lineage and refresh sign-in links feat(cli): guest login credentials, stale-key handling, and sign-in link refresh Jun 23, 2026
@leggetter leggetter changed the title feat(cli): guest login credentials, stale-key handling, and sign-in link refresh feat: guest login credentials, stale-key handling, and sign-in link refresh Jun 23, 2026
leggetter and others added 2 commits June 23, 2026 14:07
Guard empty link responses, defer refresh to async TUI path, unify
persisting refresh through login.RefreshGuestSigninLink, and fix guest
API error formatting.

Co-authored-by: Cursor <cursoragent@cursor.com>
Redact guest_api_key from PerformRequest debug bodies and Authorization
from debug headers. Write hookdeck login --local config as 0600.

Co-authored-by: Cursor <cursoragent@cursor.com>
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.

1 participant