Skip to content

Open setup links without a second sign-in, in their workspace - #1452

Merged
helpcodeai merged 2 commits into
mainfrom
fix/setup-link-friction
Nov 2, 2026
Merged

helpcodeai merged 2 commits into
mainfrom
fix/setup-link-friction

Conversation

@helpcodeai

Copy link
Copy Markdown
Contributor

What

An end-to-end test of the Claude onboarding on Cloud showed three problems with the setup links the chat tools hand out (/s/<token>, then the guided setup page):

  1. Second sign-in. A user who signed in on the authorization page to connect Claude was sent to the dashboard login again when they opened the link a minute later.
  2. Wrong workspace. For a user in several workspaces, the link opened in the dashboard's current workspace. Every save to the connector, which sits in the workspace granted to Claude, answered 403.
  3. "Already used" on a second click. Clicking the same link twice, or reloading the page, showed "This link was already used".

Change

Backend

  • auth/recent-sign-in.util.ts (new). completeSignIn (the single exit for password, social and dashboard-session sign-ins on the authorization page) sets amcp_signin:

    • a signed, httpOnly, SameSite=Lax cookie with the user id and the sign-in time;
    • it lasts 7 days, the length of a dashboard session.
  • POST /api/setup-links/open (no JWT). It opens a link with that cookie only when:

    • the cookie is valid and was not revoked by sessionsValidFrom;
    • on Cloud, the e-mail is verified;
    • the user's workspaces do not enforce SSO (isPasswordLoginBlocked), so the IdP's MFA and Conditional Access still apply;
    • the link belongs to that same user.

    It then returns a dashboard token for the link's workspace, the same token POST /organizations/switch issues. A link copied out of a chat opens nothing in a browser without the cookie, and the cookie alone opens nothing without a link.

  • POST /api/setup-links/resolve (JWT). When the dashboard session is on another workspace than the connector, it returns a session for the connector's workspace, as the workspace menu would.

  • resolveLink.

    • It returns the link's organizationId.
    • It opens again for the same user until the link expires; usedAt records only the first opening.
    • The link stays bound to its user and needs that user's session, so reopening it gives nobody else anything.

Frontend

  • /s/<token>.
    • Without a session, it tries open first and falls back to /login?redirect=….
    • It stores the returned session with login().
    • proxy.ts and auth-context no longer bounce /s/ to /login before the page can try.

Cloud and self-hosted

The behaviour is the same in both modes. The cookie is only sent when the dashboard and the authorization page share a host; with a separate API host, open answers 401 and the page falls back to the usual sign-in.

Tests

Backend, in packages/backend:

  • npx jest --maxWorkers=4 src/auth/recent-sign-in.util.spec.ts src/adapters/setup-links.controller.spec.ts src/adapters/connector-setup.service.spec.ts src/auth/login.controller.spec.ts src/auth/login.controller.social.spec.ts: 5 suites passed.
    • New specs: the cookie's flags, round trip, forged, expired and malformed values.
    • New specs for open: success, no cookie, link of another account, revoked session, unknown user, unverified e-mail, SSO enforced.
    • New specs for resolve: switches workspace, keeps the session on the same workspace, expired link.
    • Reopening a link.
    • The login sets the cookie on success and not on a wrong password.
  • npx tsc --noEmit -p tsconfig.json: passed. npx eslint on the changed files: clean.

Frontend, in packages/frontend:

  • npx tsc --noEmit -p tsconfig.json: passed.
  • npx eslint on the changed files: no errors; 2 existing warnings in auth-context.tsx.
  • npx playwright test tests/e2e/setup-link.spec.ts: 7 passed. New tests: the workspace switch, and opening with the sign-in just made.

A setup link from the chat sent a user who had just signed in to connect
Claude to the dashboard login again. The authorization page now leaves a
signed, httpOnly cookie with the user and time of that sign-in; /s/<token>
opens a dashboard session with it, only for the link's own user, never for
a workspace that requires SSO, and not after sessions were revoked.

A link now switches the dashboard to the connector's workspace, where
saving to a connector in another one was refused with 403, and it opens
again for the same user until it expires instead of saying already used.
@helpcodeai
helpcodeai enabled auto-merge (squash) November 2, 2026 16:58
@helpcodeai
helpcodeai merged commit a326c23 into main Nov 2, 2026
14 checks passed
@helpcodeai
helpcodeai deleted the fix/setup-link-friction branch November 2, 2026 17:17
@github-actions github-actions Bot locked and limited conversation to collaborators Nov 2, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant