@indigoai-us/hq-cli 5.109.2 → 5.109.3
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
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.109.3] — 2026-09-09
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- A provider that refuses the sign-in request is no longer reported as you
|
|
10
|
+
declining it. `hq integrations connect` treated every `?error=` on the
|
|
11
|
+
callback as "Sign-in was declined", so a scope the provider will not grant,
|
|
12
|
+
a misconfigured client, or a provider outage all read as a choice you made —
|
|
13
|
+
and, because declines are treated as expected, none of them reached error
|
|
14
|
+
reporting. Only `access_denied` is a decline now; anything else reports as a
|
|
15
|
+
provider refusal and is visible. The error code is matched against the
|
|
16
|
+
RFC 6749 set and reported as `other` when it is outside it, so nothing the
|
|
17
|
+
provider sends is echoed back.
|
|
18
|
+
|
|
5
19
|
## [5.109.2] — 2026-09-09
|
|
6
20
|
|
|
7
21
|
### Fixed
|
|
@@ -19,12 +19,32 @@
|
|
|
19
19
|
* as `OAUTH_REDIRECT_URI_NOT_ALLOWED` at connect time.
|
|
20
20
|
*/
|
|
21
21
|
export declare const LOOPBACK_CALLBACK_PATH = "/hq/integrations/oauth/callback";
|
|
22
|
+
/**
|
|
23
|
+
* Turn an `?error=` redirect into what to tell the user, and whether it is
|
|
24
|
+
* their doing.
|
|
25
|
+
*
|
|
26
|
+
* Every `?error=` used to be reported as "Sign-in was declined", marked
|
|
27
|
+
* expected, and therefore skipped for Sentry capture. Only `access_denied` is
|
|
28
|
+
* the person declining. `invalid_scope` is HQ or the catalog asking for
|
|
29
|
+
* something the provider does not grant; `unauthorized_client` and
|
|
30
|
+
* `invalid_request` are a misconfigured client; `server_error` is the provider
|
|
31
|
+
* failing. Reporting those as a decision the user made both misdirects the
|
|
32
|
+
* user — there is nothing for them to do differently — and hid the real,
|
|
33
|
+
* fixable causes from error reporting, because "expected" suppresses capture.
|
|
34
|
+
* So: `access_denied` stays expected and quiet; everything else is a provider
|
|
35
|
+
* rejection that reaches Sentry.
|
|
36
|
+
*/
|
|
37
|
+
export declare function authorizeErrorOutcome(raw: string): {
|
|
38
|
+
message: string;
|
|
39
|
+
expected: boolean;
|
|
40
|
+
};
|
|
22
41
|
export interface LoopbackListener {
|
|
23
42
|
/** The redirect URI to hand hq-pro — includes the OS-assigned port. */
|
|
24
43
|
redirectUri: string;
|
|
25
44
|
/**
|
|
26
45
|
* Resolves once the authorization server redirects back. Rejects on timeout,
|
|
27
|
-
* on an `?error=` response
|
|
46
|
+
* on an `?error=` response, or on a state mismatch. Only `access_denied`
|
|
47
|
+
* means the person clicked Deny — see {@link authorizeErrorOutcome}.
|
|
28
48
|
*/
|
|
29
49
|
waitForCode(expectedState: string): Promise<string>;
|
|
30
50
|
close(): void;
|
|
@@ -23,6 +23,48 @@ import { IntegrationsCliError } from "./integrations-core.js";
|
|
|
23
23
|
export const LOOPBACK_CALLBACK_PATH = "/hq/integrations/oauth/callback";
|
|
24
24
|
/** How long to wait for the browser round trip before giving the port back. */
|
|
25
25
|
const DEFAULT_TIMEOUT_MS = 5 * 60 * 1000;
|
|
26
|
+
/**
|
|
27
|
+
* The authorization-endpoint error codes RFC 6749 §4.1.2.1 defines. Anything
|
|
28
|
+
* outside this set is reported as `other`: the value arrives in a redirect
|
|
29
|
+
* query string a third party controls, so it is matched against a closed list
|
|
30
|
+
* and never echoed. `error_description` is deliberately never read at all.
|
|
31
|
+
*/
|
|
32
|
+
const OAUTH_AUTHORIZE_ERROR_CODES = new Set([
|
|
33
|
+
"invalid_request",
|
|
34
|
+
"unauthorized_client",
|
|
35
|
+
"access_denied",
|
|
36
|
+
"unsupported_response_type",
|
|
37
|
+
"invalid_scope",
|
|
38
|
+
"server_error",
|
|
39
|
+
"temporarily_unavailable",
|
|
40
|
+
]);
|
|
41
|
+
/**
|
|
42
|
+
* Turn an `?error=` redirect into what to tell the user, and whether it is
|
|
43
|
+
* their doing.
|
|
44
|
+
*
|
|
45
|
+
* Every `?error=` used to be reported as "Sign-in was declined", marked
|
|
46
|
+
* expected, and therefore skipped for Sentry capture. Only `access_denied` is
|
|
47
|
+
* the person declining. `invalid_scope` is HQ or the catalog asking for
|
|
48
|
+
* something the provider does not grant; `unauthorized_client` and
|
|
49
|
+
* `invalid_request` are a misconfigured client; `server_error` is the provider
|
|
50
|
+
* failing. Reporting those as a decision the user made both misdirects the
|
|
51
|
+
* user — there is nothing for them to do differently — and hid the real,
|
|
52
|
+
* fixable causes from error reporting, because "expected" suppresses capture.
|
|
53
|
+
* So: `access_denied` stays expected and quiet; everything else is a provider
|
|
54
|
+
* rejection that reaches Sentry.
|
|
55
|
+
*/
|
|
56
|
+
export function authorizeErrorOutcome(raw) {
|
|
57
|
+
const code = raw.trim().toLowerCase();
|
|
58
|
+
if (code === "access_denied") {
|
|
59
|
+
return { message: "Sign-in was declined.", expected: true };
|
|
60
|
+
}
|
|
61
|
+
const known = OAUTH_AUTHORIZE_ERROR_CODES.has(code) ? code : "other";
|
|
62
|
+
return {
|
|
63
|
+
message: `The provider refused the sign-in request (${known}). ` +
|
|
64
|
+
"Nothing was connected.",
|
|
65
|
+
expected: false,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
26
68
|
/**
|
|
27
69
|
* Escapes text destined for the callback page.
|
|
28
70
|
*
|
|
@@ -180,7 +222,10 @@ export async function startLoopbackListener(opts = {}) {
|
|
|
180
222
|
timer = null;
|
|
181
223
|
const { code, state, error } = result;
|
|
182
224
|
if (error) {
|
|
183
|
-
|
|
225
|
+
const outcome = authorizeErrorOutcome(error);
|
|
226
|
+
reject(new IntegrationsCliError(outcome.message, {
|
|
227
|
+
expected: outcome.expected,
|
|
228
|
+
}));
|
|
184
229
|
return;
|
|
185
230
|
}
|
|
186
231
|
if (!code || !state) {
|