Skip to content

docs: add a runnable native CLI example and CLI provider sign-in - #160

Merged
Bccorb merged 1 commit into
mainfrom
docs/native-cli-example
Nov 6, 2026
Merged

Bccorb merged 1 commit into
mainfrom
docs/native-cli-example

Conversation

@Bccorb

@Bccorb Bccorb commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

The CLI half of #137: a runnable CLI example with both sign-in flows, kept running in CI, plus real content where the guide had a placeholder and the CLI reference for seamless profile login.

Refs #137. It stays open: ADR 0002 N11 asks for the example to be kept runnable "against a released API and edge", and CI still builds the API from source until v0.23.0 ships (fells-code/seamless-auth-api#561). Everything else in the issue is met once this merges. Close it when the recipe pin moves to the released API and both workflows pass.

The example (examples/native-cli/)

A small Node CLI (notes login, login --device, status, token, logout) on openid-client and @napi-rs/keyring, the same two libraries as the desktop example.

  • Loopback: system browser, PKCE S256, 127.0.0.1 on an OS-picked port, BROWSER as an override, and the URL always printed.
  • Device: the RFC 8628 grant, with nothing reaching back to the machine.
  • Tokens: in the OS keychain, with no file fallback and no client secret. The client is registered by scripts/register-client.mjs, because the recipe's seed has no client with the device grant.

Unlike the desktop example, a CLI is a new process per command and commands can overlap, so the access token is in the keychain too and a refresh takes a cross-process lock and re-reads inside it.

Why a standalone CLI, not seamless profile login

The issue asks for an example that shows adopters the pattern, with nothing specific to one product. seamless profile login spends the provider token at POST /sessions/exchange for a first-party admin session and keeps no provider token. An adopter's CLI should keep the provider's tokens and call its own API, so copying profile login would teach the wrong thing. It is also unpublished, and it has its own e2e in seamless-cli. The guide documents it in its own subsection, and says not to copy the exchange.

The docs

  • build/native-apps-oidc: replaces the placeholder with the example, what differs for a CLI, and seamless profile login through the provider, labelled merged but unreleased. Also drops a stale "still in review" about the recipe.
  • reference/cli-commands: --device, --email-code, --oidc-issuer, --client-id on profile login, the provider flags on profile add, and a new short clients section for --session-exchange. All of it is labelled as needing an unpublished CLI and API v0.23.0. oidc_cli_client_id is described as not settable yet (feat(config): make oidc_cli_client_id writable once API v0.23 is released seamless-cli#351).

What was run

  • e2e/run.mjs against the local recipe on macOS, with the real keychain: 74 of 74 checks. Recipe at 30be59b, on spare ports and its own compose project. The stack was stopped and wiped afterwards.
  • Control: with the refresh lock removed, the six-commands-at-once check fails with the grant revoked (6 refreshes instead of 1), and the run stops later because the Seamless session ended too. So that check bites.
  • The run found one real bug, now fixed: a denied device sign-in printed openid-client's generic error. A check now covers it.
  • npm run lint, npm run typecheck, npm run build, and prettier on the example: all pass.
  • The new workflow runs on GitHub for the first time on this PR (Linux, gnome-keyring). See its checks.

Not tested

The OS opening a real browser (a capture script through BROWSER hands the URL to Playwright's Chromium), a real passkey (virtual authenticator), a second physical device for --device, Windows Credential Manager, a locked or absent keychain, a device code that expires, slow_down, a published API, and a packaged build.

seamless profile login was not run here. The docs for it come from the merged code at 5e62f4a (instanceLogin.ts, providerSignIn.ts, helpTopics.ts, README.md, the changeset). The recipe pins an API from before the exchange route. fells-code/seamless-cli#349 records its own run.

Known gaps

  • Sign-in rate limits stay on in the recipe (15 minutes, per address), and each e2e run registers a user. Several local runs in a row start failing on the hosted page. The README says so and how to clear it. CI starts fresh each time.
  • The e2e's check that no token reached a file covers the CLI's working and state directories, not the whole disk.
  • The page says CI runs on a schedule, which is true once this merges. The example's GitHub link 404s until then.
  • examples/native-desktop/README.md still links the recipe's merged PR docs: label self-hosted start pages as the self-hosted path #60. Not changed here.

A standalone CLI on the OpenID Provider with the loopback and device
flows, tokens in the OS keychain and no client secret, kept running by
a new workflow. Replaces the placeholder in the native apps guide, and
adds the provider flags of seamless profile login and seamless clients
--session-exchange to the CLI reference, labelled merged but unreleased.

Refs #137
@Bccorb
Bccorb merged commit 3b45403 into main Nov 6, 2026
4 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.

1 participant