Skip to content

feat(auth): add device-code login - #111

Open
Simon Liang (AgentWings) wants to merge 2 commits into
scalar-nextfrom
feat/device-login
Open

Simon Liang (AgentWings) wants to merge 2 commits into
scalar-nextfrom
feat/device-login

Conversation

@AgentWings

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

Copy link
Copy Markdown
Collaborator

Summary

Add dedalus login --flow device for terminals that cannot open a browser or receive a loopback callback. With a configured provider, the command displays a verification link and code, waits for approval, and saves tokens for later commands with automatic refresh.

Device authorization sends the configured API resource. Discovery verifies the issuer and advertised device grant and validates the device, token, and revocation endpoints. The saved revocation endpoint survives token refresh so logout can revoke the rotated refresh token before clearing local credentials. Failed revocation warns and still permits local sign-out.

Public device sign-in remains disabled: the default issuer and discovery URL are unset, and the command fails before making a network request. Enabling it requires verified client registration, API audience binding, and organization binding on the server; advertising discovery metadata alone is insufficient. Use an API key until that support is verified.

Depends on #106 and continues to target feat/generated-browser-login. Includes the updated parent branch and resolves the conflicting login/logout documentation.

Verification

  • pnpm build and pnpm typecheck pass for ESM and CJS.
  • node --test tests/*.test.mjs: 55 tests pass.
  • pnpm test:package: 26 installed-package checks pass.
  • The resource assertion failed against the previous implementation and passes with the fix.
  • Three additional cases cover no network requests when unconfigured and rejection of missing or insecure revocation endpoints; no tests removed.
  • The approval fixture verifies API resource transmission, saved-token use, refresh rotation, and revocation of the rotated token on logout.
  • git diff --check passes.

Release limitation

Local fixtures verify the CLI protocol behavior, not hosted readiness. Server device-grant and organization-binding support remain prerequisites for enabling public device sign-in.

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): resource is configured but never sent. login.ts:465 uses it only to check the base URL. The request at login.ts:553 sends just client_id and scope. Without resource, the AS sets the token's audience (aud, who the token is for) to the client ID (token_device.go:150-152). The gateway requires aud == https://dcs.dedaluslabs.ai (cli_gateway_as_token.go:82). So every device token would be rejected even after the server enables the grant. The fake /device handler in the test never checks resource. Fix: pass it through and assert form.get('resource') === origin.
issue (design, blocking): the server refuses this grant on purpose, and turning it on is more than a config change. Right now the live AS answers unauthorized_client: Client not registered for device authorization grant for dedalus-cli. TestCLIClientRegistersOnlyNativeCodeFlow (seed_test.go:45) locks in that restriction. There's also a deeper gap: approving a device code never records the organization. userinfo.go:99-100 rejects dedalus-cli tokens that have no org, and browser authorization only works because authorize.go:229 records it. The server contract needs to come first.

Base automatically changed from feat/generated-browser-login to scalar-next September 29, 2026 22:09

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.

2 participants