refactor: delegate auth flow to @doist/cli-core/auth#70
Merged
Conversation
- Drops the bespoke OAuth stack (`src/lib/pkce.ts`, `oauth.ts`, `oauth-server.ts`, the entire login action in `commands/auth.ts`) and delegates the flow to `@doist/cli-core/auth`'s `runOAuthFlow` + `attachLoginCommand`. - New `OutlineAuthProvider` (`src/lib/auth-provider.ts`) owns the outline-specific behaviour: `authorize` builds the `/oauth/authorize` URL with cli-core's `generateVerifier` / `deriveChallenge`, `exchangeCode` posts the form-urlencoded token exchange (public client — no `client_secret`), `validateToken` hits `auth.info` with the unsaved token via a new `apiRequest` token/baseUrl override. - New `OutlineTokenStore` adapter maps the cli-core account onto the existing config file (adds `auth_user_id` / `auth_user_name` to round-trip the identity). - Branded success / error HTML preserved byte-for-byte in `src/lib/auth-pages.ts` and passed to `attachLoginCommand` via `renderSuccess` / `renderError`. - `ol auth login` gains `--read-only`, `--callback-port`, `--json`, `--ndjson` for free; `--base-url` / `--client-id` chained onto the returned command. `--token` paste flag dropped — login is OAuth-only. - Skill content + skills/outline-cli/SKILL.md updated to match. - 115 tests pass; type-check + oxlint clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
doistbot
reviewed
May 11, 2026
Member
doistbot
left a comment
There was a problem hiding this comment.
This PR successfully delegates the bespoke OAuth stack to @doist/cli-core/auth, introducing a streamlined OutlineAuthProvider and OutlineTokenStore while preserving the custom branded authentication pages. Leveraging the shared core library significantly reduces duplication and standardizes the login flow across Doist CLI tools. There are a few remaining details to adjust, such as ensuring proxy-aware fetching during token exchange, routing interactive prompts to stderr to protect JSON outputs, preserving environment variable overrides, and addressing a few test coverage and skill documentation updates.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- [P1] Route OAuth token exchange through `fetchWithRetry` so it picks up the shared Undici proxy/retry dispatcher used by the rest of the network stack. - [P2] Preserve `OUTLINE_OAUTH_CALLBACK_PORT` env override by reading it in `commands/auth.ts` and passing it to `attachLoginCommand`'s `preferredPort` (falls back to the default for unparseable values). - [P2] Send the interactive base-URL / client-ID prompts to stderr so `--json` / `--ndjson` envelopes on stdout stay clean. - [P2] Persist `auth_team_name` alongside the rest of the auth identity so `OutlineTokenStore.active()` round-trips the full account shape instead of fabricating an empty string. `teamName` is now optional. - [P2] Document `ol auth login --read-only` in `SKILL_CONTENT` and regenerate the synced skill file. - [P2] Tests now mock `apiRequest` (not `fetchWithRetry`) when exercising `validateToken`, per `CLAUDE.md`. - [P2] New `auth-command.test.ts` covers the Commander-level surface: asserts the `--base-url` / `--client-id` chained options, the `OUTLINE_OAUTH_CALLBACK_PORT` override, and the `onSuccess` hook in both default and JSON output modes. - [P2] New `auth-pages.test.ts` covers the renderSuccess / renderError output so future edits can't silently change the branded UX or break the html-escaping on the error message. - [P2] `OutlineTokenStore.clear` test now inspects the on-disk config and asserts every auth-related key is removed (and non-auth keys like `update_channel` are preserved). - [P3] Delete `saveConfig` from `lib/auth.ts` — superseded by the token store; remove its lingering mocks in `commands.test.ts` and `empty-output.test.ts`. - [P3] Switch test mocks to `vi.mocked(fn)` instead of forced `as ReturnType<typeof vi.fn>` casts. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Squashed redundant cases in auth-provider / auth-pages / auth-command without losing coverage of the substance: provider authorize / exchange / validate; store round-trip + legacy + clear; chained command flags + env-driven callback port + success-mode silencing in JSON output; HTML rendering + escape. Net 487 -> 266 lines across the three new test files. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
|
🎉 This PR is included in version 1.6.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
src/lib/pkce.ts,oauth.ts,oauth-server.ts, the entire login action incommands/auth.ts) and delegates the flow to@doist/cli-core/auth'srunOAuthFlow+attachLoginCommand. Mirrors twist-cli#215.OutlineAuthProvider(src/lib/auth-provider.ts) owns the outline-specific behaviour:authorize()— builds the/oauth/authorizeURL with cli-core'sgenerateVerifier/deriveChallenge. Resolves--base-url/--client-idfrom flags → env → config → interactive prompt.exchangeCode()— form-urlencoded POST to/oauth/token. Public client — noclient_secret, noAuthorization: Basicheader (unlike twist).validateToken()— hitsauth.infowith the unsaved token via a newapiRequest(path, body, { token, baseUrl })override so we can identify the user before persisting.OutlineTokenStoreadapter maps the cli-core account onto the existing config file (addsauth_user_id/auth_user_nameto round-trip the identity throughstore.active()).src/lib/auth-pages.tsand passed viarenderSuccess/renderError.ol auth logingains--read-only,--callback-port,--json,--ndjsonfor free fromattachLoginCommand;--base-url/--client-idchained onto the returned command.ol auth login --token <token>flag is gone. Login is OAuth-only now. SetOUTLINE_API_TOKENenv var if you need to script with a personal token.Test plan
npm run type-checknpm run lint:checknpm test(115 tests pass)npm run buildnpm run check:skill-syncol auth login --helplists--read-only,--callback-port,--json,--ndjson,--base-url,--client-idol auth logout && ol auth login --base-url https://… --client-id …against a real Outline workspace — branded success page renders, token persisted, terminal prints green "Authenticated to … as …"ol auth statusreports team + userol auth login --jsonemits machine-readable success envelope🤖 Generated with Claude Code