@doist/cli-core 0.18.0 → 0.20.0
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 +12 -0
- package/README.md +63 -20
- package/dist/auth/errors.d.ts +1 -1
- package/dist/auth/errors.d.ts.map +1 -1
- package/dist/auth/flow.d.ts.map +1 -1
- package/dist/auth/flow.js +7 -8
- package/dist/auth/flow.js.map +1 -1
- package/dist/auth/index.d.ts +7 -3
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +3 -1
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/keyring/internal.d.ts +25 -0
- package/dist/auth/keyring/internal.d.ts.map +1 -1
- package/dist/auth/keyring/internal.js +24 -7
- package/dist/auth/keyring/internal.js.map +1 -1
- package/dist/auth/keyring/token-store.d.ts +7 -1
- package/dist/auth/keyring/token-store.d.ts.map +1 -1
- package/dist/auth/keyring/token-store.js +59 -20
- package/dist/auth/keyring/token-store.js.map +1 -1
- package/dist/auth/persist.d.ts +9 -1
- package/dist/auth/persist.d.ts.map +1 -1
- package/dist/auth/persist.js +20 -0
- package/dist/auth/persist.js.map +1 -1
- package/dist/auth/providers/dcr.d.ts +71 -0
- package/dist/auth/providers/dcr.d.ts.map +1 -0
- package/dist/auth/providers/dcr.js +187 -0
- package/dist/auth/providers/dcr.js.map +1 -0
- package/dist/auth/providers/oauth.d.ts +105 -0
- package/dist/auth/providers/oauth.d.ts.map +1 -0
- package/dist/auth/providers/oauth.js +145 -0
- package/dist/auth/providers/oauth.js.map +1 -0
- package/dist/auth/providers/pkce.d.ts +16 -5
- package/dist/auth/providers/pkce.d.ts.map +1 -1
- package/dist/auth/providers/pkce.js +92 -63
- package/dist/auth/providers/pkce.js.map +1 -1
- package/dist/auth/refresh.d.ts +49 -0
- package/dist/auth/refresh.d.ts.map +1 -0
- package/dist/auth/refresh.js +184 -0
- package/dist/auth/refresh.js.map +1 -0
- package/dist/auth/status.d.ts +12 -4
- package/dist/auth/status.d.ts.map +1 -1
- package/dist/auth/status.js +45 -5
- package/dist/auth/status.js.map +1 -1
- package/dist/auth/types.d.ts +17 -0
- package/dist/auth/types.d.ts.map +1 -1
- package/package.json +9 -4
|
@@ -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":"AAAA,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAI3D;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC1B,IAAmB,EACnB,OAAe,EACf,SAA+B,EAC/B,KAAc;IAEd,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;IAC/D,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;AACzE,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CACzB,QAAyB,EACzB,SAAkC,EAClC,KAA8B;IAE9B,OAAO,OAAO,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAA;AACrF,CAAC;AAED,mFAAmF;AACnF,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,QAAkB;IACjD,IAAI,CAAC;QACD,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;QAC3C,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAA;IAC7C,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,SAAS,CAAA;IACpB,CAAC;AACL,CAAC;AAYD,sDAAsD;AACtD,MAAM,UAAU,qBAAqB,CAAC,KAAiC;IACnE,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;IACvC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,eAAe,EAAE,MAAM,CAAC,CAAA;IAC7C,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAA;IACjD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,cAAc,EAAE,KAAK,CAAC,WAAW,CAAC,CAAA;IACvD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;IAC1C,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,gBAAgB,EAAE,KAAK,CAAC,aAAa,CAAC,CAAA;IAC3D,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,uBAAuB,EAAE,MAAM,CAAC,CAAA;IACrD,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAA;IAC1E,CAAC;IACD,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAA;AACzB,CAAC;AAeD;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAI,KAA4B;IAClE,MAAM,IAAI,GAAG,CAAC,OAAe,EAAE,KAAc,EAAY,EAAE,CACvD,cAAc,CAAC,KAAK,CAAC,SAAS,EAAE,OAAO,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,CAAA;IAErE,IAAI,QAAkB,CAAA;IACtB,IAAI,CAAC;QACD,QAAQ,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,EAAE;YACxC,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,IAAI,EAAE,KAAK,CAAC,IAAI;SACnB,CAAC,CAAA;IACN,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,MAAM,IAAI,CAAC,GAAG,KAAK,CAAC,UAAU,oBAAoB,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;IAC/E,CAAC;IAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACf,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC,CAAA;QAC3C,MAAM,IAAI,CAAC,GAAG,KAAK,CAAC,UAAU,kBAAkB,QAAQ,CAAC,MAAM,GAAG,EAAE,MAAM,CAAC,CAAA;IAC/E,CAAC;IAED,wEAAwE;IACxE,4DAA4D;IAC5D,IAAI,CAAC;QACD,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAM,CAAA;IACvC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,MAAM,IAAI,CAAC,GAAG,KAAK,CAAC,UAAU,gCAAgC,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;IAC3F,CAAC;AACL,CAAC;AAuBD;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACnC,KAA6B;IAE7B,MAAM,OAAO,GAA2B;QACpC,cAAc,EAAE,mCAAmC;QACnD,MAAM,EAAE,kBAAkB;KAC7B,CAAA;IAED,MAAM,OAAO,GAAG,MAAM,gBAAgB,CAInC;QACC,GAAG,EAAE,KAAK,CAAC,GAAG;QACd,OAAO;QACP,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE;QAC3B,SAAS,EAAE,4BAA4B;QACvC,UAAU,EAAE,gBAAgB;QAC5B,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,SAAS,EAAE,KAAK,CAAC,SAAS;KAC7B,CAAC,CAAA;IACF,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC;QACxB,MAAM,cAAc,CAChB,4BAA4B,EAC5B,+CAA+C,EAC/C,KAAK,CAAC,UAAU,CACnB,CAAA;IACL,CAAC;IACD,OAAO;QACH,WAAW,EAAE,OAAO,CAAC,YAAY;QACjC,YAAY,EAAE,OAAO,CAAC,aAAa;QACnC,SAAS,EAAE,sBAAsB,CAAC,OAAO,CAAC,UAAU,CAAC;KACxD,CAAA;AACL,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,sBAAsB,CAAC,SAA6B;IAChE,OAAO,OAAO,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC,SAAS,CAAA;AACpF,CAAC;AAED,6EAA6E;AAC7E,gFAAgF;AAChF,iDAAiD;AACjD,IAAI,kBAAsE,CAAA;AAa1E;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAClC,OAAyB;IAEzB,kBAAkB,KAAK,MAAM,CAAC,cAAc,CAAC,CAAA;IAC7C,IAAI,CAAC;QACD,OAAO,MAAM,kBAAkB,CAAA;IACnC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,MAAM,UAAU,GAAI,KAA2C,EAAE,IAAI,CAAA;QACrE,IAAI,UAAU,KAAK,sBAAsB,IAAI,UAAU,KAAK,kBAAkB,EAAE,CAAC;YAC7E,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,CAAA;YAC7E,MAAM,IAAI,QAAQ,CACd,OAAO,CAAC,IAAI,EACZ,OAAO,CAAC,cAAc,EACtB,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CACpC,CAAA;QACL,CAAC;QACD,qEAAqE;QACrE,uCAAuC;QACvC,MAAM,cAAc,CAChB,OAAO,CAAC,IAAI,EACZ,gCAAgC,eAAe,CAAC,KAAK,CAAC,EAAE,EACxD,OAAO,CAAC,SAAS,CACpB,CAAA;IACL,CAAC;AACL,CAAC"}
|
|
@@ -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
|
|
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
|
-
}) => string);
|
|
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
|
|
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":"
|
|
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,5 +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';
|
|
4
|
+
// Upper bound on the refresh-token POST. Kept under the refresh helper's
|
|
5
|
+
// stale-lock threshold so a timed-out grant releases the lock before another
|
|
6
|
+
// invocation would consider it abandoned.
|
|
7
|
+
const REFRESH_TIMEOUT_MS = 10_000;
|
|
3
8
|
/**
|
|
4
9
|
* Build an `AuthProvider` for the standard "PKCE S256, public client (no
|
|
5
10
|
* client_secret)" flow. Covers Outline (user-supplied client_id + base_url)
|
|
@@ -10,8 +15,8 @@ import { deriveChallenge, generateVerifier } from '../pkce.js';
|
|
|
10
15
|
* `runOAuthFlow` and arrives on `AuthorizeInput.scopes`; this factory does
|
|
11
16
|
* not own scope resolution.
|
|
12
17
|
*
|
|
13
|
-
* Flows that need DCR or HTTP Basic auth on the token endpoint
|
|
14
|
-
* 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).
|
|
15
20
|
*/
|
|
16
21
|
export function createPkceProvider(options) {
|
|
17
22
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
@@ -23,20 +28,22 @@ export function createPkceProvider(options) {
|
|
|
23
28
|
length: options.verifierLength,
|
|
24
29
|
});
|
|
25
30
|
const challenge = deriveChallenge(verifier);
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
31
|
+
// Resolve concurrently — both may be async (config read / prompt).
|
|
32
|
+
const [clientId, authorizeBaseUrl] = await Promise.all([
|
|
33
|
+
resolve(options.clientId, input.handshake, input.flags),
|
|
34
|
+
resolve(options.authorizeUrl, input.handshake, input.flags),
|
|
35
|
+
]);
|
|
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
|
+
});
|
|
38
45
|
return {
|
|
39
|
-
authorizeUrl
|
|
46
|
+
authorizeUrl,
|
|
40
47
|
handshake: { ...input.handshake, codeVerifier: verifier, clientId },
|
|
41
48
|
};
|
|
42
49
|
},
|
|
@@ -44,13 +51,13 @@ export function createPkceProvider(options) {
|
|
|
44
51
|
const verifier = input.handshake.codeVerifier;
|
|
45
52
|
const clientId = input.handshake.clientId;
|
|
46
53
|
if (typeof verifier !== 'string' || typeof clientId !== 'string') {
|
|
47
|
-
throw
|
|
54
|
+
throw buildAuthError('AUTH_TOKEN_EXCHANGE_FAILED', 'Internal: PKCE handshake state lost between authorize and exchange.', options.errorHints);
|
|
48
55
|
}
|
|
49
56
|
// `runOAuthFlow` folds the runtime `flags` into the handshake
|
|
50
57
|
// before calling exchange, so a `tokenUrl: ({ flags }) => ...`
|
|
51
58
|
// resolver sees the same flags it saw during authorize.
|
|
52
59
|
const flags = input.handshake.flags ?? {};
|
|
53
|
-
const tokenUrl = resolve(options.tokenUrl, input.handshake, flags);
|
|
60
|
+
const tokenUrl = await resolve(options.tokenUrl, input.handshake, flags);
|
|
54
61
|
const body = new URLSearchParams({
|
|
55
62
|
grant_type: 'authorization_code',
|
|
56
63
|
code: input.code,
|
|
@@ -58,57 +65,79 @@ export function createPkceProvider(options) {
|
|
|
58
65
|
client_id: clientId,
|
|
59
66
|
code_verifier: verifier,
|
|
60
67
|
});
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
//
|
|
81
|
-
|
|
68
|
+
const result = await postTokenEndpoint({
|
|
69
|
+
url: tokenUrl,
|
|
70
|
+
body,
|
|
71
|
+
errorHints: options.errorHints,
|
|
72
|
+
fetchImpl,
|
|
73
|
+
});
|
|
74
|
+
return {
|
|
75
|
+
accessToken: result.accessToken,
|
|
76
|
+
refreshToken: result.refreshToken,
|
|
77
|
+
expiresAt: result.expiresAt,
|
|
78
|
+
};
|
|
79
|
+
},
|
|
80
|
+
validateToken: options.validate,
|
|
81
|
+
async refreshToken(input) {
|
|
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
|
+
});
|
|
87
|
+
// Mirror `exchangeCode`: a resolver that reads `flags` sees the
|
|
88
|
+
// same view during silent refresh as it did at authorize time.
|
|
89
|
+
const flags = input.handshake.flags ?? {};
|
|
90
|
+
const [tokenUrl, clientId] = await Promise.all([
|
|
91
|
+
resolve(options.tokenUrl, input.handshake, flags),
|
|
92
|
+
resolve(options.clientId, input.handshake, flags),
|
|
93
|
+
]);
|
|
94
|
+
const as = { issuer: tokenUrl, token_endpoint: tokenUrl };
|
|
95
|
+
const client = { client_id: clientId, token_endpoint_auth_method: 'none' };
|
|
96
|
+
// Bound the network call so a hung token endpoint can't block the
|
|
97
|
+
// CLI indefinitely (and, for refresh consumers, can't hold the
|
|
98
|
+
// refresh lock forever). Route through the consumer's injected
|
|
99
|
+
// fetch when present, so a custom transport (proxy dispatcher,
|
|
100
|
+
// decompression) applies to the refresh grant too — oauth4webapi
|
|
101
|
+
// otherwise captures the global `fetch`.
|
|
102
|
+
const requestOptions = {
|
|
103
|
+
signal: AbortSignal.timeout(REFRESH_TIMEOUT_MS),
|
|
104
|
+
...(options.fetchImpl ? { [oauth.customFetch]: options.fetchImpl } : {}),
|
|
105
|
+
};
|
|
82
106
|
try {
|
|
83
|
-
|
|
107
|
+
const response = await oauth.refreshTokenGrantRequest(as, client, oauth.None(), input.refreshToken, requestOptions);
|
|
108
|
+
const result = await oauth.processRefreshTokenResponse(as, client, response);
|
|
109
|
+
return {
|
|
110
|
+
accessToken: result.access_token,
|
|
111
|
+
refreshToken: result.refresh_token,
|
|
112
|
+
expiresAt: expiresAtFromExpiresIn(result.expires_in),
|
|
113
|
+
};
|
|
84
114
|
}
|
|
85
115
|
catch (error) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
116
|
+
// A ResponseBodyError carries the server's OAuth error JSON.
|
|
117
|
+
// `invalid_grant` (any status — some proxies remap 400 → 401)
|
|
118
|
+
// means the refresh token itself was rejected; re-login is the
|
|
119
|
+
// only recovery. Every other code is transient from cli-core's
|
|
120
|
+
// POV — but surface the actual `error`/`error_description` so a
|
|
121
|
+
// misconfigured server (e.g. `invalid_request: Missing
|
|
122
|
+
// client_secret`) is diagnosable rather than hidden behind
|
|
123
|
+
// oauth4webapi's generic "server responded with an error".
|
|
124
|
+
if (error instanceof oauth.ResponseBodyError) {
|
|
125
|
+
const detail = error.error_description
|
|
126
|
+
? `${error.error} (${error.error_description})`
|
|
127
|
+
: error.error;
|
|
128
|
+
if (error.error === 'invalid_grant') {
|
|
129
|
+
throw new CliError('AUTH_REFRESH_EXPIRED', `Refresh token rejected: ${detail}`, {
|
|
130
|
+
hints: ['Re-run the login command to reauthorize.'],
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
throw new CliError('AUTH_REFRESH_TRANSIENT', `Refresh request failed: ${detail}`, {
|
|
134
|
+
hints: ['Try again.'],
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
// Network failure, non-JSON body, WWWAuthenticateChallengeError, …
|
|
138
|
+
throw new CliError('AUTH_REFRESH_TRANSIENT', `Refresh request failed: ${getErrorMessage(error)}`, { hints: ['Try again.'] });
|
|
90
139
|
}
|
|
91
|
-
return {
|
|
92
|
-
accessToken: payload.access_token,
|
|
93
|
-
refreshToken: payload.refresh_token,
|
|
94
|
-
expiresAt: typeof payload.expires_in === 'number'
|
|
95
|
-
? Date.now() + payload.expires_in * 1000
|
|
96
|
-
: undefined,
|
|
97
|
-
};
|
|
98
140
|
},
|
|
99
|
-
validateToken: options.validate,
|
|
100
141
|
};
|
|
101
142
|
}
|
|
102
|
-
function resolve(resolver, handshake, flags) {
|
|
103
|
-
return typeof resolver === 'function' ? resolver({ handshake, flags }) : resolver;
|
|
104
|
-
}
|
|
105
|
-
async function safeReadText(response) {
|
|
106
|
-
try {
|
|
107
|
-
const text = (await response.text()).trim();
|
|
108
|
-
return text.length > 0 ? text : undefined;
|
|
109
|
-
}
|
|
110
|
-
catch {
|
|
111
|
-
return undefined;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
143
|
//# sourceMappingURL=pkce.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pkce.js","sourceRoot":"","sources":["../../../src/auth/providers/pkce.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"pkce.js","sourceRoot":"","sources":["../../../src/auth/providers/pkce.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { AccountRef, AuthAccount, AuthProvider, TokenBundle, TokenStore } from './types.js';
|
|
2
|
+
export type RefreshAccessTokenOptions<TAccount extends AuthAccount> = {
|
|
3
|
+
store: TokenStore<TAccount>;
|
|
4
|
+
provider: AuthProvider<TAccount>;
|
|
5
|
+
ref?: AccountRef;
|
|
6
|
+
/**
|
|
7
|
+
* Refresh proactively when the access token's expiry is within this many
|
|
8
|
+
* ms of now. Default 60_000 (60s). Ignored when `force: true`.
|
|
9
|
+
*/
|
|
10
|
+
skewMs?: number;
|
|
11
|
+
/**
|
|
12
|
+
* Reactive path: caller hit a 401 and wants a rotation regardless of
|
|
13
|
+
* expiry. Skips the skew check; still honours all the "unavailable"
|
|
14
|
+
* gates (no refresh token, no provider hook, no `activeBundle`/`setBundle`).
|
|
15
|
+
*/
|
|
16
|
+
force?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Path to the O_EXCL concurrency lock file. Required — cli-core does not
|
|
19
|
+
* interpret `~` or know where the consumer's config lives. Recommended:
|
|
20
|
+
* `${getConfigPath(serviceName)}.refresh.lock`.
|
|
21
|
+
*/
|
|
22
|
+
lockPath: string;
|
|
23
|
+
/**
|
|
24
|
+
* Forwarded to `provider.refreshToken` as its `handshake`, so consumers
|
|
25
|
+
* can pass runtime context the provider's resolvers need (e.g. a
|
|
26
|
+
* `--env`-derived base URL / client id). Defaults to `{}`.
|
|
27
|
+
*/
|
|
28
|
+
handshake?: Record<string, unknown>;
|
|
29
|
+
};
|
|
30
|
+
export type RefreshAccessTokenResult<TAccount extends AuthAccount> = {
|
|
31
|
+
rotated: boolean;
|
|
32
|
+
bundle: TokenBundle;
|
|
33
|
+
account: TAccount;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Rotate the access token using the stored refresh token. Proactive when
|
|
37
|
+
* `accessTokenExpiresAt` is within `skewMs` of now; reactive when `force:
|
|
38
|
+
* true`. Uses an `O_EXCL` file lock at `lockPath` so concurrent CLI
|
|
39
|
+
* invocations don't issue parallel refresh-token grants — one POSTs, the
|
|
40
|
+
* others re-read the rotated bundle from the store.
|
|
41
|
+
*
|
|
42
|
+
* Throws `AUTH_REFRESH_UNAVAILABLE` when refresh isn't possible in the
|
|
43
|
+
* current setup: store doesn't implement `activeBundle` + `setBundle`,
|
|
44
|
+
* provider doesn't implement `refreshToken`, no credential, or no refresh
|
|
45
|
+
* token. Server-side rejections surface as `AUTH_REFRESH_EXPIRED` (re-login
|
|
46
|
+
* required) or `AUTH_REFRESH_TRANSIENT` (retryable).
|
|
47
|
+
*/
|
|
48
|
+
export declare function refreshAccessToken<TAccount extends AuthAccount>(options: RefreshAccessTokenOptions<TAccount>): Promise<RefreshAccessTokenResult<TAccount>>;
|
|
49
|
+
//# sourceMappingURL=refresh.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refresh.d.ts","sourceRoot":"","sources":["../../src/auth/refresh.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAEhG,MAAM,MAAM,yBAAyB,CAAC,QAAQ,SAAS,WAAW,IAAI;IAClE,KAAK,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAA;IAC3B,QAAQ,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAA;IAChC,GAAG,CAAC,EAAE,UAAU,CAAA;IAChB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAA;IACf;;;;OAIG;IACH,QAAQ,EAAE,MAAM,CAAA;IAChB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACtC,CAAA;AAED,MAAM,MAAM,wBAAwB,CAAC,QAAQ,SAAS,WAAW,IAAI;IACjE,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,EAAE,WAAW,CAAA;IACnB,OAAO,EAAE,QAAQ,CAAA;CACpB,CAAA;AAUD;;;;;;;;;;;;GAYG;AACH,wBAAsB,kBAAkB,CAAC,QAAQ,SAAS,WAAW,EACjE,OAAO,EAAE,yBAAyB,CAAC,QAAQ,CAAC,GAC7C,OAAO,CAAC,wBAAwB,CAAC,QAAQ,CAAC,CAAC,CA4F7C"}
|