Skip to content

feat(auth): enable generated browser login - #106

Merged
Shengming Liang (LiangShengming) merged 3 commits into
scalar-nextfrom
feat/generated-browser-login
Sep 29, 2026
Merged

Shengming Liang (LiangShengming) merged 3 commits into
scalar-nextfrom
feat/generated-browser-login

Conversation

@AgentWings

@AgentWings Simon Liang (AgentWings) commented Sep 23, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

dedalus login --flow browser opens the public authorization endpoint and uses the OAuth runtime already generated in this CLI for PKCE, local token storage, token refresh, and logout. This PR configures that runtime for browser sign-in; it retires the older custom login implementation.

Specifically, it aims to bind sign-in to the public API resource, send the required resource indicator and 32-byte state, and verify the callback issuer before exchanging the code. Explicit credentials retain precedence over stored credentials.

Verification

  • Build and ESM/CJS type checks pass.
  • All 39 existing CLI tests pass.
  • Four browser-login checks pass: public configuration, API-host binding, callback issuer rejection, and a subprocess lifecycle covering PKCE, saved-token requests, refresh-token rotation, explicit-key precedence, and local logout.
  • Tests use a local OAuth provider and do not sign into a real account.

Release limitation

Draft: the public authorization endpoint currently returns HTTP 404. Hosted account sign-in, consent, and resource access remain unverified and require compatible server support.

Logout removes local credentials; it does not revoke server tokens. The runtime uses its existing credential-store policy, including file storage on macOS. Existing sessions from dedalus auth login are not migrated or removed.

Replacement scope

Proposed replacement for the client-side browser-login implementation in #34, #35, and #83–#101.

Repro

pnpm install --frozen-lockfile
pnpm run build
pnpm run typecheck
node --test tests/*.test.mjs
node dist/esm/bin.js login --flow browser

The last command requires the server support described above.

Reviewers

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

issue (correctness, blocking): the hardcoded issuer can never match. index.ts:859-873 pins issuer: https://dcs.dedaluslabs.ai. The AS takes iss from its own config, not from the request host. So even if dcs proxied /oauth2/*, every real callback would fail the new iss check. Users would then see "did not match this sign-in attempt. try again", the same message as a state mismatch, so they'd retry forever. Windsor's stack (metadata.generated.ts on #101) has the same host, so this looks like a shared assumption. Someone should confirm the intended issuer before either stack lands. The AS README also shows the CLI's authorization request going through the Website, not straight to /oauth2/auth.
issue (correctness, blocking): logout doesn't revoke tokens. The AS's documented CLI contract says logout revokes both tokens through /oauth2/revoke, and only a confirmed revocation plus local deletion counts as success (README:149). dedalus-labs/dedalus-cli#106 only deletes the local copy. That leaves a live offline_access refresh token on the server, and the README change documents this as intended. dedalus-labs/dedalus-cli#106 says it replaces #83–#101, and that stack implemented revocation (#91).
issue (design, non-blocking): hand edits to Scalar-generated files have no Custom markers. auth.methods comes from the generator: Scalar's c88225c removed bearer-auth from that exact array when the spec changed. The DCS spec only declares ApiKeyAuth and BearerAuth (dcs.openapi.yaml:747-757). The method entries belong in an oauth2 security scheme in the spec. The runtime changes (resource, issuer, the iss check, discovery) should either go upstream or be wrapped in markers like src/sdk/client.ts:277.

Signed-off-by: Shengming Liang <shengming@dedaluslabs.ai>
Signed-off-by: Shengming Liang <shengming@dedaluslabs.ai>
@LiangShengming

Copy link
Copy Markdown
Collaborator

Vitus Larrieu (@vitus-dedalus) We’re adopting Codex CLI’s logout behavior here. Server-side revocation is useful, but requiring a confirmed revocation response before deleting local credentials makes logout fail when the user is offline: the local client cannot obtain that confirmation. Local sign-out needs to remain possible without connectivity.

The implementation now attempts refresh-token revocation, uses the access token only when there is no refresh token, and bounds each request to 10 seconds. A failed request produces a warning and still clears local credentials. Failure to delete local credentials still fails logout. We do not claim the server session was revoked when the request failed, and we do not issue a second access-token request after a failed refresh-token revocation.

This matches Codex’s token selection and timeout and its unconditional local cleanup after a revocation failure. The request body remains appropriate for our provider; we are adopting the behavior, not copying OpenAI-specific wire parameters.

This deliberately changes the old AS contract you cited. The revised code is committed locally in 6ad1f27 and has not been pushed yet. Build/typecheck, all 44 CLI tests, installed-package checks, and the local Go/CLI gateway integration pass. Tests include HTTP failure, a dropped connection, a stalled request, access-only sessions, and missing legacy revocation metadata. The issuer/configuration issue is separate and still requires hosted verification before enabling public browser login.

@LiangShengming
Shengming Liang (LiangShengming) merged commit 8200172 into scalar-next Sep 29, 2026
4 checks passed
@scalar-docs scalar-docs Bot mentioned this pull request Sep 29, 2026
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