Skip to content

Test that the app's /cli-auth forwards to the secure origin when sign-in is on the secure origin - #6958

Merged
habdelra merged 2 commits into
mainfrom
cs-14351-test-that-the-apps-cli-auth-forwards-to-the-secure-origin
Oct 26, 2026
Merged

habdelra merged 2 commits into
mainfrom
cs-14351-test-that-the-apps-cli-auth-forwards-to-the-secure-origin

Conversation

@habdelra

@habdelra habdelra commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

The boxel CLI authorizes a machine by opening the app's /cli-auth?port=…&state=… in a browser. port is where the CLI's loopback listener waits for the sign-in to come back, and state is a value the CLI checks when it does. When a deployment puts sign-in on the secure origin, the app must never draw a password field of its own. So CliAuthRoute.beforeModel (packages/host/app/routes/cli-auth.ts) sends the browser straight on to the secure origin's cli-auth mode with the same port and state, and holds the transition so the app's own CLI page never renders.

No test covered that path.

  • The host's unit test checks only the URL secureCliAuthURL builds.
  • The matrix suite's cli-auth-secure-origin.spec.ts opens the secure origin's link directly, as a CLI that knows the secure origin does, and never visits the app's /cli-auth.
  • That spec always asks for session=secure. So the other way the secure origin can finish a CLI sign-in was never run end to end in a browser: handing the listener a Matrix login token alone, by navigating the browser to it. That is the way a forwarded sign-in finishes, because the app forwards only port and state.

The gap was easy to miss because the app's CLI page and the secure origin's draw the same sign-in screen from the secure origin package. Both carry the same data-test-secure-* attributes under the same "Authorize Boxel CLI" title, so a check written against those attributes passes on either origin.

This PR adds one spec to packages/matrix/tests/sign-in-on-secure-origin.spec.ts, whose signInOnSecureOrigin helper already turns the mode on in the host's config. It runs the CLI's real browserLogin with only a host URL, as boxel profile add does when it doesn't know the deployment's secure origin. browserLogin starts a real loopback listener and builds the real /cli-auth URL, with a real port and state, and the spec hands that URL to a Playwright page. The spec then checks four things:

  • The redirect, by address rather than page content. The browser leaves the app for the secure origin, and page.url() there has mode=cli-auth and the same port and state the CLI put in the app's URL.
  • The sign-in. It signs in on the secure origin's page, which hands the listener a login token, and browserLogin redeems it for a Matrix device.
  • No secure session. The CLI gets no secure session beside the device, since nothing asked for one.
  • The device works. The device belongs to the user who signed in, and whoami with its access token answers with that user and device.

Testing

I ran sign-in-on-secure-origin.spec.ts locally on the matrix test stack against a host dist built from this branch. All seven tests pass.

To show the redirect check can fail, I made beforeModel return before redirecting, rebuilt the host and re-ran the spec. It timed out in page.waitForURL, waiting for a secure origin the browser never reached.

🤖 Generated with Claude Code

With sign-in on the secure origin, CliAuthRoute.beforeModel sends the
browser from the app's /cli-auth to the secure origin's cli-auth mode
with the CLI's port and state. Nothing exercised that redirect: the
existing specs open the secure origin's link directly, and both pages
carry the same data-test-secure-* attributes, so the new spec asserts
on page.url().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-24T21:32:10.163751Z 3e88eb2 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@habdelra habdelra left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

[Claude Code 🤖] This review read the new spec against CliAuthRoute.beforeModel, the host's CLI page, the secure origin's cli-auth mode and its cliCallback checks, and the URL boxel builds for the app's page. It did not run the matrix suite, so whether the spec passes rests on the author's local run and on reading the waits.

Bottom line: no blocking issues. The spec pins what it says it pins: it waits on the secure origin's origin and then checks mode, port and state by address, so neither the app's own CLI page nor a fall-through to the sign-in mode can satisfy it.

  1. Non-blocking follow-up: drive the spec with the CLI's own browserLogin({ hostUrl }) and finish the sign-in, so the hand-off a forwarded CLI receives gets end-to-end coverage (see the inline comment on the page.goto).

CI: Lint and the test-asset build were still running when this was posted, and the matrix shards had not reported yet.

Comment on lines +247 to +249
let port = '53412';
let state = 'cli-auth-state-0123456789';
await page.goto(`${APP_ORIGIN}/cli-auth?port=${port}&state=${state}`);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

[Claude Code 🤖] Could this spec open the page with the CLI's own browserLogin instead of a hand-built URL, and carry on through the sign-in to the listener? The forwarded flow ends in a hand-off that no matrix spec completes.

The app's /cli-auth forwards only port and state. So the secure origin's cli-auth page sees no session=secure and takes the profile: { secureSession: false } branch of handProfileSignIn, which hands the listener a ?loginToken= navigation. cli-auth-secure-origin.spec.ts always builds its URL with buildSecureCliAuthUrl, which adds session=secure, so it only completes the POST branch. The secureSession: false branch is covered only by unit tests in packages/secure-origin/tests/sign-in.test.ts. Taking the URL from buildCliAuthUrl also replaces the hard-coded port/state here with what boxel really sends. Its state is 32 hex characters, which cliCallback accepts.

A sketch, reusing the shape of signInForProfile and expectOwnDevice from cli-auth-secure-origin.spec.ts:

let auth = await browserLogin({
  matrixUrl: getMatrixTestContext().matrixUrl!,
  hostUrl: new URL('/', appURL).href, // no secureOriginUrl: opens the app's /cli-auth
  log: () => {},
  openBrowserFn: async (link) => {
    await page.goto(link);
    await page.waitForURL(
      (url) => url.origin === new URL(secureOriginURL).origin,
    );
    // the address assertions this spec already makes
    await signInOnSecurePage(page, username, password);
    return true;
  },
});
expect(auth.secureSession).toBeUndefined();
// then the whoami check expectOwnDevice makes

This is a follow-up, not a blocker. The spec as written does pin the redirect it claims to.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

[Claude Code 🤖] Done in a2fd2c4. The spec now runs the CLI's own browserLogin with only a host URL, so the app's /cli-auth URL, port and state are what boxel really sends. It signs in on the secure origin and waits for the listener to receive the login token. Then it checks that browserLogin returns no secure session, and that the redeemed device is the user's own and answers whoami. Passes locally with the rest of the file.

The spec now takes its URL from browserLogin's buildCliAuthUrl and signs
in on the secure origin, so the CLI's loopback listener receives the
sign-in. That completes the secure origin's login-token hand-off for a
CLI that didn't ask for a secure session, which no matrix spec reached:
cli-auth-secure-origin.spec.ts always adds session=secure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@habdelra
habdelra requested a review from a team October 24, 2026 21:59
@habdelra
habdelra merged commit c0954a2 into main Oct 26, 2026
42 of 43 checks passed
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.

2 participants