@alter-ai/alter-sdk 0.4.0 → 0.6.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/README.md +140 -47
- package/dist/index.cjs +408 -107
- package/dist/index.d.cts +232 -34
- package/dist/index.d.ts +232 -34
- package/dist/index.js +399 -104
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -34,22 +34,28 @@ __export(index_exports, {
|
|
|
34
34
|
ActorType: () => ActorType,
|
|
35
35
|
AlterSDKError: () => AlterSDKError,
|
|
36
36
|
AlterVault: () => AlterVault,
|
|
37
|
+
BackendError: () => BackendError,
|
|
38
|
+
ConnectConfigError: () => ConnectConfigError,
|
|
39
|
+
ConnectDeniedError: () => ConnectDeniedError,
|
|
37
40
|
ConnectFlowError: () => ConnectFlowError,
|
|
38
41
|
ConnectResult: () => ConnectResult,
|
|
39
42
|
ConnectSession: () => ConnectSession,
|
|
40
43
|
ConnectTimeoutError: () => ConnectTimeoutError,
|
|
44
|
+
ConnectionDeletedError: () => ConnectionDeletedError,
|
|
45
|
+
ConnectionExpiredError: () => ConnectionExpiredError,
|
|
41
46
|
ConnectionInfo: () => ConnectionInfo,
|
|
42
47
|
ConnectionListResult: () => ConnectionListResult,
|
|
43
48
|
ConnectionNotFoundError: () => ConnectionNotFoundError,
|
|
49
|
+
ConnectionRevokedError: () => ConnectionRevokedError,
|
|
44
50
|
HttpMethod: () => HttpMethod,
|
|
45
51
|
NetworkError: () => NetworkError,
|
|
46
52
|
PolicyViolationError: () => PolicyViolationError,
|
|
47
53
|
Provider: () => Provider,
|
|
48
54
|
ProviderAPIError: () => ProviderAPIError,
|
|
55
|
+
ReAuthRequiredError: () => ReAuthRequiredError,
|
|
56
|
+
ScopeReauthRequiredError: () => ScopeReauthRequiredError,
|
|
49
57
|
TimeoutError: () => TimeoutError,
|
|
50
|
-
|
|
51
|
-
TokenResponse: () => TokenResponse,
|
|
52
|
-
TokenRetrievalError: () => TokenRetrievalError
|
|
58
|
+
TokenResponse: () => TokenResponse
|
|
53
59
|
});
|
|
54
60
|
module.exports = __toCommonJS(index_exports);
|
|
55
61
|
|
|
@@ -72,40 +78,70 @@ var AlterSDKError = class extends Error {
|
|
|
72
78
|
return this.message;
|
|
73
79
|
}
|
|
74
80
|
};
|
|
75
|
-
var
|
|
81
|
+
var BackendError = class extends AlterSDKError {
|
|
76
82
|
constructor(message, details) {
|
|
77
83
|
super(message, details);
|
|
78
|
-
this.name = "
|
|
84
|
+
this.name = "BackendError";
|
|
79
85
|
}
|
|
80
86
|
};
|
|
81
|
-
var
|
|
82
|
-
|
|
83
|
-
constructor(message, policyError, details) {
|
|
87
|
+
var ReAuthRequiredError = class extends BackendError {
|
|
88
|
+
constructor(message, details) {
|
|
84
89
|
super(message, details);
|
|
85
|
-
this.name = "
|
|
86
|
-
this.policyError = policyError;
|
|
90
|
+
this.name = "ReAuthRequiredError";
|
|
87
91
|
}
|
|
88
92
|
};
|
|
89
|
-
var
|
|
93
|
+
var ConnectionExpiredError = class extends ReAuthRequiredError {
|
|
90
94
|
constructor(message, details) {
|
|
91
95
|
super(message, details);
|
|
92
|
-
this.name = "
|
|
96
|
+
this.name = "ConnectionExpiredError";
|
|
93
97
|
}
|
|
94
98
|
};
|
|
95
|
-
var
|
|
99
|
+
var ConnectionRevokedError = class extends ReAuthRequiredError {
|
|
96
100
|
connectionId;
|
|
97
101
|
constructor(message, connectionId, details) {
|
|
98
102
|
super(message, details);
|
|
99
|
-
this.name = "
|
|
103
|
+
this.name = "ConnectionRevokedError";
|
|
100
104
|
this.connectionId = connectionId;
|
|
101
105
|
}
|
|
102
106
|
};
|
|
107
|
+
var ConnectionDeletedError = class extends ReAuthRequiredError {
|
|
108
|
+
constructor(message, details) {
|
|
109
|
+
super(message, details);
|
|
110
|
+
this.name = "ConnectionDeletedError";
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
var ConnectionNotFoundError = class extends BackendError {
|
|
114
|
+
constructor(message, details) {
|
|
115
|
+
super(message, details);
|
|
116
|
+
this.name = "ConnectionNotFoundError";
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
var PolicyViolationError = class extends BackendError {
|
|
120
|
+
policyError;
|
|
121
|
+
constructor(message, policyError, details) {
|
|
122
|
+
super(message, details);
|
|
123
|
+
this.name = "PolicyViolationError";
|
|
124
|
+
this.policyError = policyError;
|
|
125
|
+
}
|
|
126
|
+
};
|
|
103
127
|
var ConnectFlowError = class extends AlterSDKError {
|
|
104
128
|
constructor(message, details) {
|
|
105
129
|
super(message, details);
|
|
106
130
|
this.name = "ConnectFlowError";
|
|
107
131
|
}
|
|
108
132
|
};
|
|
133
|
+
var ConnectDeniedError = class extends ConnectFlowError {
|
|
134
|
+
constructor(message, details) {
|
|
135
|
+
super(message, details);
|
|
136
|
+
this.name = "ConnectDeniedError";
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
var ConnectConfigError = class extends ConnectFlowError {
|
|
140
|
+
constructor(message, details) {
|
|
141
|
+
super(message, details);
|
|
142
|
+
this.name = "ConnectConfigError";
|
|
143
|
+
}
|
|
144
|
+
};
|
|
109
145
|
var ConnectTimeoutError = class extends ConnectFlowError {
|
|
110
146
|
constructor(message, details) {
|
|
111
147
|
super(message, details);
|
|
@@ -122,6 +158,16 @@ var ProviderAPIError = class extends AlterSDKError {
|
|
|
122
158
|
this.responseBody = responseBody;
|
|
123
159
|
}
|
|
124
160
|
};
|
|
161
|
+
var ScopeReauthRequiredError = class extends ProviderAPIError {
|
|
162
|
+
connectionId;
|
|
163
|
+
providerId;
|
|
164
|
+
constructor(message, connectionId, providerId, statusCode, responseBody, details) {
|
|
165
|
+
super(message, statusCode, responseBody, details);
|
|
166
|
+
this.name = "ScopeReauthRequiredError";
|
|
167
|
+
this.connectionId = connectionId;
|
|
168
|
+
this.providerId = providerId;
|
|
169
|
+
}
|
|
170
|
+
};
|
|
125
171
|
var NetworkError = class extends AlterSDKError {
|
|
126
172
|
constructor(message, details) {
|
|
127
173
|
super(message, details);
|
|
@@ -161,6 +207,8 @@ var TokenResponse = class _TokenResponse {
|
|
|
161
207
|
injectionFormat;
|
|
162
208
|
/** Extra credentials for multi-part auth (e.g. AWS SigV4 secret_key, region) */
|
|
163
209
|
additionalCredentials;
|
|
210
|
+
/** Additional injection rules for multi-header or query param auth */
|
|
211
|
+
additionalInjections;
|
|
164
212
|
constructor(data) {
|
|
165
213
|
this.tokenType = data.token_type ?? "Bearer";
|
|
166
214
|
this.expiresIn = data.expires_in ?? null;
|
|
@@ -176,6 +224,7 @@ var TokenResponse = class _TokenResponse {
|
|
|
176
224
|
} else {
|
|
177
225
|
this.additionalCredentials = null;
|
|
178
226
|
}
|
|
227
|
+
this.additionalInjections = data.additional_injections ?? null;
|
|
179
228
|
Object.freeze(this);
|
|
180
229
|
}
|
|
181
230
|
/**
|
|
@@ -221,7 +270,8 @@ var TokenResponse = class _TokenResponse {
|
|
|
221
270
|
expires_in: this.expiresIn,
|
|
222
271
|
expires_at: this.expiresAt?.toISOString() ?? null,
|
|
223
272
|
scopes: this.scopes,
|
|
224
|
-
connection_id: this.connectionId
|
|
273
|
+
connection_id: this.connectionId,
|
|
274
|
+
provider_id: this.providerId
|
|
225
275
|
};
|
|
226
276
|
}
|
|
227
277
|
/**
|
|
@@ -238,22 +288,24 @@ var TokenResponse = class _TokenResponse {
|
|
|
238
288
|
}
|
|
239
289
|
};
|
|
240
290
|
var ConnectionInfo = class {
|
|
241
|
-
|
|
291
|
+
connectionId;
|
|
242
292
|
providerId;
|
|
243
293
|
scopes;
|
|
244
294
|
accountIdentifier;
|
|
245
295
|
accountDisplayName;
|
|
246
296
|
status;
|
|
297
|
+
scopeMismatch;
|
|
247
298
|
expiresAt;
|
|
248
299
|
createdAt;
|
|
249
300
|
lastUsedAt;
|
|
250
301
|
constructor(data) {
|
|
251
|
-
this.
|
|
302
|
+
this.connectionId = data.connection_id;
|
|
252
303
|
this.providerId = data.provider_id;
|
|
253
304
|
this.scopes = data.scopes ?? [];
|
|
254
305
|
this.accountIdentifier = data.account_identifier ?? null;
|
|
255
306
|
this.accountDisplayName = data.account_display_name ?? null;
|
|
256
307
|
this.status = data.status;
|
|
308
|
+
this.scopeMismatch = data.scope_mismatch ?? false;
|
|
257
309
|
this.expiresAt = data.expires_at ?? null;
|
|
258
310
|
this.createdAt = data.created_at;
|
|
259
311
|
this.lastUsedAt = data.last_used_at ?? null;
|
|
@@ -261,19 +313,20 @@ var ConnectionInfo = class {
|
|
|
261
313
|
}
|
|
262
314
|
toJSON() {
|
|
263
315
|
return {
|
|
264
|
-
|
|
316
|
+
connection_id: this.connectionId,
|
|
265
317
|
provider_id: this.providerId,
|
|
266
318
|
scopes: this.scopes,
|
|
267
319
|
account_identifier: this.accountIdentifier,
|
|
268
320
|
account_display_name: this.accountDisplayName,
|
|
269
321
|
status: this.status,
|
|
322
|
+
scope_mismatch: this.scopeMismatch,
|
|
270
323
|
expires_at: this.expiresAt,
|
|
271
324
|
created_at: this.createdAt,
|
|
272
325
|
last_used_at: this.lastUsedAt
|
|
273
326
|
};
|
|
274
327
|
}
|
|
275
328
|
toString() {
|
|
276
|
-
return `ConnectionInfo(
|
|
329
|
+
return `ConnectionInfo(connection_id=${this.connectionId}, provider=${this.providerId}, status=${this.status})`;
|
|
277
330
|
}
|
|
278
331
|
};
|
|
279
332
|
var ConnectSession = class {
|
|
@@ -320,11 +373,23 @@ var ConnectResult = class {
|
|
|
320
373
|
providerId;
|
|
321
374
|
accountIdentifier;
|
|
322
375
|
scopes;
|
|
376
|
+
connectionPolicy;
|
|
323
377
|
constructor(data) {
|
|
324
378
|
this.connectionId = data.connection_id;
|
|
325
379
|
this.providerId = data.provider_id;
|
|
326
380
|
this.accountIdentifier = data.account_identifier ?? null;
|
|
327
381
|
this.scopes = data.scopes ?? [];
|
|
382
|
+
const rawPolicy = data.connection_policy;
|
|
383
|
+
if (rawPolicy) {
|
|
384
|
+
const raw = rawPolicy;
|
|
385
|
+
this.connectionPolicy = Object.freeze({
|
|
386
|
+
expiresAt: raw["expires_at"] !== void 0 ? raw["expires_at"] : rawPolicy.expiresAt ?? null,
|
|
387
|
+
createdBy: raw["created_by"] !== void 0 ? raw["created_by"] : rawPolicy.createdBy ?? null,
|
|
388
|
+
createdAt: raw["created_at"] !== void 0 ? raw["created_at"] : rawPolicy.createdAt ?? null
|
|
389
|
+
});
|
|
390
|
+
} else {
|
|
391
|
+
this.connectionPolicy = null;
|
|
392
|
+
}
|
|
328
393
|
Object.freeze(this);
|
|
329
394
|
}
|
|
330
395
|
toJSON() {
|
|
@@ -332,7 +397,12 @@ var ConnectResult = class {
|
|
|
332
397
|
connection_id: this.connectionId,
|
|
333
398
|
provider_id: this.providerId,
|
|
334
399
|
account_identifier: this.accountIdentifier,
|
|
335
|
-
scopes: this.scopes
|
|
400
|
+
scopes: this.scopes,
|
|
401
|
+
connection_policy: this.connectionPolicy ? {
|
|
402
|
+
expires_at: this.connectionPolicy.expiresAt ?? null,
|
|
403
|
+
created_by: this.connectionPolicy.createdBy ?? null,
|
|
404
|
+
created_at: this.connectionPolicy.createdAt ?? null
|
|
405
|
+
} : null
|
|
336
406
|
};
|
|
337
407
|
}
|
|
338
408
|
toString() {
|
|
@@ -346,7 +416,8 @@ var SENSITIVE_HEADERS = /* @__PURE__ */ new Set([
|
|
|
346
416
|
"x-api-key",
|
|
347
417
|
"x-auth-token",
|
|
348
418
|
"x-amz-date",
|
|
349
|
-
"x-amz-content-sha256"
|
|
419
|
+
"x-amz-content-sha256",
|
|
420
|
+
"x-amz-security-token"
|
|
350
421
|
]);
|
|
351
422
|
var APICallAuditLog = class {
|
|
352
423
|
connectionId;
|
|
@@ -425,6 +496,7 @@ var import_node_crypto = require("crypto");
|
|
|
425
496
|
var ALGORITHM = "AWS4-HMAC-SHA256";
|
|
426
497
|
var AWS_HOST_RE = /^(?<service>[a-z0-9-]+)\.(?<region>[a-z]{2}(?:-[a-z0-9]+)+-\d+)\.amazonaws\.com$/;
|
|
427
498
|
var S3_VIRTUAL_HOST_RE = /^[^.]+\.s3\.(?<region>[a-z]{2}(?:-[a-z0-9]+)+-\d+)\.amazonaws\.com$/;
|
|
499
|
+
var VPCE_HOST_RE = /^vpce-[a-z0-9-]+\.(?<service>[a-z0-9-]+)\.(?<region>[a-z]{2}(?:-[a-z0-9]+)+-\d+)\.vpce\.amazonaws\.com$/;
|
|
428
500
|
function hmacSha256(key, message) {
|
|
429
501
|
return (0, import_node_crypto.createHmac)("sha256", key).update(message, "utf8").digest();
|
|
430
502
|
}
|
|
@@ -447,6 +519,10 @@ function detectServiceAndRegion(hostname) {
|
|
|
447
519
|
if (s3m?.groups) {
|
|
448
520
|
return { service: "s3", region: s3m.groups.region };
|
|
449
521
|
}
|
|
522
|
+
const vpcem = VPCE_HOST_RE.exec(lower);
|
|
523
|
+
if (vpcem?.groups) {
|
|
524
|
+
return { service: vpcem.groups.service, region: vpcem.groups.region };
|
|
525
|
+
}
|
|
450
526
|
return { service: null, region: null };
|
|
451
527
|
}
|
|
452
528
|
function deriveSigningKey(secretKey, dateStamp, region, service) {
|
|
@@ -481,18 +557,21 @@ function canonicalQueryString(query) {
|
|
|
481
557
|
]);
|
|
482
558
|
}
|
|
483
559
|
}
|
|
484
|
-
|
|
560
|
+
const sigv4Encode = (s) => encodeURIComponent(s).replace(
|
|
561
|
+
/[!'()*]/g,
|
|
562
|
+
(c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`
|
|
563
|
+
);
|
|
564
|
+
const encoded = sorted.map(
|
|
565
|
+
([k, v]) => [sigv4Encode(k), sigv4Encode(v)]
|
|
566
|
+
);
|
|
567
|
+
encoded.sort((a, b) => {
|
|
485
568
|
if (a[0] < b[0]) return -1;
|
|
486
569
|
if (a[0] > b[0]) return 1;
|
|
487
570
|
if (a[1] < b[1]) return -1;
|
|
488
571
|
if (a[1] > b[1]) return 1;
|
|
489
572
|
return 0;
|
|
490
573
|
});
|
|
491
|
-
|
|
492
|
-
/[!'()*]/g,
|
|
493
|
-
(c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`
|
|
494
|
-
);
|
|
495
|
-
return sorted.map(([k, v]) => `${sigv4Encode(k)}=${sigv4Encode(v)}`).join("&");
|
|
574
|
+
return encoded.map(([k, v]) => `${k}=${v}`).join("&");
|
|
496
575
|
}
|
|
497
576
|
function canonicalHeadersAndSigned(headers) {
|
|
498
577
|
const canonical = {};
|
|
@@ -511,11 +590,9 @@ function signAwsRequest(opts) {
|
|
|
511
590
|
const parsed = new URL(opts.url);
|
|
512
591
|
const hostname = parsed.hostname;
|
|
513
592
|
let { region, service } = opts;
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
if (service == null) service = detected.service;
|
|
518
|
-
}
|
|
593
|
+
const detected = detectServiceAndRegion(hostname);
|
|
594
|
+
if (detected.region != null) region = detected.region;
|
|
595
|
+
if (detected.service != null) service = detected.service;
|
|
519
596
|
if (!region) {
|
|
520
597
|
throw new Error(
|
|
521
598
|
`Cannot determine AWS region from URL '${opts.url}'. Pass region explicitly via additional_credentials.`
|
|
@@ -598,10 +675,11 @@ function _extractAdditionalCredentials(token) {
|
|
|
598
675
|
return _additionalCredsStore.get(token);
|
|
599
676
|
}
|
|
600
677
|
var _fetch;
|
|
601
|
-
var SDK_VERSION = "0.
|
|
678
|
+
var SDK_VERSION = "0.6.0";
|
|
602
679
|
var SDK_USER_AGENT = `alter-sdk-node/${SDK_VERSION}`;
|
|
603
680
|
var HTTP_FORBIDDEN = 403;
|
|
604
681
|
var HTTP_NOT_FOUND = 404;
|
|
682
|
+
var HTTP_GONE = 410;
|
|
605
683
|
var HTTP_BAD_REQUEST = 400;
|
|
606
684
|
var HTTP_UNAUTHORIZED = 401;
|
|
607
685
|
var HTTP_BAD_GATEWAY = 502;
|
|
@@ -657,6 +735,9 @@ var HttpClient = class {
|
|
|
657
735
|
};
|
|
658
736
|
if (options?.body !== void 0) {
|
|
659
737
|
init.body = options.body;
|
|
738
|
+
if (!mergedHeaders["Content-Type"]) {
|
|
739
|
+
mergedHeaders["Content-Type"] = "application/json";
|
|
740
|
+
}
|
|
660
741
|
} else if (options?.json !== void 0) {
|
|
661
742
|
init.body = JSON.stringify(options.json);
|
|
662
743
|
mergedHeaders["Content-Type"] = "application/json";
|
|
@@ -693,6 +774,8 @@ var AlterVault = class _AlterVault {
|
|
|
693
774
|
#closed = false;
|
|
694
775
|
/** Pending audit log promises (fire-and-forget) */
|
|
695
776
|
#auditPromises = /* @__PURE__ */ new Set();
|
|
777
|
+
/** JWT identity resolution: callable that returns user token */
|
|
778
|
+
#userTokenGetter = null;
|
|
696
779
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
697
780
|
// Public readonly properties (frozen by Object.freeze)
|
|
698
781
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
@@ -728,6 +811,7 @@ var AlterVault = class _AlterVault {
|
|
|
728
811
|
this.#actorName = options.actorName;
|
|
729
812
|
this.#actorVersion = options.actorVersion;
|
|
730
813
|
this.#clientType = options.clientType;
|
|
814
|
+
this.#userTokenGetter = options.userTokenGetter ?? null;
|
|
731
815
|
this.#framework = options.framework;
|
|
732
816
|
if (!_fetch) {
|
|
733
817
|
_fetch = globalThis.fetch;
|
|
@@ -902,6 +986,24 @@ ${contentHash}`;
|
|
|
902
986
|
}
|
|
903
987
|
return false;
|
|
904
988
|
}
|
|
989
|
+
/**
|
|
990
|
+
* Resolve a value_source to the actual value for injection.
|
|
991
|
+
*
|
|
992
|
+
* @param valueSource - "token" or "additional_credentials.<field>"
|
|
993
|
+
* @param accessToken - The main credential value
|
|
994
|
+
* @param additionalCreds - Additional credentials from vault
|
|
995
|
+
* @returns The resolved value, or null if not available
|
|
996
|
+
*/
|
|
997
|
+
static #resolveInjectionValue(valueSource, accessToken, additionalCreds) {
|
|
998
|
+
if (valueSource === "token") {
|
|
999
|
+
return accessToken;
|
|
1000
|
+
}
|
|
1001
|
+
if (valueSource.startsWith("additional_credentials.")) {
|
|
1002
|
+
const field = valueSource.slice("additional_credentials.".length);
|
|
1003
|
+
return additionalCreds?.[field] ?? null;
|
|
1004
|
+
}
|
|
1005
|
+
return null;
|
|
1006
|
+
}
|
|
905
1007
|
static async #safeParseJson(response) {
|
|
906
1008
|
try {
|
|
907
1009
|
return await response.clone().json();
|
|
@@ -923,12 +1025,25 @@ ${contentHash}`;
|
|
|
923
1025
|
}
|
|
924
1026
|
if (response.status === HTTP_FORBIDDEN) {
|
|
925
1027
|
const errorData = await _AlterVault.#safeParseJson(response);
|
|
1028
|
+
if (errorData.error === "connection_expired") {
|
|
1029
|
+
throw new ConnectionExpiredError(
|
|
1030
|
+
errorData.message ?? "Connection expired per TTL policy",
|
|
1031
|
+
errorData.details
|
|
1032
|
+
);
|
|
1033
|
+
}
|
|
926
1034
|
throw new PolicyViolationError(
|
|
927
1035
|
errorData.message ?? "Access denied by policy",
|
|
928
1036
|
errorData.error,
|
|
929
1037
|
errorData.details
|
|
930
1038
|
);
|
|
931
1039
|
}
|
|
1040
|
+
if (response.status === HTTP_GONE) {
|
|
1041
|
+
const errorData = await _AlterVault.#safeParseJson(response);
|
|
1042
|
+
throw new ConnectionDeletedError(
|
|
1043
|
+
errorData.message ?? "Connection has been deleted. A new connection_id will be issued on re-authorization.",
|
|
1044
|
+
errorData
|
|
1045
|
+
);
|
|
1046
|
+
}
|
|
932
1047
|
if (response.status === HTTP_NOT_FOUND) {
|
|
933
1048
|
const errorData = await _AlterVault.#safeParseJson(response);
|
|
934
1049
|
throw new ConnectionNotFoundError(
|
|
@@ -938,35 +1053,35 @@ ${contentHash}`;
|
|
|
938
1053
|
}
|
|
939
1054
|
if (response.status === HTTP_BAD_REQUEST || response.status === HTTP_BAD_GATEWAY) {
|
|
940
1055
|
const errorData = await _AlterVault.#safeParseJson(response);
|
|
941
|
-
if (
|
|
942
|
-
throw new
|
|
943
|
-
errorData.message ?? "
|
|
1056
|
+
if (errorData.error === "connection_revoked") {
|
|
1057
|
+
throw new ConnectionRevokedError(
|
|
1058
|
+
errorData.message ?? "Connection has been revoked. User must re-authorize.",
|
|
944
1059
|
errorData.connection_id,
|
|
945
1060
|
errorData
|
|
946
1061
|
);
|
|
947
1062
|
}
|
|
948
|
-
throw new
|
|
1063
|
+
throw new BackendError(
|
|
949
1064
|
errorData.message ?? `Backend error ${response.status}`,
|
|
950
1065
|
errorData
|
|
951
1066
|
);
|
|
952
1067
|
}
|
|
953
1068
|
if (response.status === HTTP_UNAUTHORIZED) {
|
|
954
1069
|
const errorData = await _AlterVault.#safeParseJson(response);
|
|
955
|
-
throw new
|
|
1070
|
+
throw new BackendError(
|
|
956
1071
|
errorData.message ?? "Unauthorized \u2014 check your API key",
|
|
957
1072
|
errorData
|
|
958
1073
|
);
|
|
959
1074
|
}
|
|
960
1075
|
if (response.status === HTTP_INTERNAL_SERVER_ERROR || response.status === HTTP_SERVICE_UNAVAILABLE) {
|
|
961
1076
|
const errorData = await _AlterVault.#safeParseJson(response);
|
|
962
|
-
throw new
|
|
1077
|
+
throw new BackendError(
|
|
963
1078
|
errorData.message ?? `Backend unavailable (HTTP ${response.status})`,
|
|
964
1079
|
errorData
|
|
965
1080
|
);
|
|
966
1081
|
}
|
|
967
1082
|
if (response.status >= HTTP_CLIENT_ERROR_START) {
|
|
968
1083
|
const errorData = await _AlterVault.#safeParseJson(response);
|
|
969
|
-
throw new
|
|
1084
|
+
throw new BackendError(
|
|
970
1085
|
errorData.message ?? `Unexpected backend error (HTTP ${response.status})`,
|
|
971
1086
|
errorData
|
|
972
1087
|
);
|
|
@@ -978,24 +1093,52 @@ ${contentHash}`;
|
|
|
978
1093
|
* This is a private method. Tokens are NEVER exposed to developers.
|
|
979
1094
|
* Use request() instead, which handles tokens internally.
|
|
980
1095
|
*/
|
|
981
|
-
async #
|
|
1096
|
+
async #getUserToken() {
|
|
1097
|
+
if (!this.#userTokenGetter) {
|
|
1098
|
+
throw new AlterSDKError(
|
|
1099
|
+
"userTokenGetter is required for provider-based resolution. Pass userTokenGetter to AlterVault constructor."
|
|
1100
|
+
);
|
|
1101
|
+
}
|
|
1102
|
+
const result = this.#userTokenGetter();
|
|
1103
|
+
const token = result instanceof Promise ? await result : result;
|
|
1104
|
+
if (!token || typeof token !== "string") {
|
|
1105
|
+
throw new AlterSDKError("userTokenGetter must return a non-empty string");
|
|
1106
|
+
}
|
|
1107
|
+
return token;
|
|
1108
|
+
}
|
|
1109
|
+
async #getToken(connectionId, reason, requestMetadata, runId, threadId, toolCallId, provider, account) {
|
|
982
1110
|
const actorHeaders = this.#getActorRequestHeaders(
|
|
983
1111
|
runId,
|
|
984
1112
|
threadId,
|
|
985
1113
|
toolCallId
|
|
986
1114
|
);
|
|
987
1115
|
let response;
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
1116
|
+
let tokenBody;
|
|
1117
|
+
if (provider) {
|
|
1118
|
+
const userToken = await this.#getUserToken();
|
|
1119
|
+
tokenBody = {
|
|
1120
|
+
provider_id: provider,
|
|
1121
|
+
user_token: userToken,
|
|
1122
|
+
reason: reason ?? null,
|
|
1123
|
+
request: requestMetadata ?? null
|
|
1124
|
+
};
|
|
1125
|
+
if (account) {
|
|
1126
|
+
tokenBody.account = account;
|
|
1127
|
+
}
|
|
1128
|
+
} else {
|
|
1129
|
+
tokenBody = {
|
|
1130
|
+
connection_id: connectionId,
|
|
1131
|
+
reason: reason ?? null,
|
|
1132
|
+
request: requestMetadata ?? null
|
|
1133
|
+
};
|
|
1134
|
+
}
|
|
993
1135
|
const tokenPath = "/sdk/token";
|
|
994
|
-
const
|
|
1136
|
+
const tokenBodyStr = JSON.stringify(tokenBody);
|
|
1137
|
+
const hmacHeaders = this.#computeHmacHeaders("POST", tokenPath, tokenBodyStr);
|
|
995
1138
|
try {
|
|
996
1139
|
response = await this.#alterClient.post(tokenPath, {
|
|
997
|
-
|
|
998
|
-
headers: { ...actorHeaders, ...hmacHeaders }
|
|
1140
|
+
body: tokenBodyStr,
|
|
1141
|
+
headers: { ...actorHeaders, ...hmacHeaders, "Content-Type": "application/json" }
|
|
999
1142
|
});
|
|
1000
1143
|
} catch (error) {
|
|
1001
1144
|
if (_AlterVault.#isTimeoutOrAbortError(error)) {
|
|
@@ -1010,7 +1153,7 @@ ${contentHash}`;
|
|
|
1010
1153
|
{ base_url: this.baseUrl }
|
|
1011
1154
|
);
|
|
1012
1155
|
}
|
|
1013
|
-
throw new
|
|
1156
|
+
throw new BackendError(
|
|
1014
1157
|
`Failed to retrieve token: ${error instanceof Error ? error.message : String(error)}`,
|
|
1015
1158
|
{ connection_id: connectionId, error: String(error) }
|
|
1016
1159
|
);
|
|
@@ -1019,15 +1162,16 @@ ${contentHash}`;
|
|
|
1019
1162
|
await this.#handleErrorResponse(response);
|
|
1020
1163
|
const tokenData = await response.json();
|
|
1021
1164
|
const typedData = tokenData;
|
|
1165
|
+
const scopeMismatch = typedData.scope_mismatch ?? false;
|
|
1022
1166
|
const tokenResponse = new TokenResponse(typedData);
|
|
1023
1167
|
if (!/^[A-Za-z][A-Za-z0-9-]*$/.test(tokenResponse.injectionHeader)) {
|
|
1024
|
-
throw new
|
|
1168
|
+
throw new BackendError(
|
|
1025
1169
|
`Backend returned invalid injection_header: ${tokenResponse.injectionHeader}`,
|
|
1026
1170
|
{ connectionId: String(connectionId) }
|
|
1027
1171
|
);
|
|
1028
1172
|
}
|
|
1029
1173
|
if (/[\r\n\x00]/.test(tokenResponse.injectionFormat)) {
|
|
1030
|
-
throw new
|
|
1174
|
+
throw new BackendError(
|
|
1031
1175
|
`Backend returned invalid injection_format (contains control characters)`,
|
|
1032
1176
|
{ connectionId: String(connectionId) }
|
|
1033
1177
|
);
|
|
@@ -1036,7 +1180,7 @@ ${contentHash}`;
|
|
|
1036
1180
|
if (typedData.additional_credentials) {
|
|
1037
1181
|
_storeAdditionalCredentials(tokenResponse, typedData.additional_credentials);
|
|
1038
1182
|
}
|
|
1039
|
-
return tokenResponse;
|
|
1183
|
+
return { tokenResponse, scopeMismatch };
|
|
1040
1184
|
}
|
|
1041
1185
|
/**
|
|
1042
1186
|
* Log an API call to the backend audit endpoint (INTERNAL).
|
|
@@ -1066,10 +1210,11 @@ ${contentHash}`;
|
|
|
1066
1210
|
const actorHeaders = this.#getActorRequestHeaders(params.runId);
|
|
1067
1211
|
const auditPath = "/sdk/oauth/audit/api-call";
|
|
1068
1212
|
const auditBody = sanitized;
|
|
1069
|
-
const
|
|
1213
|
+
const auditBodyStr = JSON.stringify(auditBody);
|
|
1214
|
+
const auditHmac = this.#computeHmacHeaders("POST", auditPath, auditBodyStr);
|
|
1070
1215
|
const response = await this.#alterClient.post(auditPath, {
|
|
1071
|
-
|
|
1072
|
-
headers: { ...actorHeaders, ...auditHmac }
|
|
1216
|
+
body: auditBodyStr,
|
|
1217
|
+
headers: { ...actorHeaders, ...auditHmac, "Content-Type": "application/json" }
|
|
1073
1218
|
});
|
|
1074
1219
|
this.#cacheActorIdFromResponse(response);
|
|
1075
1220
|
if (!response.ok) {
|
|
@@ -1141,12 +1286,22 @@ ${contentHash}`;
|
|
|
1141
1286
|
"SDK instance has been closed. Create a new AlterVault instance to make requests."
|
|
1142
1287
|
);
|
|
1143
1288
|
}
|
|
1289
|
+
const provider = options?.provider;
|
|
1290
|
+
const account = options?.account;
|
|
1291
|
+
if (!connectionId && !provider) {
|
|
1292
|
+
throw new AlterSDKError("Provide connectionId or options.provider");
|
|
1293
|
+
}
|
|
1294
|
+
if (connectionId && provider) {
|
|
1295
|
+
throw new AlterSDKError("Cannot provide both connectionId and options.provider");
|
|
1296
|
+
}
|
|
1297
|
+
const effectiveConnectionId = connectionId ?? null;
|
|
1298
|
+
let currentUrl = url;
|
|
1144
1299
|
const runId = options?.runId ?? (0, import_node_crypto2.randomUUID)();
|
|
1145
1300
|
const methodStr = String(method).toUpperCase();
|
|
1146
|
-
const urlLower =
|
|
1301
|
+
const urlLower = currentUrl.toLowerCase();
|
|
1147
1302
|
if (!ALLOWED_URL_SCHEMES.some((scheme) => urlLower.startsWith(scheme))) {
|
|
1148
1303
|
throw new AlterSDKError(
|
|
1149
|
-
`URL must start with https:// or http://, got: ${
|
|
1304
|
+
`URL must start with https:// or http://, got: ${currentUrl.slice(0, 50)}`
|
|
1150
1305
|
);
|
|
1151
1306
|
}
|
|
1152
1307
|
if (options?.pathParams && Object.keys(options.pathParams).length > 0) {
|
|
@@ -1155,7 +1310,7 @@ ${contentHash}`;
|
|
|
1155
1310
|
encodedParams[key] = encodeURIComponent(String(value));
|
|
1156
1311
|
}
|
|
1157
1312
|
try {
|
|
1158
|
-
let resolvedUrl =
|
|
1313
|
+
let resolvedUrl = currentUrl;
|
|
1159
1314
|
for (const [key, value] of Object.entries(encodedParams)) {
|
|
1160
1315
|
const placeholder = `{${key}}`;
|
|
1161
1316
|
if (!resolvedUrl.includes(placeholder)) {
|
|
@@ -1166,26 +1321,28 @@ ${contentHash}`;
|
|
|
1166
1321
|
const remaining = resolvedUrl.match(/\{(\w+)\}/);
|
|
1167
1322
|
if (remaining) {
|
|
1168
1323
|
throw new AlterSDKError(
|
|
1169
|
-
`Invalid URL template or missing path_params: '${remaining[1]}'. URL: ${
|
|
1324
|
+
`Invalid URL template or missing path_params: '${remaining[1]}'. URL: ${currentUrl}, path_params: ${JSON.stringify(options.pathParams)}`
|
|
1170
1325
|
);
|
|
1171
1326
|
}
|
|
1172
|
-
|
|
1327
|
+
currentUrl = resolvedUrl;
|
|
1173
1328
|
} catch (error) {
|
|
1174
1329
|
if (error instanceof AlterSDKError) {
|
|
1175
1330
|
throw error;
|
|
1176
1331
|
}
|
|
1177
1332
|
throw new AlterSDKError(
|
|
1178
|
-
`Invalid URL template or missing path_params: ${error instanceof Error ? error.message : String(error)}. URL: ${
|
|
1333
|
+
`Invalid URL template or missing path_params: ${error instanceof Error ? error.message : String(error)}. URL: ${currentUrl}, path_params: ${JSON.stringify(options.pathParams)}`
|
|
1179
1334
|
);
|
|
1180
1335
|
}
|
|
1181
1336
|
}
|
|
1182
|
-
const tokenResponse = await this.#getToken(
|
|
1183
|
-
|
|
1337
|
+
const { tokenResponse, scopeMismatch } = await this.#getToken(
|
|
1338
|
+
effectiveConnectionId,
|
|
1184
1339
|
options?.reason,
|
|
1185
|
-
{ method: methodStr, url },
|
|
1340
|
+
{ method: methodStr, url: currentUrl },
|
|
1186
1341
|
runId,
|
|
1187
1342
|
options?.threadId,
|
|
1188
|
-
options?.toolCallId
|
|
1343
|
+
options?.toolCallId,
|
|
1344
|
+
provider,
|
|
1345
|
+
account
|
|
1189
1346
|
);
|
|
1190
1347
|
const requestHeaders = options?.extraHeaders ? { ...options.extraHeaders } : {};
|
|
1191
1348
|
const accessToken = _extractAccessToken(tokenResponse);
|
|
@@ -1202,14 +1359,14 @@ ${contentHash}`;
|
|
|
1202
1359
|
}
|
|
1203
1360
|
}
|
|
1204
1361
|
if (!additionalCreds.secret_key) {
|
|
1205
|
-
throw new
|
|
1362
|
+
throw new BackendError(
|
|
1206
1363
|
"AWS SigV4 credential is missing secret_key in additional_credentials. Re-store the credential with both Access Key ID and Secret Access Key.",
|
|
1207
|
-
{ connection_id:
|
|
1364
|
+
{ connection_id: effectiveConnectionId }
|
|
1208
1365
|
);
|
|
1209
1366
|
}
|
|
1210
1367
|
const awsHeaders = signAwsRequest({
|
|
1211
1368
|
method: methodStr,
|
|
1212
|
-
url,
|
|
1369
|
+
url: currentUrl,
|
|
1213
1370
|
headers: requestHeaders,
|
|
1214
1371
|
body: sigv4BodyStr,
|
|
1215
1372
|
accessKeyId,
|
|
@@ -1228,6 +1385,25 @@ ${contentHash}`;
|
|
|
1228
1385
|
}
|
|
1229
1386
|
requestHeaders[tokenResponse.injectionHeader] = tokenResponse.injectionFormat.replace("{token}", accessToken);
|
|
1230
1387
|
}
|
|
1388
|
+
const auditUrl = currentUrl;
|
|
1389
|
+
if (tokenResponse.additionalInjections && !isSigV4) {
|
|
1390
|
+
for (const rule of tokenResponse.additionalInjections) {
|
|
1391
|
+
const value = _AlterVault.#resolveInjectionValue(
|
|
1392
|
+
rule.value_source,
|
|
1393
|
+
accessToken,
|
|
1394
|
+
additionalCreds
|
|
1395
|
+
);
|
|
1396
|
+
if (!value) continue;
|
|
1397
|
+
if (!/^[A-Za-z][A-Za-z0-9\-_]*$/.test(rule.key)) continue;
|
|
1398
|
+
if (rule.target === "header") {
|
|
1399
|
+
requestHeaders[rule.key] = value;
|
|
1400
|
+
} else if (rule.target === "query_param") {
|
|
1401
|
+
const urlObj = new URL(currentUrl);
|
|
1402
|
+
urlObj.searchParams.set(rule.key, value);
|
|
1403
|
+
currentUrl = urlObj.toString();
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1231
1407
|
if (!requestHeaders["User-Agent"]) {
|
|
1232
1408
|
requestHeaders["User-Agent"] = SDK_USER_AGENT;
|
|
1233
1409
|
}
|
|
@@ -1235,13 +1411,13 @@ ${contentHash}`;
|
|
|
1235
1411
|
let response;
|
|
1236
1412
|
try {
|
|
1237
1413
|
if (isSigV4 && sigv4BodyStr != null) {
|
|
1238
|
-
response = await this.#providerClient.request(methodStr,
|
|
1414
|
+
response = await this.#providerClient.request(methodStr, currentUrl, {
|
|
1239
1415
|
body: sigv4BodyStr,
|
|
1240
1416
|
headers: requestHeaders,
|
|
1241
1417
|
params: options?.queryParams
|
|
1242
1418
|
});
|
|
1243
1419
|
} else {
|
|
1244
|
-
response = await this.#providerClient.request(methodStr,
|
|
1420
|
+
response = await this.#providerClient.request(methodStr, currentUrl, {
|
|
1245
1421
|
json: options?.json,
|
|
1246
1422
|
headers: requestHeaders,
|
|
1247
1423
|
params: options?.queryParams
|
|
@@ -1252,18 +1428,18 @@ ${contentHash}`;
|
|
|
1252
1428
|
throw new TimeoutError(
|
|
1253
1429
|
`Provider API request timed out: ${error instanceof Error ? error.message : String(error)}`,
|
|
1254
1430
|
{
|
|
1255
|
-
connection_id:
|
|
1431
|
+
connection_id: effectiveConnectionId,
|
|
1256
1432
|
method: methodStr,
|
|
1257
|
-
url
|
|
1433
|
+
url: currentUrl
|
|
1258
1434
|
}
|
|
1259
1435
|
);
|
|
1260
1436
|
}
|
|
1261
1437
|
throw new NetworkError(
|
|
1262
1438
|
`Failed to call provider API: ${error instanceof Error ? error.message : String(error)}`,
|
|
1263
1439
|
{
|
|
1264
|
-
connection_id:
|
|
1440
|
+
connection_id: effectiveConnectionId,
|
|
1265
1441
|
method: methodStr,
|
|
1266
|
-
url,
|
|
1442
|
+
url: currentUrl,
|
|
1267
1443
|
error: String(error)
|
|
1268
1444
|
}
|
|
1269
1445
|
);
|
|
@@ -1271,6 +1447,13 @@ ${contentHash}`;
|
|
|
1271
1447
|
const latencyMs = Date.now() - startTime;
|
|
1272
1448
|
const sigv4Sensitive = /* @__PURE__ */ new Set(["authorization", "x-amz-date", "x-amz-content-sha256"]);
|
|
1273
1449
|
const stripHeaders = isSigV4 ? sigv4Sensitive : /* @__PURE__ */ new Set([injectionHeaderLower]);
|
|
1450
|
+
if (tokenResponse.additionalInjections && !isSigV4) {
|
|
1451
|
+
for (const rule of tokenResponse.additionalInjections) {
|
|
1452
|
+
if (rule.target === "header") {
|
|
1453
|
+
stripHeaders.add(rule.key.toLowerCase());
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1274
1457
|
const auditHeaders = {};
|
|
1275
1458
|
for (const [key, value] of Object.entries(requestHeaders)) {
|
|
1276
1459
|
if (!stripHeaders.has(key.toLowerCase())) {
|
|
@@ -1284,9 +1467,9 @@ ${contentHash}`;
|
|
|
1284
1467
|
});
|
|
1285
1468
|
this.#scheduleAuditLog({
|
|
1286
1469
|
connectionId: tokenResponse.connectionId,
|
|
1287
|
-
providerId: tokenResponse.providerId ||
|
|
1470
|
+
providerId: tokenResponse.providerId || effectiveConnectionId || "",
|
|
1288
1471
|
method: methodStr,
|
|
1289
|
-
url,
|
|
1472
|
+
url: auditUrl,
|
|
1290
1473
|
requestHeaders: auditHeaders,
|
|
1291
1474
|
requestBody: options?.json ?? null,
|
|
1292
1475
|
responseStatus: response.status,
|
|
@@ -1299,14 +1482,29 @@ ${contentHash}`;
|
|
|
1299
1482
|
toolCallId: options?.toolCallId ?? null
|
|
1300
1483
|
});
|
|
1301
1484
|
if (response.status >= HTTP_CLIENT_ERROR_START) {
|
|
1485
|
+
if (response.status === HTTP_FORBIDDEN && scopeMismatch) {
|
|
1486
|
+
throw new ScopeReauthRequiredError(
|
|
1487
|
+
"Provider API returned 403 and the connection's scopes don't match the provider config. The user needs to re-authorize to grant updated permissions.",
|
|
1488
|
+
tokenResponse.connectionId,
|
|
1489
|
+
tokenResponse.providerId,
|
|
1490
|
+
response.status,
|
|
1491
|
+
responseBody,
|
|
1492
|
+
{
|
|
1493
|
+
connection_id: tokenResponse.connectionId,
|
|
1494
|
+
provider_id: tokenResponse.providerId,
|
|
1495
|
+
method: methodStr,
|
|
1496
|
+
url: currentUrl
|
|
1497
|
+
}
|
|
1498
|
+
);
|
|
1499
|
+
}
|
|
1302
1500
|
throw new ProviderAPIError(
|
|
1303
1501
|
`Provider API returned error ${response.status}`,
|
|
1304
1502
|
response.status,
|
|
1305
1503
|
responseBody,
|
|
1306
1504
|
{
|
|
1307
|
-
connection_id:
|
|
1505
|
+
connection_id: effectiveConnectionId,
|
|
1308
1506
|
method: methodStr,
|
|
1309
|
-
url
|
|
1507
|
+
url: currentUrl
|
|
1310
1508
|
}
|
|
1311
1509
|
);
|
|
1312
1510
|
}
|
|
@@ -1331,12 +1529,23 @@ ${contentHash}`;
|
|
|
1331
1529
|
limit: options?.limit ?? 100,
|
|
1332
1530
|
offset: options?.offset ?? 0
|
|
1333
1531
|
};
|
|
1532
|
+
if (this.#userTokenGetter) {
|
|
1533
|
+
try {
|
|
1534
|
+
listBody.user_token = await this.#getUserToken();
|
|
1535
|
+
} catch (err) {
|
|
1536
|
+
console.warn(
|
|
1537
|
+
"user_token_getter failed in listConnections, falling back to un-scoped listing:",
|
|
1538
|
+
err instanceof Error ? err.message : String(err)
|
|
1539
|
+
);
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1334
1542
|
const listPath = "/sdk/oauth/connections/list";
|
|
1335
|
-
const
|
|
1543
|
+
const listBodyStr = JSON.stringify(listBody);
|
|
1544
|
+
const listHmac = this.#computeHmacHeaders("POST", listPath, listBodyStr);
|
|
1336
1545
|
try {
|
|
1337
1546
|
response = await this.#alterClient.post(listPath, {
|
|
1338
|
-
|
|
1339
|
-
headers: { ...actorHeaders, ...listHmac }
|
|
1547
|
+
body: listBodyStr,
|
|
1548
|
+
headers: { ...actorHeaders, ...listHmac, "Content-Type": "application/json" }
|
|
1340
1549
|
});
|
|
1341
1550
|
} catch (error) {
|
|
1342
1551
|
if (_AlterVault.#isTimeoutOrAbortError(error)) {
|
|
@@ -1382,24 +1591,37 @@ ${contentHash}`;
|
|
|
1382
1591
|
"SDK instance has been closed. Create a new AlterVault instance to make requests."
|
|
1383
1592
|
);
|
|
1384
1593
|
}
|
|
1385
|
-
if (!options.endUser?.id) {
|
|
1386
|
-
throw new AlterSDKError("endUser.id is required");
|
|
1387
|
-
}
|
|
1388
1594
|
const actorHeaders = this.#getActorRequestHeaders();
|
|
1389
1595
|
let response;
|
|
1390
1596
|
const sessionBody = {
|
|
1391
|
-
end_user: options.endUser,
|
|
1392
1597
|
allowed_providers: options.allowedProviders ?? null,
|
|
1393
1598
|
return_url: options.returnUrl ?? null,
|
|
1394
1599
|
allowed_origin: options.allowedOrigin ?? null,
|
|
1395
1600
|
metadata: options.metadata ?? null
|
|
1396
1601
|
};
|
|
1602
|
+
if (options.connectionPolicy) {
|
|
1603
|
+
sessionBody.connection_policy = {
|
|
1604
|
+
max_ttl_seconds: options.connectionPolicy.maxTtlSeconds ?? null,
|
|
1605
|
+
default_ttl_seconds: options.connectionPolicy.defaultTtlSeconds ?? null
|
|
1606
|
+
};
|
|
1607
|
+
}
|
|
1608
|
+
if (this.#userTokenGetter) {
|
|
1609
|
+
try {
|
|
1610
|
+
sessionBody.user_token = await this.#getUserToken();
|
|
1611
|
+
} catch (err) {
|
|
1612
|
+
console.warn(
|
|
1613
|
+
"userTokenGetter failed in createConnectSession, session will not have identity tagging:",
|
|
1614
|
+
err instanceof Error ? err.message : String(err)
|
|
1615
|
+
);
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1397
1618
|
const sessionPath = "/sdk/oauth/connect/session";
|
|
1398
|
-
const
|
|
1619
|
+
const sessionBodyStr = JSON.stringify(sessionBody);
|
|
1620
|
+
const sessionHmac = this.#computeHmacHeaders("POST", sessionPath, sessionBodyStr);
|
|
1399
1621
|
try {
|
|
1400
1622
|
response = await this.#alterClient.post(sessionPath, {
|
|
1401
|
-
|
|
1402
|
-
headers: { ...actorHeaders, ...sessionHmac }
|
|
1623
|
+
body: sessionBodyStr,
|
|
1624
|
+
headers: { ...actorHeaders, ...sessionHmac, "Content-Type": "application/json" }
|
|
1403
1625
|
});
|
|
1404
1626
|
} catch (error) {
|
|
1405
1627
|
if (_AlterVault.#isTimeoutOrAbortError(error)) {
|
|
@@ -1430,7 +1652,7 @@ ${contentHash}`;
|
|
|
1430
1652
|
* server-side applications. It creates a Connect session, opens the
|
|
1431
1653
|
* browser, and polls until the user completes OAuth.
|
|
1432
1654
|
*
|
|
1433
|
-
* @param options - Connect options
|
|
1655
|
+
* @param options - Connect options
|
|
1434
1656
|
* @returns Array of ConnectResult objects (one per connected provider)
|
|
1435
1657
|
* @throws ConnectTimeoutError if the user doesn't complete within timeout
|
|
1436
1658
|
* @throws ConnectFlowError if the user denies or provider returns error
|
|
@@ -1446,8 +1668,8 @@ ${contentHash}`;
|
|
|
1446
1668
|
const pollInterval = options.pollInterval ?? 2;
|
|
1447
1669
|
const openBrowser = options.openBrowser ?? true;
|
|
1448
1670
|
const session = await this.createConnectSession({
|
|
1449
|
-
|
|
1450
|
-
|
|
1671
|
+
allowedProviders: options.providers,
|
|
1672
|
+
connectionPolicy: options.connectionPolicy
|
|
1451
1673
|
});
|
|
1452
1674
|
if (openBrowser) {
|
|
1453
1675
|
try {
|
|
@@ -1484,18 +1706,28 @@ ${contentHash}`;
|
|
|
1484
1706
|
connection_id: conn.connection_id ?? "",
|
|
1485
1707
|
provider_id: conn.provider_id ?? "",
|
|
1486
1708
|
account_identifier: conn.account_identifier ?? null,
|
|
1487
|
-
scopes: conn.scopes ?? []
|
|
1709
|
+
scopes: conn.scopes ?? [],
|
|
1710
|
+
connection_policy: conn.connection_policy ?? null
|
|
1488
1711
|
})
|
|
1489
1712
|
);
|
|
1490
1713
|
}
|
|
1491
1714
|
if (pollStatus === "error") {
|
|
1492
1715
|
const err = pollResult.error;
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1716
|
+
const errorCode = err?.error_code ?? "unknown_error";
|
|
1717
|
+
const errorMessage = err?.error_message ?? "OAuth flow failed";
|
|
1718
|
+
const errorDetails = { error_code: errorCode };
|
|
1719
|
+
if (errorCode === "connect_denied") {
|
|
1720
|
+
throw new ConnectDeniedError(errorMessage, errorDetails);
|
|
1721
|
+
}
|
|
1722
|
+
if ([
|
|
1723
|
+
"connect_config_error",
|
|
1724
|
+
"invalid_redirect_uri",
|
|
1725
|
+
"invalid_client",
|
|
1726
|
+
"unauthorized_client"
|
|
1727
|
+
].includes(errorCode)) {
|
|
1728
|
+
throw new ConnectConfigError(errorMessage, errorDetails);
|
|
1729
|
+
}
|
|
1730
|
+
throw new ConnectFlowError(errorMessage, errorDetails);
|
|
1499
1731
|
}
|
|
1500
1732
|
if (pollStatus === "expired") {
|
|
1501
1733
|
throw new ConnectFlowError(
|
|
@@ -1517,16 +1749,17 @@ ${contentHash}`;
|
|
|
1517
1749
|
const actorHeaders = this.#getActorRequestHeaders();
|
|
1518
1750
|
const pollPath = "/sdk/oauth/connect/session/poll";
|
|
1519
1751
|
const pollBody = { session_token: sessionToken };
|
|
1752
|
+
const pollBodyStr = JSON.stringify(pollBody);
|
|
1520
1753
|
const pollHmac = this.#computeHmacHeaders(
|
|
1521
1754
|
"POST",
|
|
1522
1755
|
pollPath,
|
|
1523
|
-
|
|
1756
|
+
pollBodyStr
|
|
1524
1757
|
);
|
|
1525
1758
|
let response;
|
|
1526
1759
|
try {
|
|
1527
1760
|
response = await this.#alterClient.post(pollPath, {
|
|
1528
|
-
|
|
1529
|
-
headers: { ...actorHeaders, ...pollHmac }
|
|
1761
|
+
body: pollBodyStr,
|
|
1762
|
+
headers: { ...actorHeaders, ...pollHmac, "Content-Type": "application/json" }
|
|
1530
1763
|
});
|
|
1531
1764
|
} catch (error) {
|
|
1532
1765
|
if (_AlterVault.#isTimeoutOrAbortError(error)) {
|
|
@@ -1574,10 +1807,72 @@ Object.freeze(AlterVault.prototype);
|
|
|
1574
1807
|
|
|
1575
1808
|
// src/providers/enums.ts
|
|
1576
1809
|
var Provider = /* @__PURE__ */ ((Provider2) => {
|
|
1577
|
-
Provider2["
|
|
1810
|
+
Provider2["ACUITY_SCHEDULING"] = "acuity-scheduling";
|
|
1811
|
+
Provider2["ADOBE"] = "adobe";
|
|
1812
|
+
Provider2["AIRCALL"] = "aircall";
|
|
1813
|
+
Provider2["AIRTABLE"] = "airtable";
|
|
1814
|
+
Provider2["APOLLO"] = "apollo";
|
|
1815
|
+
Provider2["ASANA"] = "asana";
|
|
1816
|
+
Provider2["ATLASSIAN"] = "atlassian";
|
|
1817
|
+
Provider2["ATTIO"] = "attio";
|
|
1818
|
+
Provider2["AUTODESK"] = "autodesk";
|
|
1819
|
+
Provider2["BASECAMP"] = "basecamp";
|
|
1820
|
+
Provider2["BITBUCKET"] = "bitbucket";
|
|
1821
|
+
Provider2["BITLY"] = "bitly";
|
|
1822
|
+
Provider2["BOX"] = "box";
|
|
1823
|
+
Provider2["BREX"] = "brex";
|
|
1824
|
+
Provider2["CALENDLY"] = "calendly";
|
|
1825
|
+
Provider2["CAL_COM"] = "cal-com";
|
|
1826
|
+
Provider2["CANVA"] = "canva";
|
|
1827
|
+
Provider2["CLICKUP"] = "clickup";
|
|
1828
|
+
Provider2["CLOSE"] = "close";
|
|
1829
|
+
Provider2["CONSTANT_CONTACT"] = "constant-contact";
|
|
1830
|
+
Provider2["CONTENTFUL"] = "contentful";
|
|
1831
|
+
Provider2["DEEL"] = "deel";
|
|
1832
|
+
Provider2["DIALPAD"] = "dialpad";
|
|
1833
|
+
Provider2["DIGITALOCEAN"] = "digitalocean";
|
|
1834
|
+
Provider2["DISCORD"] = "discord";
|
|
1835
|
+
Provider2["DOCUSIGN"] = "docusign";
|
|
1836
|
+
Provider2["DROPBOX"] = "dropbox";
|
|
1837
|
+
Provider2["EBAY"] = "ebay";
|
|
1838
|
+
Provider2["EVENTBRITE"] = "eventbrite";
|
|
1839
|
+
Provider2["FACEBOOK"] = "facebook";
|
|
1840
|
+
Provider2["FIGMA"] = "figma";
|
|
1578
1841
|
Provider2["GITHUB"] = "github";
|
|
1579
|
-
Provider2["
|
|
1842
|
+
Provider2["GOOGLE"] = "google";
|
|
1843
|
+
Provider2["HUBSPOT"] = "hubspot";
|
|
1844
|
+
Provider2["INSTAGRAM"] = "instagram";
|
|
1845
|
+
Provider2["LINEAR"] = "linear";
|
|
1846
|
+
Provider2["LINKEDIN"] = "linkedin";
|
|
1847
|
+
Provider2["MAILCHIMP"] = "mailchimp";
|
|
1848
|
+
Provider2["MERCURY"] = "mercury";
|
|
1849
|
+
Provider2["MICROSOFT"] = "microsoft";
|
|
1850
|
+
Provider2["MIRO"] = "miro";
|
|
1851
|
+
Provider2["MONDAY"] = "monday";
|
|
1852
|
+
Provider2["NOTION"] = "notion";
|
|
1853
|
+
Provider2["OUTREACH"] = "outreach";
|
|
1854
|
+
Provider2["PAGERDUTY"] = "pagerduty";
|
|
1855
|
+
Provider2["PAYPAL"] = "paypal";
|
|
1856
|
+
Provider2["PINTEREST"] = "pinterest";
|
|
1857
|
+
Provider2["PIPEDRIVE"] = "pipedrive";
|
|
1858
|
+
Provider2["QUICKBOOKS"] = "quickbooks";
|
|
1859
|
+
Provider2["RAMP"] = "ramp";
|
|
1860
|
+
Provider2["REDDIT"] = "reddit";
|
|
1861
|
+
Provider2["RINGCENTRAL"] = "ringcentral";
|
|
1862
|
+
Provider2["SALESFORCE"] = "salesforce";
|
|
1580
1863
|
Provider2["SENTRY"] = "sentry";
|
|
1864
|
+
Provider2["SLACK"] = "slack";
|
|
1865
|
+
Provider2["SNAPCHAT"] = "snapchat";
|
|
1866
|
+
Provider2["SPOTIFY"] = "spotify";
|
|
1867
|
+
Provider2["SQUARE"] = "square";
|
|
1868
|
+
Provider2["SQUARESPACE"] = "squarespace";
|
|
1869
|
+
Provider2["STRIPE"] = "stripe";
|
|
1870
|
+
Provider2["TIKTOK"] = "tiktok";
|
|
1871
|
+
Provider2["TODOIST"] = "todoist";
|
|
1872
|
+
Provider2["TWITTER"] = "twitter";
|
|
1873
|
+
Provider2["TYPEFORM"] = "typeform";
|
|
1874
|
+
Provider2["WEBEX"] = "webex";
|
|
1875
|
+
Provider2["WEBFLOW"] = "webflow";
|
|
1581
1876
|
return Provider2;
|
|
1582
1877
|
})(Provider || {});
|
|
1583
1878
|
var HttpMethod = /* @__PURE__ */ ((HttpMethod2) => {
|
|
@@ -1596,20 +1891,26 @@ var HttpMethod = /* @__PURE__ */ ((HttpMethod2) => {
|
|
|
1596
1891
|
ActorType,
|
|
1597
1892
|
AlterSDKError,
|
|
1598
1893
|
AlterVault,
|
|
1894
|
+
BackendError,
|
|
1895
|
+
ConnectConfigError,
|
|
1896
|
+
ConnectDeniedError,
|
|
1599
1897
|
ConnectFlowError,
|
|
1600
1898
|
ConnectResult,
|
|
1601
1899
|
ConnectSession,
|
|
1602
1900
|
ConnectTimeoutError,
|
|
1901
|
+
ConnectionDeletedError,
|
|
1902
|
+
ConnectionExpiredError,
|
|
1603
1903
|
ConnectionInfo,
|
|
1604
1904
|
ConnectionListResult,
|
|
1605
1905
|
ConnectionNotFoundError,
|
|
1906
|
+
ConnectionRevokedError,
|
|
1606
1907
|
HttpMethod,
|
|
1607
1908
|
NetworkError,
|
|
1608
1909
|
PolicyViolationError,
|
|
1609
1910
|
Provider,
|
|
1610
1911
|
ProviderAPIError,
|
|
1912
|
+
ReAuthRequiredError,
|
|
1913
|
+
ScopeReauthRequiredError,
|
|
1611
1914
|
TimeoutError,
|
|
1612
|
-
|
|
1613
|
-
TokenResponse,
|
|
1614
|
-
TokenRetrievalError
|
|
1915
|
+
TokenResponse
|
|
1615
1916
|
});
|