@doist/cli-core 0.19.0 → 0.20.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## [0.20.1](https://github.com/Doist/cli-core/compare/v0.20.0...v0.20.1) (2026-05-21)
2
+
3
+ ### Bug Fixes
4
+
5
+ * **auth:** send DCR HTTP Basic credentials raw, not form-url-encoded ([#43](https://github.com/Doist/cli-core/issues/43)) ([97a626a](https://github.com/Doist/cli-core/commit/97a626a69a52761117e70fccc7049d24ad001937))
6
+
7
+ ## [0.20.0](https://github.com/Doist/cli-core/compare/v0.19.0...v0.20.0) (2026-05-21)
8
+
9
+ ### Features
10
+
11
+ * **auth:** add createDcrProvider for RFC 7591 dynamic client registration ([#31](https://github.com/Doist/cli-core/issues/31)) ([dd34551](https://github.com/Doist/cli-core/commit/dd34551d7f27df83ecd4a0aa888ec079af2007a0))
12
+
1
13
  ## [0.19.0](https://github.com/Doist/cli-core/compare/v0.18.0...v0.19.0) (2026-05-21)
2
14
 
3
15
  ### Features
package/README.md CHANGED
@@ -12,20 +12,20 @@ npm install @doist/cli-core
12
12
 
13
13
  ## What's in it
14
14
 
15
- | Module | Key exports | Purpose |
16
- | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
17
- | `auth` (subpath) | `attachLoginCommand`, `attachLogoutCommand`, `attachStatusCommand`, `attachTokenViewCommand`, `runOAuthFlow`, `refreshAccessToken`, `createPkceProvider`, `createSecureStore`, `createKeyringTokenStore`, `migrateLegacyAuth`, `persistBundle`, `bundleFromExchange`, PKCE helpers, `AuthProvider` / `TokenStore` / `TokenBundle` / `ActiveBundleSnapshot` / `RefreshInput` / `AccountRef` / `SecureStore` / `UserRecordStore` types, `AttachLogoutRevokeContext` | OAuth runtime plus the Commander attachers for `<cli> [auth] login` / `logout` / `status` / `token`. `attachLogoutCommand` accepts an optional `revokeToken` hook for best-effort server-side token revocation. Ships the standard public-client PKCE flow (`createPkceProvider`), a thin cross-platform OS-keyring wrapper (`createSecureStore`), and a multi-account keyring-backed `TokenStore` (`createKeyringTokenStore`) that stores secrets in the OS credential manager and degrades to plaintext in the consumer's config when the keyring is unavailable (WSL/headless Linux/containers). The store contract supports an optional `setBundle(account, bundle)` write method (required on `KeyringTokenStore`) so consumers that need refresh-token persistence can opt in via `TokenBundle`; `active()` stays narrow (access token + account only) so callers that don't need refresh state don't pay extra keyring IPC. `AuthProvider` and `TokenStore` remain the escape hatches for DCR or fully bespoke backends. `logout` / `status` / `token` always attach `--user <ref>` and thread the parsed ref to `store.active(ref)` (and `store.clear(ref)` on `logout`). `commander` (when using the attachers), `open` (browser launch), `@napi-rs/keyring` (when using `createSecureStore` or the keyring `TokenStore`), and `oauth4webapi` (when a consumer opts into silent refresh) are optional peer/optional deps. |
18
- | `commands` (subpath) | `registerChangelogCommand`, `registerUpdateCommand` (+ semver helpers) | Commander wiring for cli-core's standard commands (e.g. `<cli> changelog`, `<cli> update`, `<cli> update switch`). **Requires** `commander` as an optional peer-dep. |
19
- | `config` | `getConfigPath`, `readConfig`, `readConfigStrict`, `writeConfig`, `updateConfig`, `CoreConfig`, `UpdateChannel` | Read / write a per-CLI JSON config file with typed error codes; `CoreConfig` is the shape of fields cli-core itself owns (extend it for per-CLI fields). |
20
- | `empty` | `printEmpty` | Print an empty-state message gated on `--json` / `--ndjson` so machine consumers never see human strings on stdout. |
21
- | `errors` | `CliError` | Typed CLI error class with `code` and exit-code mapping. |
22
- | `global-args` | `parseGlobalArgs`, `stripUserFlag`, `createGlobalArgsStore`, `createAccessibleGate`, `createSpinnerGate`, `getProgressJsonlPath`, `isProgressJsonlEnabled` | Parse well-known global flags (`--json`, `--ndjson`, `--quiet`, `--verbose`, `--accessible`, `--no-spinner`, `--progress-jsonl`, `--user <ref>`) and derive predicates from them. `stripUserFlag` removes `--user` tokens from argv so the cleaned array can be forwarded to Commander when the flag has no root-program attachment. |
23
- | `json` | `formatJson`, `formatNdjson` | Stable JSON / newline-delimited JSON formatting for stdout. |
24
- | `markdown` (subpath) | `preloadMarkdown`, `renderMarkdown`, `TerminalRendererOptions` | Lazy-init terminal markdown renderer. **Requires** `marked` and `marked-terminal-renderer` as peer-deps — install only if your CLI uses this subpath. |
25
- | `options` | `ViewOptions` | Type contract for `{ json?, ndjson? }` per-command options that machine-output gates derive from. |
26
- | `spinner` | `createSpinner` | Loading spinner factory wrapping `yocto-spinner` with disable gates. |
27
- | `terminal` | `isCI`, `isStderrTTY`, `isStdinTTY`, `isStdoutTTY` | TTY / CI detection helpers. |
28
- | `testing` (subpath) | `describeEmptyMachineOutput` | Vitest helpers reusable by consuming CLIs (e.g. parametrised empty-state suite covering `--json` / `--ndjson` / human modes). |
15
+ | Module | Key exports | Purpose |
16
+ | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
17
+ | `auth` (subpath) | `attachLoginCommand`, `attachLogoutCommand`, `attachStatusCommand`, `attachTokenViewCommand`, `runOAuthFlow`, `refreshAccessToken`, `createPkceProvider`, `createDcrProvider`, `createSecureStore`, `createKeyringTokenStore`, `migrateLegacyAuth`, `persistBundle`, `bundleFromExchange`, PKCE helpers, `AuthProvider` / `TokenStore` / `TokenBundle` / `ActiveBundleSnapshot` / `RefreshInput` / `AccountRef` / `SecureStore` / `UserRecordStore` types, `AttachLogoutRevokeContext` | OAuth runtime plus the Commander attachers for `<cli> [auth] login` / `logout` / `status` / `token`. `attachLogoutCommand` accepts an optional `revokeToken` hook for best-effort server-side token revocation. Ships the standard public-client PKCE flow (`createPkceProvider`), the RFC 7591 Dynamic Client Registration flow (`createDcrProvider`), a thin cross-platform OS-keyring wrapper (`createSecureStore`), and a multi-account keyring-backed `TokenStore` (`createKeyringTokenStore`) that stores secrets in the OS credential manager and degrades to plaintext in the consumer's config when the keyring is unavailable (WSL/headless Linux/containers). The store contract supports an optional `setBundle(account, bundle)` write method (required on `KeyringTokenStore`) so consumers that need refresh-token persistence can opt in via `TokenBundle`; `active()` stays narrow (access token + account only) so callers that don't need refresh state don't pay extra keyring IPC. `AuthProvider` and `TokenStore` remain the escape hatches for fully bespoke backends (device code, magic-link, …). `logout` / `status` / `token` always attach `--user <ref>` and thread the parsed ref to `store.active(ref)` (and `store.clear(ref)` on `logout`). `commander` (when using the attachers), `open` (browser launch), `@napi-rs/keyring` (when using `createSecureStore` or the keyring `TokenStore`), and `oauth4webapi` (when a consumer opts into silent refresh or uses `createDcrProvider`) are optional peer/optional deps. |
18
+ | `commands` (subpath) | `registerChangelogCommand`, `registerUpdateCommand` (+ semver helpers) | Commander wiring for cli-core's standard commands (e.g. `<cli> changelog`, `<cli> update`, `<cli> update switch`). **Requires** `commander` as an optional peer-dep. |
19
+ | `config` | `getConfigPath`, `readConfig`, `readConfigStrict`, `writeConfig`, `updateConfig`, `CoreConfig`, `UpdateChannel` | Read / write a per-CLI JSON config file with typed error codes; `CoreConfig` is the shape of fields cli-core itself owns (extend it for per-CLI fields). |
20
+ | `empty` | `printEmpty` | Print an empty-state message gated on `--json` / `--ndjson` so machine consumers never see human strings on stdout. |
21
+ | `errors` | `CliError` | Typed CLI error class with `code` and exit-code mapping. |
22
+ | `global-args` | `parseGlobalArgs`, `stripUserFlag`, `createGlobalArgsStore`, `createAccessibleGate`, `createSpinnerGate`, `getProgressJsonlPath`, `isProgressJsonlEnabled` | Parse well-known global flags (`--json`, `--ndjson`, `--quiet`, `--verbose`, `--accessible`, `--no-spinner`, `--progress-jsonl`, `--user <ref>`) and derive predicates from them. `stripUserFlag` removes `--user` tokens from argv so the cleaned array can be forwarded to Commander when the flag has no root-program attachment. |
23
+ | `json` | `formatJson`, `formatNdjson` | Stable JSON / newline-delimited JSON formatting for stdout. |
24
+ | `markdown` (subpath) | `preloadMarkdown`, `renderMarkdown`, `TerminalRendererOptions` | Lazy-init terminal markdown renderer. **Requires** `marked` and `marked-terminal-renderer` as peer-deps — install only if your CLI uses this subpath. |
25
+ | `options` | `ViewOptions` | Type contract for `{ json?, ndjson? }` per-command options that machine-output gates derive from. |
26
+ | `spinner` | `createSpinner` | Loading spinner factory wrapping `yocto-spinner` with disable gates. |
27
+ | `terminal` | `isCI`, `isStderrTTY`, `isStdinTTY`, `isStdoutTTY` | TTY / CI detection helpers. |
28
+ | `testing` (subpath) | `describeEmptyMachineOutput` | Vitest helpers reusable by consuming CLIs (e.g. parametrised empty-state suite covering `--json` / `--ndjson` / human modes). |
29
29
 
30
30
  ## Usage
31
31
 
@@ -124,7 +124,7 @@ The semver helpers (`parseVersion`, `compareVersions`, `isNewer`, `getInstallTag
124
124
 
125
125
  ### Auth (optional subpath)
126
126
 
127
- Wire `<cli> [auth] login` and the supporting OAuth runtime. cli-core ships the standard public-client PKCE flow (`createPkceProvider`) and the `attachLoginCommand` Commander helper that drives `runOAuthFlow` end-to-end. Bespoke flows (Dynamic Client Registration, device code, magic link, username / password) implement the `AuthProvider` interface directly — no cli-core release needed. Token storage is a `TokenStore` the consumer provides; cli-core does not ship a default.
127
+ Wire `<cli> [auth] login` and the supporting OAuth runtime. cli-core ships the standard public-client PKCE flow (`createPkceProvider`), the RFC 7591 Dynamic Client Registration flow (`createDcrProvider`), and the `attachLoginCommand` Commander helper that drives `runOAuthFlow` end-to-end. Other bespoke flows (device code, magic link, username / password) implement the `AuthProvider` interface directly — no cli-core release needed. Token storage is a `TokenStore` the consumer provides; cli-core does not ship a default.
128
128
 
129
129
  #### Install
130
130
 
@@ -171,6 +171,32 @@ attachLoginCommand<Account>(auth, {
171
171
 
172
172
  The `authorizeUrl` / `tokenUrl` / `clientId` resolvers may return `string` **or** `Promise<string>` — so a consumer can resolve the base URL or client id asynchronously (reading config, prompting the user) without abandoning `createPkceProvider`. An injected `fetchImpl` is used for the token exchange **and** the refresh grant (threaded into `oauth4webapi` via its `customFetch`), so a custom transport — proxy dispatcher, decompression — applies on every OAuth call rather than being bypassed by the library's global `fetch`.
173
173
 
174
+ #### Quick start (Dynamic Client Registration)
175
+
176
+ For providers that issue per-install `client_id` / `client_secret` via [RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591). `createDcrProvider` registers in `prepare()`, then drives the standard PKCE authorize / token-exchange dance against the resulting client. Registration and token exchange run through [`oauth4webapi`](https://github.com/panva/oauth4webapi) (the same optional peer dep PKCE refresh uses — `npm install oauth4webapi`), so endpoints must be HTTPS and the registration endpoint must return RFC 7591-conformant `201` responses.
177
+
178
+ ```ts
179
+ import { attachLoginCommand, createDcrProvider } from '@doist/cli-core/auth'
180
+
181
+ const provider = createDcrProvider<Account>({
182
+ registrationUrl: 'https://example.com/oauth/register',
183
+ authorizeUrl: 'https://example.com/oauth/authorize',
184
+ tokenUrl: 'https://example.com/oauth/token',
185
+ clientMetadata: {
186
+ clientName: 'Example CLI',
187
+ clientUri: 'https://github.com/example/cli',
188
+ logoUri: 'https://example.com/logo.png',
189
+ applicationType: 'native',
190
+ tokenEndpointAuthMethod: 'client_secret_basic', // default
191
+ },
192
+ validate: async ({ token }) => probeUser(token),
193
+ })
194
+ ```
195
+
196
+ The DCR-issued `client_id` (and `client_secret`, if returned) are stashed in the handshake and threaded through the rest of the flow. The server-returned `token_endpoint_auth_method` is authoritative (RFC 7591 §3.2.1) and overrides the configured one. By default the token exchange uses `Authorization: Basic` with each credential component percent-encoded via `encodeURIComponent` (RFC 3986) rather than oauth4webapi's stricter RFC 6749 §2.3.1 form-url-encoding. Both escape genuinely reserved chars (`:` / `%` / `+` / `/`) so a conformant server reconstructs them, but §2.3.1 _also_ escapes the unreserved `-` `_` `.` `~` — which breaks servers that don't url-decode the Basic credential (a DCR-issued `twd_…` would arrive as `twd%5F…` and miss the lookup). Pass `tokenEndpointAuthMethod: 'client_secret_post'` to send credentials in the body instead, or `'none'` for a public-client registration. When the registration response carries no `client_secret`, the exchange falls back to a public-client POST regardless of the requested method. Any extra registration metadata (e.g. `software_statement`) goes in `clientMetadata.extra`. cli-core does **not** cache the registered client — each login mints a fresh one.
197
+
198
+ Both `createPkceProvider` and `createDcrProvider` accept an optional `errorHints: string[]` that is prepended to every `CliError` they throw. Use it for CLI-specific remediation that should accompany every auth failure (e.g. `['Try again: tw auth login', 'Or set TWIST_API_TOKEN environment variable']`). Server-returned response bodies (for non-2xx replies) are appended after the user hints so the actionable hint stays at the top.
199
+
174
200
  #### Sibling attachers (`logout` / `status` / `token`)
175
201
 
176
202
  The same registrar shape covers the other three auth subcommands. Each returns the new `Command` for chaining and shares the same `TokenStore<TAccount>` instance.
@@ -310,7 +336,7 @@ Error contract:
310
336
  - `AUTH_REFRESH_TRANSIENT` — 5xx, network, non-JSON body, lock timeout. Caller may retry.
311
337
  - `AUTH_REFRESH_UNAVAILABLE` — refresh isn't possible in the current setup: no refresh token stored, the store doesn't implement **both** `activeBundle` and `setBundle` (a full bundle must be readable and persistable), the credential was removed mid-refresh, the provider doesn't implement `refreshToken`, or the optional `oauth4webapi` peer dep isn't installed.
312
338
 
313
- The PKCE provider (`createPkceProvider`) implements `refreshToken` via the [`oauth4webapi`](https://github.com/panva/oauth4webapi) library, declared as an **optional peer dependency** — only CLIs that opt into refresh need to install it (`npm install oauth4webapi`). Providers built directly against the `AuthProvider` interface (e.g. DCR, device code) implement the `refreshToken?` hook themselves; the storage and helper contract is identical.
339
+ The PKCE provider (`createPkceProvider`) implements `refreshToken` via the [`oauth4webapi`](https://github.com/panva/oauth4webapi) library, declared as an **optional peer dependency** — only CLIs that opt into refresh or use `createDcrProvider` need to install it (`npm install oauth4webapi`). Providers built directly against the `AuthProvider` interface (e.g. device code) implement the `refreshToken?` hook themselves; the storage and helper contract is identical.
314
340
 
315
341
  #### Keyring primitive (`createSecureStore`)
316
342
 
@@ -447,9 +473,9 @@ Account-selection resolvers (env > `--user` > default > single-only > error), `a
447
473
 
448
474
  A `TokenStore` MAY throw `CliError('AUTH_STORE_READ_FAILED', …)` from `active(ref)` when a matching record exists but the token itself can't be read (e.g. an OS keyring backing the store is offline). `attachLogoutCommand` catches this specific code on the explicit-ref path and proceeds with `clear(ref)` — local logout doesn't need the token, and the `revokeToken` hook is skipped because there's no token to send. Every other error from `active(ref)` (notably `ACCOUNT_NOT_FOUND` from a genuine ref miss, plus any consumer-thrown code) still propagates so a real miss isn't masked. Without `--user`, the logout pre-flight swallows any snapshot read failure so the local clear always runs. `attachStatusCommand` and `attachTokenViewCommand` propagate `AUTH_STORE_READ_FAILED` since they have no way to render or print without the token.
449
475
 
450
- #### Custom `AuthProvider` (non-PKCE flows)
476
+ #### Custom `AuthProvider` (non-PKCE, non-DCR flows)
451
477
 
452
- Implement `AuthProvider` directly for Dynamic Client Registration, device code, magic-link, etc. The four hooks fire in this order during `runOAuthFlow`:
478
+ Implement `AuthProvider` directly for device code, magic-link, username / password, or any other flow not covered by `createPkceProvider` / `createDcrProvider`. The four hooks fire in this order during `runOAuthFlow`:
453
479
 
454
480
  | Hook | When | Purpose |
455
481
  | --------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------- |
@@ -493,6 +519,7 @@ Every failure in this subpath surfaces as a `CliError`:
493
519
  | `AUTH_OAUTH_FAILED` | Provider returned `?error=...`, the flow was aborted via `signal`, or the callback server stopped before completion. |
494
520
  | `AUTH_CALLBACK_TIMEOUT` | No valid callback within `timeoutMs` (default 3 minutes). |
495
521
  | `AUTH_PORT_BIND_FAILED` | Could not bind any port in `[preferredPort, preferredPort + portFallbackCount]`, or `--callback-port` was out of range. |
522
+ | `AUTH_DCR_FAILED` | `createDcrProvider` registration failed (network error, non-`201`, non-JSON body, response missing `client_id`, or the `oauth4webapi` peer dep isn't installed). |
496
523
  | `AUTH_TOKEN_EXCHANGE_FAILED` | Token endpoint network error, non-2xx response, non-JSON body, or missing `access_token`. |
497
524
  | `AUTH_STORE_WRITE_FAILED` | `TokenStore.set` threw a non-`CliError`. (`CliError`s thrown from `set` propagate unchanged.) |
498
525
  | `NOT_AUTHENTICATED` | `status` / `token` ran with an empty `TokenStore` (and no `onNotAuthenticated` callback for `status`). Default message: `'Not signed in.'`. |
@@ -3,5 +3,5 @@
3
3
  * aggregator in `../errors.ts` so consumers don't have to redeclare them in
4
4
  * their own `TCode` union when catching.
5
5
  */
6
- export type AuthErrorCode = 'AUTH_OAUTH_FAILED' | 'AUTH_CALLBACK_TIMEOUT' | 'AUTH_PORT_BIND_FAILED' | 'AUTH_TOKEN_EXCHANGE_FAILED' | 'AUTH_STORE_WRITE_FAILED' | 'AUTH_STORE_READ_FAILED' | 'AUTH_REFRESH_EXPIRED' | 'AUTH_REFRESH_TRANSIENT' | 'AUTH_REFRESH_UNAVAILABLE' | 'NOT_AUTHENTICATED' | 'TOKEN_FROM_ENV' | 'NO_ACCOUNT_SELECTED' | 'ACCOUNT_NOT_FOUND';
6
+ export type AuthErrorCode = 'AUTH_OAUTH_FAILED' | 'AUTH_CALLBACK_TIMEOUT' | 'AUTH_PORT_BIND_FAILED' | 'AUTH_DCR_FAILED' | 'AUTH_TOKEN_EXCHANGE_FAILED' | 'AUTH_STORE_WRITE_FAILED' | 'AUTH_STORE_READ_FAILED' | 'AUTH_REFRESH_EXPIRED' | 'AUTH_REFRESH_TRANSIENT' | 'AUTH_REFRESH_UNAVAILABLE' | 'NOT_AUTHENTICATED' | 'TOKEN_FROM_ENV' | 'NO_ACCOUNT_SELECTED' | 'ACCOUNT_NOT_FOUND';
7
7
  //# sourceMappingURL=errors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/auth/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,MAAM,aAAa,GACnB,mBAAmB,GACnB,uBAAuB,GACvB,uBAAuB,GACvB,4BAA4B,GAC5B,yBAAyB,GACzB,wBAAwB,GACxB,sBAAsB,GACtB,wBAAwB,GACxB,0BAA0B,GAC1B,mBAAmB,GACnB,gBAAgB,GAChB,qBAAqB,GACrB,mBAAmB,CAAA"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/auth/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,MAAM,aAAa,GACnB,mBAAmB,GACnB,uBAAuB,GACvB,uBAAuB,GACvB,iBAAiB,GACjB,4BAA4B,GAC5B,yBAAyB,GACzB,wBAAwB,GACxB,sBAAsB,GACtB,wBAAwB,GACxB,0BAA0B,GAC1B,mBAAmB,GACnB,gBAAgB,GAChB,qBAAqB,GACrB,mBAAmB,CAAA"}
@@ -14,9 +14,11 @@ export type { GenerateVerifierOptions } from './pkce.js';
14
14
  export { bundleFromExchange, persistBundle } from './persist.js';
15
15
  export type { PersistBundleOptions } from './persist.js';
16
16
  export { createPkceProvider } from './providers/pkce.js';
17
- export type { PkceLazyString, PkceProviderOptions } from './providers/pkce.js';
17
+ export type { OAuthLazyString, PkceLazyString, PkceProviderOptions } from './providers/pkce.js';
18
18
  export { refreshAccessToken } from './refresh.js';
19
19
  export type { RefreshAccessTokenOptions, RefreshAccessTokenResult } from './refresh.js';
20
+ export { createDcrProvider } from './providers/dcr.js';
21
+ export type { DcrClientMetadata, DcrProviderOptions, DcrTokenEndpointAuthMethod, } from './providers/dcr.js';
20
22
  export type { AccountRef, ActiveBundleSnapshot, AuthAccount, AuthorizeInput, AuthorizeResult, AuthProvider, ExchangeInput, ExchangeResult, PrepareInput, PrepareResult, RefreshInput, TokenBundle, TokenStore, ValidateInput, } from './types.js';
21
23
  export { SecureStoreUnavailableError, createKeyringTokenStore, createSecureStore, migrateLegacyAuth, } from './keyring/index.js';
22
24
  export type { CreateKeyringTokenStoreOptions, CreateSecureStoreOptions, KeyringTokenStore, MigrateAuthResult, MigrateLegacyAuthOptions, MigrateSkipReason, SecureStore, TokenStorageLocation, TokenStorageResult, UserRecord, UserRecordStore, } from './keyring/index.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AACxC,YAAY,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAA;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAC/C,YAAY,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAC/E,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AACjD,YAAY,EACR,0BAA0B,EAC1B,mBAAmB,EACnB,yBAAyB,GAC5B,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AACjD,YAAY,EAAE,0BAA0B,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AAClF,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AACxD,YAAY,EAAE,6BAA6B,EAAE,MAAM,iBAAiB,CAAA;AACpE,OAAO,EACH,yBAAyB,EACzB,eAAe,EACf,aAAa,EACb,gBAAgB,GACnB,MAAM,WAAW,CAAA;AAClB,YAAY,EAAE,uBAAuB,EAAE,MAAM,WAAW,CAAA;AACxD,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAChE,YAAY,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AACxD,YAAY,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AAC9E,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AACjD,YAAY,EAAE,yBAAyB,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAA;AACvF,YAAY,EACR,UAAU,EACV,oBAAoB,EACpB,WAAW,EACX,cAAc,EACd,eAAe,EACf,YAAY,EACZ,aAAa,EACb,cAAc,EACd,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,WAAW,EACX,UAAU,EACV,aAAa,GAChB,MAAM,YAAY,CAAA;AACnB,OAAO,EACH,2BAA2B,EAC3B,uBAAuB,EACvB,iBAAiB,EACjB,iBAAiB,GACpB,MAAM,oBAAoB,CAAA;AAC3B,YAAY,EACR,8BAA8B,EAC9B,wBAAwB,EACxB,iBAAiB,EACjB,iBAAiB,EACjB,wBAAwB,EACxB,iBAAiB,EACjB,WAAW,EACX,oBAAoB,EACpB,kBAAkB,EAClB,UAAU,EACV,eAAe,GAClB,MAAM,oBAAoB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AACxC,YAAY,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAA;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAC/C,YAAY,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAC/E,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AACjD,YAAY,EACR,0BAA0B,EAC1B,mBAAmB,EACnB,yBAAyB,GAC5B,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AACjD,YAAY,EAAE,0BAA0B,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AAClF,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AACxD,YAAY,EAAE,6BAA6B,EAAE,MAAM,iBAAiB,CAAA;AACpE,OAAO,EACH,yBAAyB,EACzB,eAAe,EACf,aAAa,EACb,gBAAgB,GACnB,MAAM,WAAW,CAAA;AAClB,YAAY,EAAE,uBAAuB,EAAE,MAAM,WAAW,CAAA;AACxD,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAChE,YAAY,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AACxD,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAA;AAC/F,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AACjD,YAAY,EAAE,yBAAyB,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAA;AACvF,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AACtD,YAAY,EACR,iBAAiB,EACjB,kBAAkB,EAClB,0BAA0B,GAC7B,MAAM,oBAAoB,CAAA;AAC3B,YAAY,EACR,UAAU,EACV,oBAAoB,EACpB,WAAW,EACX,cAAc,EACd,eAAe,EACf,YAAY,EACZ,aAAa,EACb,cAAc,EACd,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,WAAW,EACX,UAAU,EACV,aAAa,GAChB,MAAM,YAAY,CAAA;AACnB,OAAO,EACH,2BAA2B,EAC3B,uBAAuB,EACvB,iBAAiB,EACjB,iBAAiB,GACpB,MAAM,oBAAoB,CAAA;AAC3B,YAAY,EACR,8BAA8B,EAC9B,wBAAwB,EACxB,iBAAiB,EACjB,iBAAiB,EACjB,wBAAwB,EACxB,iBAAiB,EACjB,WAAW,EACX,oBAAoB,EACpB,kBAAkB,EAClB,UAAU,EACV,eAAe,GAClB,MAAM,oBAAoB,CAAA"}
@@ -7,5 +7,6 @@ export { DEFAULT_VERIFIER_ALPHABET, deriveChallenge, generateState, generateVeri
7
7
  export { bundleFromExchange, persistBundle } from './persist.js';
8
8
  export { createPkceProvider } from './providers/pkce.js';
9
9
  export { refreshAccessToken } from './refresh.js';
10
+ export { createDcrProvider } from './providers/dcr.js';
10
11
  export { SecureStoreUnavailableError, createKeyringTokenStore, createSecureStore, migrateLegacyAuth, } from './keyring/index.js';
11
12
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAExC,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAE/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AAMjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AAEjD,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AAExD,OAAO,EACH,yBAAyB,EACzB,eAAe,EACf,aAAa,EACb,gBAAgB,GACnB,MAAM,WAAW,CAAA;AAElB,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAEhE,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAExD,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAkBjD,OAAO,EACH,2BAA2B,EAC3B,uBAAuB,EACvB,iBAAiB,EACjB,iBAAiB,GACpB,MAAM,oBAAoB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAExC,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAE/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AAMjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AAEjD,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AAExD,OAAO,EACH,yBAAyB,EACzB,eAAe,EACf,aAAa,EACb,gBAAgB,GACnB,MAAM,WAAW,CAAA;AAElB,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAEhE,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAExD,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAEjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAsBtD,OAAO,EACH,2BAA2B,EAC3B,uBAAuB,EACvB,iBAAiB,EACjB,iBAAiB,GACpB,MAAM,oBAAoB,CAAA"}
@@ -0,0 +1,72 @@
1
+ import type { AuthAccount, AuthProvider, ValidateInput } from '../types.js';
2
+ import type { OAuthLazyString } from './pkce.js';
3
+ export type DcrTokenEndpointAuthMethod = 'client_secret_basic' | 'client_secret_post' | 'none';
4
+ /**
5
+ * RFC 7591 Dynamic Client Registration metadata POSTed to the registration
6
+ * endpoint. Only fields the CLI typically cares about are named; pass anything
7
+ * else (`software_statement`, `jwks`, …) via `extra`.
8
+ */
9
+ export type DcrClientMetadata = {
10
+ clientName: string;
11
+ clientUri?: string;
12
+ logoUri?: string;
13
+ applicationType?: 'native' | 'web';
14
+ /**
15
+ * Requested token-endpoint auth method. Defaults to `'client_secret_basic'`.
16
+ * The registration response is authoritative per RFC 7591 §3.2.1 — when
17
+ * the server returns its own `token_endpoint_auth_method`, that value
18
+ * wins over this configured one.
19
+ */
20
+ tokenEndpointAuthMethod?: DcrTokenEndpointAuthMethod;
21
+ /** Defaults to `['authorization_code']`. */
22
+ grantTypes?: string[];
23
+ /** Defaults to `['code']`. */
24
+ responseTypes?: string[];
25
+ /** Merged verbatim into the registration POST body. */
26
+ extra?: Record<string, unknown>;
27
+ };
28
+ export type DcrProviderOptions<TAccount extends AuthAccount = AuthAccount> = {
29
+ /** RFC 7591 registration endpoint. Function form supports per-flow base URLs. */
30
+ registrationUrl: OAuthLazyString;
31
+ /** OAuth 2.0 authorize endpoint. */
32
+ authorizeUrl: OAuthLazyString;
33
+ /** OAuth 2.0 token endpoint. */
34
+ tokenUrl: OAuthLazyString;
35
+ clientMetadata: DcrClientMetadata;
36
+ /** How to join scopes in the authorize URL. Default `' '` (RFC 6749). */
37
+ scopeSeparator?: string;
38
+ verifierAlphabet?: string;
39
+ /** Default 64. */
40
+ verifierLength?: number;
41
+ /** Probe an authenticated endpoint to confirm the token works and resolve the account. */
42
+ validate: (input: ValidateInput) => Promise<TAccount>;
43
+ /**
44
+ * User-facing remediation hints attached to every CliError this factory
45
+ * throws (`AUTH_DCR_FAILED` from `prepare()` / `authorize()` and
46
+ * `AUTH_TOKEN_EXCHANGE_FAILED` from `exchangeCode()`). Server-returned
47
+ * error details are appended after these so the actionable hint stays
48
+ * first.
49
+ */
50
+ errorHints?: string[];
51
+ /** Inject a fetch implementation (tests / custom transport). */
52
+ fetchImpl?: typeof fetch;
53
+ };
54
+ /**
55
+ * Build an `AuthProvider` for the RFC 7591 Dynamic Client Registration flow,
56
+ * driven by [`oauth4webapi`](https://github.com/panva/oauth4webapi) (an
57
+ * optional peer dep — installed only by DCR/refresh consumers).
58
+ *
59
+ * - `prepare`: register via `dynamicClientRegistrationRequest`. Stash the
60
+ * issued `client_id`, optional `client_secret`, and the server-returned
61
+ * `token_endpoint_auth_method` (RFC 7591 §3.2.1 — server is authoritative)
62
+ * in the handshake.
63
+ * - `authorize`: standard PKCE S256 with `client_id` read from the handshake.
64
+ * - `exchangeCode`: `authorizationCodeGrantRequest` authenticated per the
65
+ * handshake's server-returned auth method (falling back to the configured
66
+ * one) — HTTP Basic (RFC 3986-encoded, see `clientSecretBasicRfc3986`),
67
+ * client-secret POST, or public-client `None` (the last also when the
68
+ * registration response carried no `client_secret`).
69
+ * - `validateToken`: caller-supplied.
70
+ */
71
+ export declare function createDcrProvider<TAccount extends AuthAccount>(options: DcrProviderOptions<TAccount>): AuthProvider<TAccount>;
72
+ //# sourceMappingURL=dcr.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dcr.d.ts","sourceRoot":"","sources":["../../../src/auth/providers/dcr.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACR,WAAW,EACX,YAAY,EAOZ,aAAa,EAChB,MAAM,aAAa,CAAA;AAQpB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAEhD,MAAM,MAAM,0BAA0B,GAAG,qBAAqB,GAAG,oBAAoB,GAAG,MAAM,CAAA;AAE9F;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC5B,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,eAAe,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAA;IAClC;;;;;OAKG;IACH,uBAAuB,CAAC,EAAE,0BAA0B,CAAA;IACpD,4CAA4C;IAC5C,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;IACrB,8BAA8B;IAC9B,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAClC,CAAA;AAED,MAAM,MAAM,kBAAkB,CAAC,QAAQ,SAAS,WAAW,GAAG,WAAW,IAAI;IACzE,iFAAiF;IACjF,eAAe,EAAE,eAAe,CAAA;IAChC,oCAAoC;IACpC,YAAY,EAAE,eAAe,CAAA;IAC7B,gCAAgC;IAChC,QAAQ,EAAE,eAAe,CAAA;IACzB,cAAc,EAAE,iBAAiB,CAAA;IACjC,yEAAyE;IACzE,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,kBAAkB;IAClB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,0FAA0F;IAC1F,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;IACrD;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;IACrB,gEAAgE;IAChE,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;CAC3B,CAAA;AAUD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,SAAS,WAAW,EAC1D,OAAO,EAAE,kBAAkB,CAAC,QAAQ,CAAC,GACtC,YAAY,CAAC,QAAQ,CAAC,CAkLxB"}
@@ -0,0 +1,204 @@
1
+ import { getErrorMessage } from '../../errors.js';
2
+ import { deriveChallenge, generateVerifier } from '../pkce.js';
3
+ import { buildAuthError, buildPkceAuthorizeUrl, expiresAtFromExpiresIn, loadOauth4webapi, resolve, } from './oauth.js';
4
+ const MISSING_PEER_HINTS = ['Run `npm install oauth4webapi` in your CLI.'];
5
+ const VALID_AUTH_METHODS = new Set([
6
+ 'client_secret_basic',
7
+ 'client_secret_post',
8
+ 'none',
9
+ ]);
10
+ /**
11
+ * Build an `AuthProvider` for the RFC 7591 Dynamic Client Registration flow,
12
+ * driven by [`oauth4webapi`](https://github.com/panva/oauth4webapi) (an
13
+ * optional peer dep — installed only by DCR/refresh consumers).
14
+ *
15
+ * - `prepare`: register via `dynamicClientRegistrationRequest`. Stash the
16
+ * issued `client_id`, optional `client_secret`, and the server-returned
17
+ * `token_endpoint_auth_method` (RFC 7591 §3.2.1 — server is authoritative)
18
+ * in the handshake.
19
+ * - `authorize`: standard PKCE S256 with `client_id` read from the handshake.
20
+ * - `exchangeCode`: `authorizationCodeGrantRequest` authenticated per the
21
+ * handshake's server-returned auth method (falling back to the configured
22
+ * one) — HTTP Basic (RFC 3986-encoded, see `clientSecretBasicRfc3986`),
23
+ * client-secret POST, or public-client `None` (the last also when the
24
+ * registration response carried no `client_secret`).
25
+ * - `validateToken`: caller-supplied.
26
+ */
27
+ export function createDcrProvider(options) {
28
+ const scopeSeparator = options.scopeSeparator ?? ' ';
29
+ const configuredAuthMethod = options.clientMetadata.tokenEndpointAuthMethod ?? 'client_secret_basic';
30
+ return {
31
+ async prepare(input) {
32
+ const oauth = await loadOauth4webapi({
33
+ code: 'AUTH_DCR_FAILED',
34
+ missingMessage: 'oauth4webapi is required for Dynamic Client Registration.',
35
+ userHints: options.errorHints,
36
+ missingHints: MISSING_PEER_HINTS,
37
+ });
38
+ const registrationUrl = await resolve(options.registrationUrl, {}, input.flags);
39
+ const as = {
40
+ issuer: registrationUrl,
41
+ registration_endpoint: registrationUrl,
42
+ };
43
+ const metadata = buildRegistrationMetadata(options.clientMetadata, input.redirectUri, configuredAuthMethod);
44
+ let client;
45
+ try {
46
+ const response = await oauth.dynamicClientRegistrationRequest(as, metadata, customFetchOptions(oauth, options.fetchImpl));
47
+ client = await oauth.processDynamicClientRegistrationResponse(response);
48
+ }
49
+ catch (error) {
50
+ throw mapOauthError(error, oauth, 'AUTH_DCR_FAILED', 'Dynamic Client Registration failed.', options.errorHints);
51
+ }
52
+ const handshake = { clientId: client.client_id };
53
+ if (typeof client.client_secret === 'string') {
54
+ handshake.clientSecret = client.client_secret;
55
+ }
56
+ // Per RFC 7591 §3.2.1 the server's chosen method is authoritative.
57
+ // Honour a supported one; fail fast on a method we can't perform
58
+ // (e.g. `private_key_jwt`) rather than silently authenticating the
59
+ // token request with the wrong scheme.
60
+ const serverMethod = client.token_endpoint_auth_method;
61
+ if (typeof serverMethod === 'string') {
62
+ if (!VALID_AUTH_METHODS.has(serverMethod)) {
63
+ throw buildAuthError('AUTH_DCR_FAILED', `Registration server selected an unsupported token_endpoint_auth_method: ${serverMethod}.`, options.errorHints);
64
+ }
65
+ handshake.tokenEndpointAuthMethod = serverMethod;
66
+ }
67
+ return { handshake };
68
+ },
69
+ async authorize(input) {
70
+ const clientId = input.handshake.clientId;
71
+ if (typeof clientId !== 'string') {
72
+ throw buildAuthError('AUTH_DCR_FAILED', 'Internal: DCR handshake missing clientId before authorize.', options.errorHints);
73
+ }
74
+ const verifier = generateVerifier({
75
+ alphabet: options.verifierAlphabet,
76
+ length: options.verifierLength,
77
+ });
78
+ const challenge = deriveChallenge(verifier);
79
+ const authorizeUrl = buildPkceAuthorizeUrl({
80
+ authorizeUrl: await resolve(options.authorizeUrl, input.handshake, input.flags),
81
+ clientId,
82
+ redirectUri: input.redirectUri,
83
+ state: input.state,
84
+ scopes: input.scopes,
85
+ scopeSeparator,
86
+ codeChallenge: challenge,
87
+ });
88
+ return {
89
+ authorizeUrl,
90
+ handshake: { ...input.handshake, codeVerifier: verifier },
91
+ };
92
+ },
93
+ async exchangeCode(input) {
94
+ const verifier = input.handshake.codeVerifier;
95
+ const clientId = input.handshake.clientId;
96
+ if (typeof verifier !== 'string' || typeof clientId !== 'string') {
97
+ throw buildAuthError('AUTH_TOKEN_EXCHANGE_FAILED', 'Internal: DCR handshake state lost between authorize and exchange.', options.errorHints);
98
+ }
99
+ const clientSecretRaw = input.handshake.clientSecret;
100
+ const clientSecret = typeof clientSecretRaw === 'string' ? clientSecretRaw : undefined;
101
+ const issuedMethodRaw = input.handshake.tokenEndpointAuthMethod;
102
+ const issuedMethod = typeof issuedMethodRaw === 'string' &&
103
+ VALID_AUTH_METHODS.has(issuedMethodRaw)
104
+ ? issuedMethodRaw
105
+ : undefined;
106
+ // Server-issued method wins (RFC 7591 §3.2.1). Fall back to the
107
+ // configured one only when the server didn't echo a known method.
108
+ const effectiveAuthMethod = issuedMethod ?? configuredAuthMethod;
109
+ const oauth = await loadOauth4webapi({
110
+ code: 'AUTH_TOKEN_EXCHANGE_FAILED',
111
+ missingMessage: 'oauth4webapi is required for the DCR token exchange.',
112
+ userHints: options.errorHints,
113
+ missingHints: MISSING_PEER_HINTS,
114
+ });
115
+ const flags = input.handshake.flags ?? {};
116
+ const tokenUrl = await resolve(options.tokenUrl, input.handshake, flags);
117
+ const as = { issuer: tokenUrl, token_endpoint: tokenUrl };
118
+ const client = { client_id: clientId };
119
+ // Public-client fallback: a registration with no `client_secret`
120
+ // can't authenticate Basic/Post regardless of the requested method,
121
+ // so we POST `client_id` like a non-confidential client. Otherwise
122
+ // honour the effective auth method.
123
+ let clientAuth;
124
+ if (!clientSecret || effectiveAuthMethod === 'none') {
125
+ clientAuth = oauth.None();
126
+ }
127
+ else if (effectiveAuthMethod === 'client_secret_post') {
128
+ clientAuth = oauth.ClientSecretPost(clientSecret);
129
+ }
130
+ else {
131
+ clientAuth = clientSecretBasicRfc3986(clientSecret);
132
+ }
133
+ try {
134
+ // The flow runtime owns CSRF state validation; skip oauth4webapi's
135
+ // own state check (it only brands the params for the grant call).
136
+ const callbackParameters = oauth.validateAuthResponse(as, client, new URLSearchParams({ code: input.code }), oauth.skipStateCheck);
137
+ const response = await oauth.authorizationCodeGrantRequest(as, client, clientAuth, callbackParameters, input.redirectUri, verifier, customFetchOptions(oauth, options.fetchImpl));
138
+ const result = await oauth.processAuthorizationCodeResponse(as, client, response);
139
+ return {
140
+ accessToken: result.access_token,
141
+ refreshToken: result.refresh_token,
142
+ expiresAt: expiresAtFromExpiresIn(result.expires_in),
143
+ };
144
+ }
145
+ catch (error) {
146
+ throw mapOauthError(error, oauth, 'AUTH_TOKEN_EXCHANGE_FAILED', 'Token exchange failed.', options.errorHints);
147
+ }
148
+ },
149
+ validateToken: options.validate,
150
+ };
151
+ }
152
+ /**
153
+ * HTTP Basic client auth that percent-encodes each credential component with
154
+ * `encodeURIComponent` (RFC 3986) rather than oauth4webapi's stricter RFC 6749
155
+ * §2.3.1 `application/x-www-form-urlencoded` form. Both escape the genuinely
156
+ * reserved chars (`:` `%` `+` `/` …) so a conformant server reconstructs them —
157
+ * but §2.3.1 *also* escapes the unreserved `-` `_` `.` `~`, which breaks servers
158
+ * that don't url-decode the Basic credential (a DCR-issued `twd_…` would arrive
159
+ * as `twd%5F…` and miss the lookup). Leaving those unreserved chars intact keeps
160
+ * such servers working while still transmitting reserved chars safely.
161
+ */
162
+ function clientSecretBasicRfc3986(clientSecret) {
163
+ return (_as, client, _body, headers) => {
164
+ const credentials = Buffer.from(`${encodeURIComponent(client.client_id)}:${encodeURIComponent(clientSecret)}`, 'utf8').toString('base64');
165
+ headers.set('authorization', `Basic ${credentials}`);
166
+ };
167
+ }
168
+ /** Thread an injected `fetchImpl` into oauth4webapi via its `customFetch` symbol. */
169
+ function customFetchOptions(oauth, fetchImpl) {
170
+ return fetchImpl ? { [oauth.customFetch]: fetchImpl } : undefined;
171
+ }
172
+ /**
173
+ * Translate an oauth4webapi failure into a typed `CliError`. A `ResponseBodyError`
174
+ * carries the server's OAuth error JSON (`error` / `error_description`) — surface
175
+ * it so a misconfigured server is diagnosable. Everything else (non-conform
176
+ * status, non-JSON body, network failure) collapses to the raw message.
177
+ */
178
+ function mapOauthError(error, oauth, code, message, hints) {
179
+ if (error instanceof oauth.ResponseBodyError) {
180
+ const detail = error.error_description
181
+ ? `${error.error} (${error.error_description})`
182
+ : error.error;
183
+ return buildAuthError(code, message, hints, detail);
184
+ }
185
+ return buildAuthError(code, message, hints, getErrorMessage(error));
186
+ }
187
+ function buildRegistrationMetadata(metadata, redirectUri, tokenEndpointAuthMethod) {
188
+ const body = {
189
+ ...metadata.extra,
190
+ client_name: metadata.clientName,
191
+ redirect_uris: [redirectUri],
192
+ grant_types: metadata.grantTypes ?? ['authorization_code'],
193
+ response_types: metadata.responseTypes ?? ['code'],
194
+ token_endpoint_auth_method: tokenEndpointAuthMethod,
195
+ };
196
+ if (metadata.clientUri)
197
+ body.client_uri = metadata.clientUri;
198
+ if (metadata.logoUri)
199
+ body.logo_uri = metadata.logoUri;
200
+ if (metadata.applicationType)
201
+ body.application_type = metadata.applicationType;
202
+ return body;
203
+ }
204
+ //# sourceMappingURL=dcr.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dcr.js","sourceRoot":"","sources":["../../../src/auth/providers/dcr.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,105 @@
1
+ import { CliError } from '../../errors.js';
2
+ import type { AuthErrorCode } from '../errors.js';
3
+ import type { OAuthLazyString } from './pkce.js';
4
+ /**
5
+ * Build a `CliError` with user-supplied `errorHints` prepended and an optional
6
+ * server-derived `extra` detail appended. Centralises the "user-actionable
7
+ * first, diagnostic second" ordering used everywhere in this directory.
8
+ */
9
+ export declare function buildAuthError(code: AuthErrorCode, message: string, userHints: string[] | undefined, extra?: string): CliError;
10
+ /**
11
+ * Resolve a literal-or-function endpoint/clientId against the current handshake
12
+ * and runtime flags. Used by every provider in this directory.
13
+ */
14
+ export declare function resolve(resolver: OAuthLazyString, handshake: Record<string, unknown>, flags: Record<string, unknown>): Promise<string>;
15
+ /** Read a response body without letting a stream error escape — used for hints. */
16
+ export declare function safeReadText(response: Response): Promise<string | undefined>;
17
+ type BuildPkceAuthorizeUrlInput = {
18
+ authorizeUrl: string;
19
+ clientId: string;
20
+ redirectUri: string;
21
+ state: string;
22
+ scopes: string[];
23
+ scopeSeparator: string;
24
+ codeChallenge: string;
25
+ };
26
+ /** Construct the standard PKCE S256 authorize URL. */
27
+ export declare function buildPkceAuthorizeUrl(input: BuildPkceAuthorizeUrlInput): string;
28
+ type PostAndParseJsonInput = {
29
+ url: string;
30
+ headers: Record<string, string>;
31
+ /** Pre-encoded request body. */
32
+ body: string;
33
+ /** Error code wrapped around every failure mode. */
34
+ errorCode: AuthErrorCode;
35
+ /** Prefix for error messages, e.g. `'Token endpoint'` or `'Registration endpoint'`. */
36
+ errorLabel: string;
37
+ errorHints?: string[];
38
+ fetchImpl: typeof fetch;
39
+ };
40
+ /**
41
+ * POST a request, parse a JSON response, and wrap every failure mode as a
42
+ * typed `CliError`. Common backbone for the OAuth token endpoint and the
43
+ * RFC 7591 dynamic-client-registration endpoint — both POST a body, both
44
+ * expect a JSON reply, both want uniform error handling.
45
+ *
46
+ * Throws `errorCode` with the configured hints on:
47
+ * - network failure (fetch rejection)
48
+ * - non-2xx response (body text appended as a hint after `errorHints`)
49
+ * - non-JSON 2xx body (a misconfigured proxy returning HTML, etc.)
50
+ *
51
+ * Success-shape validation (e.g. `access_token` present) is the caller's
52
+ * job, because it differs per endpoint.
53
+ */
54
+ export declare function postAndParseJson<T>(input: PostAndParseJsonInput): Promise<T>;
55
+ type PostTokenEndpointInput = {
56
+ url: string;
57
+ /** Form-encoded body. Caller owns grant_type + grant-specific params. */
58
+ body: URLSearchParams;
59
+ /**
60
+ * User-facing remediation hints attached to every `CliError` this helper
61
+ * throws (network failure, non-2xx, parse failure, missing access_token).
62
+ * The server-returned response body (for non-2xx) is appended after these
63
+ * so user hints stay at the top.
64
+ */
65
+ errorHints?: string[];
66
+ fetchImpl: typeof fetch;
67
+ };
68
+ type PostTokenEndpointResult = {
69
+ accessToken: string;
70
+ refreshToken?: string;
71
+ /** Unix-epoch ms. Computed from `expires_in` when the server returns it. */
72
+ expiresAt?: number;
73
+ };
74
+ /**
75
+ * POST to an OAuth 2.0 token endpoint and parse the standard JSON response.
76
+ * Covers the public-client `authorization_code` exchange (PKCE) — the caller
77
+ * owns `grant_type` and the grant-specific params via `body`.
78
+ *
79
+ * Failures uniformly throw `CliError('AUTH_TOKEN_EXCHANGE_FAILED', …)`:
80
+ * network errors, non-2xx responses (with body text as a hint), non-JSON
81
+ * bodies, and responses missing `access_token`.
82
+ */
83
+ export declare function postTokenEndpoint(input: PostTokenEndpointInput): Promise<PostTokenEndpointResult>;
84
+ /** Convert an OAuth `expires_in` (seconds from now) into a Unix-epoch ms deadline. */
85
+ export declare function expiresAtFromExpiresIn(expiresIn: number | undefined): number | undefined;
86
+ type LoadOauthOptions = {
87
+ /** Error code wrapped around a missing/broken peer dep. */
88
+ code: AuthErrorCode;
89
+ /** Message when the peer dep isn't installed. */
90
+ missingMessage: string;
91
+ /** Caller-supplied remediation hints (e.g. provider `errorHints`), prepended first. */
92
+ userHints?: string[];
93
+ /** Install hint for the missing-peer case, appended after `userHints`. */
94
+ missingHints?: string[];
95
+ };
96
+ /**
97
+ * Lazily import `oauth4webapi`, surfacing a typed `CliError` when the optional
98
+ * peer dep is absent (vs. installed-but-broken). Shared by `createPkceProvider`
99
+ * (refresh) and `createDcrProvider` (registration + token exchange). Caller
100
+ * `userHints` are prepended on both failure branches so the provider's
101
+ * `errorHints` contract holds even when the dep is missing.
102
+ */
103
+ export declare function loadOauth4webapi(options: LoadOauthOptions): Promise<typeof import('oauth4webapi')>;
104
+ export {};
105
+ //# sourceMappingURL=oauth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../../../src/auth/providers/oauth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,145 @@
1
+ import { CliError, getErrorMessage } from '../../errors.js';
2
+ /**
3
+ * Build a `CliError` with user-supplied `errorHints` prepended and an optional
4
+ * server-derived `extra` detail appended. Centralises the "user-actionable
5
+ * first, diagnostic second" ordering used everywhere in this directory.
6
+ */
7
+ export function buildAuthError(code, message, userHints, extra) {
8
+ const hints = [...(userHints ?? []), ...(extra ? [extra] : [])];
9
+ return new CliError(code, message, hints.length > 0 ? { hints } : {});
10
+ }
11
+ /**
12
+ * Resolve a literal-or-function endpoint/clientId against the current handshake
13
+ * and runtime flags. Used by every provider in this directory.
14
+ */
15
+ export async function resolve(resolver, handshake, flags) {
16
+ return typeof resolver === 'function' ? resolver({ handshake, flags }) : resolver;
17
+ }
18
+ /** Read a response body without letting a stream error escape — used for hints. */
19
+ export async function safeReadText(response) {
20
+ try {
21
+ const text = (await response.text()).trim();
22
+ return text.length > 0 ? text : undefined;
23
+ }
24
+ catch {
25
+ return undefined;
26
+ }
27
+ }
28
+ /** Construct the standard PKCE S256 authorize URL. */
29
+ export function buildPkceAuthorizeUrl(input) {
30
+ const url = new URL(input.authorizeUrl);
31
+ url.searchParams.set('response_type', 'code');
32
+ url.searchParams.set('client_id', input.clientId);
33
+ url.searchParams.set('redirect_uri', input.redirectUri);
34
+ url.searchParams.set('state', input.state);
35
+ url.searchParams.set('code_challenge', input.codeChallenge);
36
+ url.searchParams.set('code_challenge_method', 'S256');
37
+ if (input.scopes.length > 0) {
38
+ url.searchParams.set('scope', input.scopes.join(input.scopeSeparator));
39
+ }
40
+ return url.toString();
41
+ }
42
+ /**
43
+ * POST a request, parse a JSON response, and wrap every failure mode as a
44
+ * typed `CliError`. Common backbone for the OAuth token endpoint and the
45
+ * RFC 7591 dynamic-client-registration endpoint — both POST a body, both
46
+ * expect a JSON reply, both want uniform error handling.
47
+ *
48
+ * Throws `errorCode` with the configured hints on:
49
+ * - network failure (fetch rejection)
50
+ * - non-2xx response (body text appended as a hint after `errorHints`)
51
+ * - non-JSON 2xx body (a misconfigured proxy returning HTML, etc.)
52
+ *
53
+ * Success-shape validation (e.g. `access_token` present) is the caller's
54
+ * job, because it differs per endpoint.
55
+ */
56
+ export async function postAndParseJson(input) {
57
+ const fail = (message, extra) => buildAuthError(input.errorCode, message, input.errorHints, extra);
58
+ let response;
59
+ try {
60
+ response = await input.fetchImpl(input.url, {
61
+ method: 'POST',
62
+ headers: input.headers,
63
+ body: input.body,
64
+ });
65
+ }
66
+ catch (error) {
67
+ throw fail(`${input.errorLabel} request failed: ${getErrorMessage(error)}`);
68
+ }
69
+ if (!response.ok) {
70
+ const detail = await safeReadText(response);
71
+ throw fail(`${input.errorLabel} returned HTTP ${response.status}.`, detail);
72
+ }
73
+ // Parse defensively — a misconfigured proxy can return a 2xx HTML error
74
+ // page that would otherwise blow up with a raw SyntaxError.
75
+ try {
76
+ return (await response.json());
77
+ }
78
+ catch (error) {
79
+ throw fail(`${input.errorLabel} returned non-JSON response: ${getErrorMessage(error)}`);
80
+ }
81
+ }
82
+ /**
83
+ * POST to an OAuth 2.0 token endpoint and parse the standard JSON response.
84
+ * Covers the public-client `authorization_code` exchange (PKCE) — the caller
85
+ * owns `grant_type` and the grant-specific params via `body`.
86
+ *
87
+ * Failures uniformly throw `CliError('AUTH_TOKEN_EXCHANGE_FAILED', …)`:
88
+ * network errors, non-2xx responses (with body text as a hint), non-JSON
89
+ * bodies, and responses missing `access_token`.
90
+ */
91
+ export async function postTokenEndpoint(input) {
92
+ const headers = {
93
+ 'Content-Type': 'application/x-www-form-urlencoded',
94
+ Accept: 'application/json',
95
+ };
96
+ const payload = await postAndParseJson({
97
+ url: input.url,
98
+ headers,
99
+ body: input.body.toString(),
100
+ errorCode: 'AUTH_TOKEN_EXCHANGE_FAILED',
101
+ errorLabel: 'Token endpoint',
102
+ errorHints: input.errorHints,
103
+ fetchImpl: input.fetchImpl,
104
+ });
105
+ if (!payload.access_token) {
106
+ throw buildAuthError('AUTH_TOKEN_EXCHANGE_FAILED', 'Token endpoint response missing access_token.', input.errorHints);
107
+ }
108
+ return {
109
+ accessToken: payload.access_token,
110
+ refreshToken: payload.refresh_token,
111
+ expiresAt: expiresAtFromExpiresIn(payload.expires_in),
112
+ };
113
+ }
114
+ /** Convert an OAuth `expires_in` (seconds from now) into a Unix-epoch ms deadline. */
115
+ export function expiresAtFromExpiresIn(expiresIn) {
116
+ return typeof expiresIn === 'number' ? Date.now() + expiresIn * 1000 : undefined;
117
+ }
118
+ // Optional peer dep — only DCR and refresh consumers install it. The dynamic
119
+ // import (and a missing-peer failure) is memoised so it isn't repeated on every
120
+ // call that sits on the authenticated-call path.
121
+ let oauthModulePromise;
122
+ /**
123
+ * Lazily import `oauth4webapi`, surfacing a typed `CliError` when the optional
124
+ * peer dep is absent (vs. installed-but-broken). Shared by `createPkceProvider`
125
+ * (refresh) and `createDcrProvider` (registration + token exchange). Caller
126
+ * `userHints` are prepended on both failure branches so the provider's
127
+ * `errorHints` contract holds even when the dep is missing.
128
+ */
129
+ export async function loadOauth4webapi(options) {
130
+ oauthModulePromise ??= import('oauth4webapi');
131
+ try {
132
+ return await oauthModulePromise;
133
+ }
134
+ catch (error) {
135
+ const moduleCode = error?.code;
136
+ if (moduleCode === 'ERR_MODULE_NOT_FOUND' || moduleCode === 'MODULE_NOT_FOUND') {
137
+ const hints = [...(options.userHints ?? []), ...(options.missingHints ?? [])];
138
+ throw new CliError(options.code, options.missingMessage, hints.length > 0 ? { hints } : {});
139
+ }
140
+ // Installed but failed to initialise — surface the real cause rather
141
+ // than a misleading "install it" hint.
142
+ throw buildAuthError(options.code, `Failed to load oauth4webapi: ${getErrorMessage(error)}`, options.userHints);
143
+ }
144
+ }
145
+ //# sourceMappingURL=oauth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.js","sourceRoot":"","sources":["../../../src/auth/providers/oauth.ts"],"names":[],"mappings":"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"}
@@ -1,13 +1,17 @@
1
1
  import type { AuthAccount, AuthProvider, ValidateInput } from '../types.js';
2
2
  /**
3
3
  * Lazy resolver: a literal string, or a function that builds one from the
4
- * current PKCE handshake (so callers can derive the URL or client_id from
5
- * the active session's `baseUrl` / per-flow flags).
4
+ * current OAuth handshake (so callers can derive the URL or client_id from
5
+ * the active session's `baseUrl` / per-flow flags). Used by both
6
+ * `createPkceProvider` and `createDcrProvider`; prefer the grant-agnostic
7
+ * alias `OAuthLazyString` for new code.
6
8
  */
7
9
  export type PkceLazyString = string | ((ctx: {
8
10
  handshake: Record<string, unknown>;
9
11
  flags: Record<string, unknown>;
10
12
  }) => string | Promise<string>);
13
+ /** Grant-agnostic alias for {@link PkceLazyString}. Identical type. */
14
+ export type OAuthLazyString = PkceLazyString;
11
15
  export type PkceProviderOptions<TAccount extends AuthAccount = AuthAccount> = {
12
16
  /** OAuth 2.0 authorize endpoint. Function form supports per-flow base URLs (Outline self-hosted). */
13
17
  authorizeUrl: PkceLazyString;
@@ -22,6 +26,13 @@ export type PkceProviderOptions<TAccount extends AuthAccount = AuthAccount> = {
22
26
  verifierLength?: number;
23
27
  /** Probe an authenticated endpoint to confirm the token works and resolve the account. */
24
28
  validate: (input: ValidateInput) => Promise<TAccount>;
29
+ /**
30
+ * User-facing remediation hints attached to every CliError this factory
31
+ * throws (token-endpoint failures, internal handshake-state guards).
32
+ * Server-returned response bodies are appended after these so the
33
+ * actionable hint stays first.
34
+ */
35
+ errorHints?: string[];
25
36
  /** Inject a fetch implementation (tests). */
26
37
  fetchImpl?: typeof fetch;
27
38
  };
@@ -35,8 +46,8 @@ export type PkceProviderOptions<TAccount extends AuthAccount = AuthAccount> = {
35
46
  * `runOAuthFlow` and arrives on `AuthorizeInput.scopes`; this factory does
36
47
  * not own scope resolution.
37
48
  *
38
- * Flows that need DCR or HTTP Basic auth on the token endpoint implement
39
- * the `AuthProvider` interface directly.
49
+ * Flows that need DCR or HTTP Basic auth on the token endpoint use
50
+ * `createDcrProvider` (or implement the `AuthProvider` interface directly).
40
51
  */
41
52
  export declare function createPkceProvider<TAccount extends AuthAccount>(options: PkceProviderOptions<TAccount>): AuthProvider<TAccount>;
42
53
  //# sourceMappingURL=pkce.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pkce.d.ts","sourceRoot":"","sources":["../../../src/auth/providers/pkce.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACR,WAAW,EACX,YAAY,EAMZ,aAAa,EAChB,MAAM,aAAa,CAAA;AAWpB;;;;GAIG;AACH,MAAM,MAAM,cAAc,GACpB,MAAM,GACN,CAAC,CAAC,GAAG,EAAE;IACH,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAClC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACjC,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAA;AAErC,MAAM,MAAM,mBAAmB,CAAC,QAAQ,SAAS,WAAW,GAAG,WAAW,IAAI;IAC1E,qGAAqG;IACrG,YAAY,EAAE,cAAc,CAAA;IAC5B,2EAA2E;IAC3E,QAAQ,EAAE,cAAc,CAAA;IACxB,mFAAmF;IACnF,QAAQ,EAAE,cAAc,CAAA;IACxB,iGAAiG;IACjG,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,kBAAkB;IAClB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,0FAA0F;IAC1F,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;IACrD,6CAA6C;IAC7C,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;CAC3B,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,SAAS,WAAW,EAC3D,OAAO,EAAE,mBAAmB,CAAC,QAAQ,CAAC,GACvC,YAAY,CAAC,QAAQ,CAAC,CAsLxB"}
1
+ {"version":3,"file":"pkce.d.ts","sourceRoot":"","sources":["../../../src/auth/providers/pkce.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACR,WAAW,EACX,YAAY,EAMZ,aAAa,EAChB,MAAM,aAAa,CAAA;AAepB;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GACpB,MAAM,GACN,CAAC,CAAC,GAAG,EAAE;IACH,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAClC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACjC,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAA;AAErC,uEAAuE;AACvE,MAAM,MAAM,eAAe,GAAG,cAAc,CAAA;AAE5C,MAAM,MAAM,mBAAmB,CAAC,QAAQ,SAAS,WAAW,GAAG,WAAW,IAAI;IAC1E,qGAAqG;IACrG,YAAY,EAAE,cAAc,CAAA;IAC5B,2EAA2E;IAC3E,QAAQ,EAAE,cAAc,CAAA;IACxB,mFAAmF;IACnF,QAAQ,EAAE,cAAc,CAAA;IACxB,iGAAiG;IACjG,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,kBAAkB;IAClB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,0FAA0F;IAC1F,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;IACrD;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;IACrB,6CAA6C;IAC7C,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;CAC3B,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,SAAS,WAAW,EAC3D,OAAO,EAAE,mBAAmB,CAAC,QAAQ,CAAC,GACvC,YAAY,CAAC,QAAQ,CAAC,CAqJxB"}
@@ -1,12 +1,10 @@
1
1
  import { CliError, getErrorMessage } from '../../errors.js';
2
2
  import { deriveChallenge, generateVerifier } from '../pkce.js';
3
+ import { buildAuthError, buildPkceAuthorizeUrl, expiresAtFromExpiresIn, loadOauth4webapi, postTokenEndpoint, resolve, } from './oauth.js';
3
4
  // Upper bound on the refresh-token POST. Kept under the refresh helper's
4
5
  // stale-lock threshold so a timed-out grant releases the lock before another
5
6
  // invocation would consider it abandoned.
6
7
  const REFRESH_TIMEOUT_MS = 10_000;
7
- function expiresAtFromExpiresIn(expiresIn) {
8
- return typeof expiresIn === 'number' ? Date.now() + expiresIn * 1000 : undefined;
9
- }
10
8
  /**
11
9
  * Build an `AuthProvider` for the standard "PKCE S256, public client (no
12
10
  * client_secret)" flow. Covers Outline (user-supplied client_id + base_url)
@@ -17,8 +15,8 @@ function expiresAtFromExpiresIn(expiresIn) {
17
15
  * `runOAuthFlow` and arrives on `AuthorizeInput.scopes`; this factory does
18
16
  * not own scope resolution.
19
17
  *
20
- * Flows that need DCR or HTTP Basic auth on the token endpoint implement
21
- * the `AuthProvider` interface directly.
18
+ * Flows that need DCR or HTTP Basic auth on the token endpoint use
19
+ * `createDcrProvider` (or implement the `AuthProvider` interface directly).
22
20
  */
23
21
  export function createPkceProvider(options) {
24
22
  const fetchImpl = options.fetchImpl ?? fetch;
@@ -30,22 +28,22 @@ export function createPkceProvider(options) {
30
28
  length: options.verifierLength,
31
29
  });
32
30
  const challenge = deriveChallenge(verifier);
33
- const [clientId, authorizeUrl] = await Promise.all([
31
+ // Resolve concurrently both may be async (config read / prompt).
32
+ const [clientId, authorizeBaseUrl] = await Promise.all([
34
33
  resolve(options.clientId, input.handshake, input.flags),
35
34
  resolve(options.authorizeUrl, input.handshake, input.flags),
36
35
  ]);
37
- const url = new URL(authorizeUrl);
38
- url.searchParams.set('response_type', 'code');
39
- url.searchParams.set('client_id', clientId);
40
- url.searchParams.set('redirect_uri', input.redirectUri);
41
- url.searchParams.set('state', input.state);
42
- url.searchParams.set('code_challenge', challenge);
43
- url.searchParams.set('code_challenge_method', 'S256');
44
- if (input.scopes.length > 0) {
45
- url.searchParams.set('scope', input.scopes.join(scopeSeparator));
46
- }
36
+ const authorizeUrl = buildPkceAuthorizeUrl({
37
+ authorizeUrl: authorizeBaseUrl,
38
+ clientId,
39
+ redirectUri: input.redirectUri,
40
+ state: input.state,
41
+ scopes: input.scopes,
42
+ scopeSeparator,
43
+ codeChallenge: challenge,
44
+ });
47
45
  return {
48
- authorizeUrl: url.toString(),
46
+ authorizeUrl,
49
47
  handshake: { ...input.handshake, codeVerifier: verifier, clientId },
50
48
  };
51
49
  },
@@ -53,7 +51,7 @@ export function createPkceProvider(options) {
53
51
  const verifier = input.handshake.codeVerifier;
54
52
  const clientId = input.handshake.clientId;
55
53
  if (typeof verifier !== 'string' || typeof clientId !== 'string') {
56
- throw new CliError('AUTH_TOKEN_EXCHANGE_FAILED', 'Internal: PKCE handshake state lost between authorize and exchange.');
54
+ throw buildAuthError('AUTH_TOKEN_EXCHANGE_FAILED', 'Internal: PKCE handshake state lost between authorize and exchange.', options.errorHints);
57
55
  }
58
56
  // `runOAuthFlow` folds the runtime `flags` into the handshake
59
57
  // before calling exchange, so a `tokenUrl: ({ flags }) => ...`
@@ -67,45 +65,25 @@ export function createPkceProvider(options) {
67
65
  client_id: clientId,
68
66
  code_verifier: verifier,
69
67
  });
70
- let response;
71
- try {
72
- response = await fetchImpl(tokenUrl, {
73
- method: 'POST',
74
- headers: {
75
- 'Content-Type': 'application/x-www-form-urlencoded',
76
- Accept: 'application/json',
77
- },
78
- body: body.toString(),
79
- });
80
- }
81
- catch (error) {
82
- throw new CliError('AUTH_TOKEN_EXCHANGE_FAILED', `Token endpoint request failed: ${getErrorMessage(error)}`);
83
- }
84
- if (!response.ok) {
85
- const detail = await safeReadText(response);
86
- throw new CliError('AUTH_TOKEN_EXCHANGE_FAILED', `Token endpoint returned HTTP ${response.status}.`, detail ? { hints: [detail] } : {});
87
- }
88
- // Parse defensively — a misconfigured proxy can return a 2xx HTML
89
- // error page that would otherwise blow up with a raw SyntaxError.
90
- let payload;
91
- try {
92
- payload = (await response.json());
93
- }
94
- catch (error) {
95
- throw new CliError('AUTH_TOKEN_EXCHANGE_FAILED', `Token endpoint returned non-JSON response: ${getErrorMessage(error)}`);
96
- }
97
- if (!payload.access_token) {
98
- throw new CliError('AUTH_TOKEN_EXCHANGE_FAILED', 'Token endpoint response missing access_token.');
99
- }
68
+ const result = await postTokenEndpoint({
69
+ url: tokenUrl,
70
+ body,
71
+ errorHints: options.errorHints,
72
+ fetchImpl,
73
+ });
100
74
  return {
101
- accessToken: payload.access_token,
102
- refreshToken: payload.refresh_token,
103
- expiresAt: expiresAtFromExpiresIn(payload.expires_in),
75
+ accessToken: result.accessToken,
76
+ refreshToken: result.refreshToken,
77
+ expiresAt: result.expiresAt,
104
78
  };
105
79
  },
106
80
  validateToken: options.validate,
107
81
  async refreshToken(input) {
108
- const oauth = await loadOauth4webapi();
82
+ const oauth = await loadOauth4webapi({
83
+ code: 'AUTH_REFRESH_UNAVAILABLE',
84
+ missingMessage: 'oauth4webapi is required for refresh-token support.',
85
+ missingHints: ['Run `npm install oauth4webapi` in your CLI.'],
86
+ });
109
87
  // Mirror `exchangeCode`: a resolver that reads `flags` sees the
110
88
  // same view during silent refresh as it did at authorize time.
111
89
  const flags = input.handshake.flags ?? {};
@@ -162,35 +140,4 @@ export function createPkceProvider(options) {
162
140
  },
163
141
  };
164
142
  }
165
- async function resolve(resolver, handshake, flags) {
166
- return typeof resolver === 'function' ? resolver({ handshake, flags }) : resolver;
167
- }
168
- // Optional peer dep — only refresh consumers install it. The dynamic import
169
- // (and a missing-peer failure) is memoised so it isn't repeated on every
170
- // refresh, which sits on the authenticated-call path.
171
- let oauthModulePromise;
172
- async function loadOauth4webapi() {
173
- oauthModulePromise ??= import('oauth4webapi');
174
- try {
175
- return await oauthModulePromise;
176
- }
177
- catch (error) {
178
- const code = error?.code;
179
- if (code === 'ERR_MODULE_NOT_FOUND' || code === 'MODULE_NOT_FOUND') {
180
- throw new CliError('AUTH_REFRESH_UNAVAILABLE', 'oauth4webapi is required for refresh-token support.', { hints: ['Run `npm install oauth4webapi` in your CLI.'] });
181
- }
182
- // Installed but failed to initialise — surface the real cause rather
183
- // than a misleading "install it" hint.
184
- throw new CliError('AUTH_REFRESH_UNAVAILABLE', `Failed to load oauth4webapi: ${getErrorMessage(error)}`);
185
- }
186
- }
187
- async function safeReadText(response) {
188
- try {
189
- const text = (await response.text()).trim();
190
- return text.length > 0 ? text : undefined;
191
- }
192
- catch {
193
- return undefined;
194
- }
195
- }
196
143
  //# sourceMappingURL=pkce.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pkce.js","sourceRoot":"","sources":["../../../src/auth/providers/pkce.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pkce.js","sourceRoot":"","sources":["../../../src/auth/providers/pkce.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doist/cli-core",
3
- "version": "0.19.0",
3
+ "version": "0.20.1",
4
4
  "description": "Shared core utilities for Doist CLI projects",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",