@everworker/oneringai 0.4.2 → 0.4.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/README.md +30 -8
- package/dist/{IProvider-CNJqZItJ.d.cts → IProvider-B8sqUzJG.d.cts} +36 -6
- package/dist/{IProvider-B6hqVVq8.d.ts → IProvider-CxDUGl6n.d.ts} +36 -6
- package/dist/{ImageModel-B64HX3lN.d.cts → ImageModel-Ds5_6sf7.d.cts} +1 -1
- package/dist/{ImageModel-DU-y_WOb.d.ts → ImageModel-OWbA277F.d.ts} +1 -1
- package/dist/capabilities/agents/index.d.cts +2 -2
- package/dist/capabilities/agents/index.d.ts +2 -2
- package/dist/capabilities/images/index.cjs +251 -106
- package/dist/capabilities/images/index.cjs.map +1 -1
- package/dist/capabilities/images/index.d.cts +2 -2
- package/dist/capabilities/images/index.d.ts +2 -2
- package/dist/capabilities/images/index.js +251 -106
- package/dist/capabilities/images/index.js.map +1 -1
- package/dist/{index-9VOnAX17.d.ts → index-CEjKTeSb.d.cts} +857 -2
- package/dist/{index-BMjyFNJQ.d.cts → index-CzGnmqOs.d.ts} +857 -2
- package/dist/index.cjs +1303 -432
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +234 -904
- package/dist/index.d.ts +234 -904
- package/dist/index.js +1286 -417
- package/dist/index.js.map +1 -1
- package/dist/shared/index.cjs +9 -0
- package/dist/shared/index.cjs.map +1 -1
- package/dist/shared/index.js +9 -0
- package/dist/shared/index.js.map +1 -1
- package/package.json +2 -1
|
@@ -92,6 +92,12 @@ var MemoryStorage = class {
|
|
|
92
92
|
size() {
|
|
93
93
|
return this.tokens.size;
|
|
94
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* List all storage keys (for account enumeration)
|
|
97
|
+
*/
|
|
98
|
+
async listKeys() {
|
|
99
|
+
return Array.from(this.tokens.keys());
|
|
100
|
+
}
|
|
95
101
|
};
|
|
96
102
|
|
|
97
103
|
// src/connectors/oauth/domain/TokenStore.ts
|
|
@@ -103,14 +109,23 @@ var TokenStore = class {
|
|
|
103
109
|
this.storage = storage || new MemoryStorage();
|
|
104
110
|
}
|
|
105
111
|
/**
|
|
106
|
-
* Get user-scoped storage key
|
|
107
|
-
*
|
|
108
|
-
*
|
|
112
|
+
* Get user-scoped (and optionally account-scoped) storage key
|
|
113
|
+
*
|
|
114
|
+
* Key format (backward compatible):
|
|
115
|
+
* - No userId, no accountId → baseKey
|
|
116
|
+
* - userId only → baseKey:userId
|
|
117
|
+
* - userId + accountId → baseKey:userId:accountId
|
|
118
|
+
* - accountId only → baseKey:default:accountId
|
|
109
119
|
*
|
|
110
120
|
* @param userId - User identifier (optional, defaults to single-user mode)
|
|
111
|
-
* @
|
|
121
|
+
* @param accountId - Account alias for multi-account support (optional)
|
|
122
|
+
* @returns Storage key scoped to user and account
|
|
112
123
|
*/
|
|
113
|
-
getScopedKey(userId) {
|
|
124
|
+
getScopedKey(userId, accountId) {
|
|
125
|
+
if (accountId) {
|
|
126
|
+
const userPart = userId && userId !== "default" ? userId : "default";
|
|
127
|
+
return `${this.baseStorageKey}:${userPart}:${accountId}`;
|
|
128
|
+
}
|
|
114
129
|
if (!userId || userId === "default") {
|
|
115
130
|
return this.baseStorageKey;
|
|
116
131
|
}
|
|
@@ -120,8 +135,9 @@ var TokenStore = class {
|
|
|
120
135
|
* Store token (encrypted by storage layer)
|
|
121
136
|
* @param tokenResponse - Token response from OAuth provider
|
|
122
137
|
* @param userId - Optional user identifier for multi-user support
|
|
138
|
+
* @param accountId - Optional account alias for multi-account support
|
|
123
139
|
*/
|
|
124
|
-
async storeToken(tokenResponse, userId) {
|
|
140
|
+
async storeToken(tokenResponse, userId, accountId) {
|
|
125
141
|
if (!tokenResponse.access_token) {
|
|
126
142
|
throw new Error("OAuth response missing required access_token field");
|
|
127
143
|
}
|
|
@@ -139,39 +155,46 @@ var TokenStore = class {
|
|
|
139
155
|
scope: tokenResponse.scope,
|
|
140
156
|
obtained_at: Date.now()
|
|
141
157
|
};
|
|
142
|
-
const key = this.getScopedKey(userId);
|
|
158
|
+
const key = this.getScopedKey(userId, accountId);
|
|
143
159
|
await this.storage.storeToken(key, token);
|
|
144
160
|
}
|
|
145
161
|
/**
|
|
146
162
|
* Get access token
|
|
147
163
|
* @param userId - Optional user identifier for multi-user support
|
|
164
|
+
* @param accountId - Optional account alias for multi-account support
|
|
148
165
|
*/
|
|
149
|
-
async getAccessToken(userId) {
|
|
150
|
-
const key = this.getScopedKey(userId);
|
|
166
|
+
async getAccessToken(userId, accountId) {
|
|
167
|
+
const key = this.getScopedKey(userId, accountId);
|
|
151
168
|
const token = await this.storage.getToken(key);
|
|
152
169
|
if (!token) {
|
|
153
|
-
|
|
170
|
+
const userLabel = userId ? `user: ${userId}` : "default user";
|
|
171
|
+
const accountLabel = accountId ? `, account: ${accountId}` : "";
|
|
172
|
+
throw new Error(`No token stored for ${userLabel}${accountLabel}`);
|
|
154
173
|
}
|
|
155
174
|
return token.access_token;
|
|
156
175
|
}
|
|
157
176
|
/**
|
|
158
177
|
* Get refresh token
|
|
159
178
|
* @param userId - Optional user identifier for multi-user support
|
|
179
|
+
* @param accountId - Optional account alias for multi-account support
|
|
160
180
|
*/
|
|
161
|
-
async getRefreshToken(userId) {
|
|
162
|
-
const key = this.getScopedKey(userId);
|
|
181
|
+
async getRefreshToken(userId, accountId) {
|
|
182
|
+
const key = this.getScopedKey(userId, accountId);
|
|
163
183
|
const token = await this.storage.getToken(key);
|
|
164
184
|
if (!token?.refresh_token) {
|
|
165
|
-
|
|
185
|
+
const userLabel = userId ? `user: ${userId}` : "default user";
|
|
186
|
+
const accountLabel = accountId ? `, account: ${accountId}` : "";
|
|
187
|
+
throw new Error(`No refresh token available for ${userLabel}${accountLabel}`);
|
|
166
188
|
}
|
|
167
189
|
return token.refresh_token;
|
|
168
190
|
}
|
|
169
191
|
/**
|
|
170
192
|
* Check if has refresh token
|
|
171
193
|
* @param userId - Optional user identifier for multi-user support
|
|
194
|
+
* @param accountId - Optional account alias for multi-account support
|
|
172
195
|
*/
|
|
173
|
-
async hasRefreshToken(userId) {
|
|
174
|
-
const key = this.getScopedKey(userId);
|
|
196
|
+
async hasRefreshToken(userId, accountId) {
|
|
197
|
+
const key = this.getScopedKey(userId, accountId);
|
|
175
198
|
const token = await this.storage.getToken(key);
|
|
176
199
|
return !!token?.refresh_token;
|
|
177
200
|
}
|
|
@@ -180,9 +203,10 @@ var TokenStore = class {
|
|
|
180
203
|
*
|
|
181
204
|
* @param bufferSeconds - Refresh this many seconds before expiry (default: 300 = 5 min)
|
|
182
205
|
* @param userId - Optional user identifier for multi-user support
|
|
206
|
+
* @param accountId - Optional account alias for multi-account support
|
|
183
207
|
*/
|
|
184
|
-
async isValid(bufferSeconds = 300, userId) {
|
|
185
|
-
const key = this.getScopedKey(userId);
|
|
208
|
+
async isValid(bufferSeconds = 300, userId, accountId) {
|
|
209
|
+
const key = this.getScopedKey(userId, accountId);
|
|
186
210
|
const token = await this.storage.getToken(key);
|
|
187
211
|
if (!token) {
|
|
188
212
|
return false;
|
|
@@ -194,19 +218,46 @@ var TokenStore = class {
|
|
|
194
218
|
/**
|
|
195
219
|
* Clear stored token
|
|
196
220
|
* @param userId - Optional user identifier for multi-user support
|
|
221
|
+
* @param accountId - Optional account alias for multi-account support
|
|
197
222
|
*/
|
|
198
|
-
async clear(userId) {
|
|
199
|
-
const key = this.getScopedKey(userId);
|
|
223
|
+
async clear(userId, accountId) {
|
|
224
|
+
const key = this.getScopedKey(userId, accountId);
|
|
200
225
|
await this.storage.deleteToken(key);
|
|
201
226
|
}
|
|
202
227
|
/**
|
|
203
228
|
* Get full token info
|
|
204
229
|
* @param userId - Optional user identifier for multi-user support
|
|
230
|
+
* @param accountId - Optional account alias for multi-account support
|
|
205
231
|
*/
|
|
206
|
-
async getTokenInfo(userId) {
|
|
207
|
-
const key = this.getScopedKey(userId);
|
|
232
|
+
async getTokenInfo(userId, accountId) {
|
|
233
|
+
const key = this.getScopedKey(userId, accountId);
|
|
208
234
|
return this.storage.getToken(key);
|
|
209
235
|
}
|
|
236
|
+
/**
|
|
237
|
+
* List account aliases for a user on this connector.
|
|
238
|
+
* Returns account IDs that have stored tokens.
|
|
239
|
+
*
|
|
240
|
+
* @param userId - Optional user identifier
|
|
241
|
+
* @returns Array of account aliases (e.g., ['work', 'personal'])
|
|
242
|
+
*/
|
|
243
|
+
async listAccounts(userId) {
|
|
244
|
+
if (!this.storage.listKeys) {
|
|
245
|
+
return [];
|
|
246
|
+
}
|
|
247
|
+
const allKeys = await this.storage.listKeys();
|
|
248
|
+
const userPart = userId && userId !== "default" ? userId : "default";
|
|
249
|
+
const prefix = `${this.baseStorageKey}:${userPart}:`;
|
|
250
|
+
const accounts = [];
|
|
251
|
+
for (const key of allKeys) {
|
|
252
|
+
if (key.startsWith(prefix)) {
|
|
253
|
+
const accountId = key.slice(prefix.length);
|
|
254
|
+
if (accountId && !accountId.includes(":")) {
|
|
255
|
+
accounts.push(accountId);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return accounts;
|
|
260
|
+
}
|
|
210
261
|
};
|
|
211
262
|
function generatePKCE() {
|
|
212
263
|
const codeVerifier = base64URLEncode(crypto.randomBytes(32));
|
|
@@ -232,20 +283,28 @@ var AuthCodePKCEFlow = class {
|
|
|
232
283
|
this.tokenStore = new TokenStore(storageKey, config.storage);
|
|
233
284
|
}
|
|
234
285
|
tokenStore;
|
|
235
|
-
// Store PKCE data per user with timestamps for cleanup
|
|
286
|
+
// Store PKCE data per user+account with timestamps for cleanup
|
|
236
287
|
codeVerifiers = /* @__PURE__ */ new Map();
|
|
237
288
|
states = /* @__PURE__ */ new Map();
|
|
238
|
-
// Store refresh locks per user to prevent concurrent refresh
|
|
289
|
+
// Store refresh locks per user+account to prevent concurrent refresh
|
|
239
290
|
refreshLocks = /* @__PURE__ */ new Map();
|
|
240
291
|
// PKCE data TTL: 15 minutes (auth flows should complete within this time)
|
|
241
292
|
PKCE_TTL = 15 * 60 * 1e3;
|
|
293
|
+
/**
|
|
294
|
+
* Build a map key from userId and accountId for internal PKCE/state/lock maps.
|
|
295
|
+
*/
|
|
296
|
+
getMapKey(userId, accountId) {
|
|
297
|
+
const userPart = userId || "default";
|
|
298
|
+
return accountId ? `${userPart}:${accountId}` : userPart;
|
|
299
|
+
}
|
|
242
300
|
/**
|
|
243
301
|
* Generate authorization URL for user to visit
|
|
244
302
|
* Opens browser or redirects user to this URL
|
|
245
303
|
*
|
|
246
304
|
* @param userId - User identifier for multi-user support (optional)
|
|
305
|
+
* @param accountId - Account alias for multi-account support (optional)
|
|
247
306
|
*/
|
|
248
|
-
async getAuthorizationUrl(userId) {
|
|
307
|
+
async getAuthorizationUrl(userId, accountId) {
|
|
249
308
|
if (!this.config.authorizationUrl) {
|
|
250
309
|
throw new Error("authorizationUrl is required for authorization_code flow");
|
|
251
310
|
}
|
|
@@ -253,11 +312,11 @@ var AuthCodePKCEFlow = class {
|
|
|
253
312
|
throw new Error("redirectUri is required for authorization_code flow");
|
|
254
313
|
}
|
|
255
314
|
this.cleanupExpiredPKCE();
|
|
256
|
-
const
|
|
315
|
+
const mapKey = this.getMapKey(userId, accountId);
|
|
257
316
|
const { codeVerifier, codeChallenge } = generatePKCE();
|
|
258
|
-
this.codeVerifiers.set(
|
|
317
|
+
this.codeVerifiers.set(mapKey, { verifier: codeVerifier, timestamp: Date.now() });
|
|
259
318
|
const state = generateState();
|
|
260
|
-
this.states.set(
|
|
319
|
+
this.states.set(mapKey, { state, timestamp: Date.now() });
|
|
261
320
|
const params = new URLSearchParams({
|
|
262
321
|
response_type: "code",
|
|
263
322
|
client_id: this.config.clientId,
|
|
@@ -271,33 +330,48 @@ var AuthCodePKCEFlow = class {
|
|
|
271
330
|
params.append("code_challenge", codeChallenge);
|
|
272
331
|
params.append("code_challenge_method", "S256");
|
|
273
332
|
}
|
|
274
|
-
|
|
275
|
-
|
|
333
|
+
let stateWithMetadata = state;
|
|
334
|
+
if (userId || accountId) {
|
|
335
|
+
stateWithMetadata = `${state}::${userId || ""}`;
|
|
336
|
+
if (accountId) {
|
|
337
|
+
stateWithMetadata += `::${accountId}`;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
params.set("state", stateWithMetadata);
|
|
276
341
|
return `${this.config.authorizationUrl}?${params.toString()}`;
|
|
277
342
|
}
|
|
278
343
|
/**
|
|
279
344
|
* Exchange authorization code for access token
|
|
280
345
|
*
|
|
281
346
|
* @param code - Authorization code from callback
|
|
282
|
-
* @param state - State parameter from callback (for CSRF verification, may include userId)
|
|
347
|
+
* @param state - State parameter from callback (for CSRF verification, may include userId/accountId)
|
|
283
348
|
* @param userId - User identifier (optional, can be extracted from state)
|
|
349
|
+
* @param accountId - Account alias (optional, can be extracted from state)
|
|
284
350
|
*/
|
|
285
|
-
async exchangeCode(code, state, userId) {
|
|
351
|
+
async exchangeCode(code, state, userId, accountId) {
|
|
286
352
|
let actualState = state;
|
|
287
353
|
let actualUserId = userId;
|
|
354
|
+
let actualAccountId = accountId;
|
|
288
355
|
if (state.includes("::")) {
|
|
289
356
|
const parts = state.split("::");
|
|
290
357
|
actualState = parts[0];
|
|
291
|
-
actualUserId
|
|
358
|
+
if (!actualUserId && parts[1]) {
|
|
359
|
+
actualUserId = parts[1];
|
|
360
|
+
}
|
|
361
|
+
if (!actualAccountId && parts[2]) {
|
|
362
|
+
actualAccountId = parts[2];
|
|
363
|
+
}
|
|
292
364
|
}
|
|
293
|
-
const
|
|
294
|
-
const stateData = this.states.get(
|
|
365
|
+
const mapKey = this.getMapKey(actualUserId, actualAccountId);
|
|
366
|
+
const stateData = this.states.get(mapKey);
|
|
295
367
|
if (!stateData) {
|
|
296
|
-
|
|
368
|
+
const label = actualAccountId ? `user ${actualUserId}, account ${actualAccountId}` : `user ${actualUserId}`;
|
|
369
|
+
throw new Error(`No PKCE state found for ${label}. Authorization flow may have expired (15 min TTL).`);
|
|
297
370
|
}
|
|
298
371
|
const expectedState = stateData.state;
|
|
299
372
|
if (actualState !== expectedState) {
|
|
300
|
-
|
|
373
|
+
const label = actualAccountId ? `user ${actualUserId}, account ${actualAccountId}` : `user ${actualUserId}`;
|
|
374
|
+
throw new Error(`State mismatch for ${label} - possible CSRF attack. Expected: ${expectedState}, Got: ${actualState}`);
|
|
301
375
|
}
|
|
302
376
|
if (!this.config.redirectUri) {
|
|
303
377
|
throw new Error("redirectUri is required");
|
|
@@ -311,7 +385,7 @@ var AuthCodePKCEFlow = class {
|
|
|
311
385
|
if (this.config.clientSecret) {
|
|
312
386
|
params.append("client_secret", this.config.clientSecret);
|
|
313
387
|
}
|
|
314
|
-
const verifierData = this.codeVerifiers.get(
|
|
388
|
+
const verifierData = this.codeVerifiers.get(mapKey);
|
|
315
389
|
if (this.config.usePKCE !== false && verifierData) {
|
|
316
390
|
params.append("code_verifier", verifierData.verifier);
|
|
317
391
|
}
|
|
@@ -345,39 +419,43 @@ var AuthCodePKCEFlow = class {
|
|
|
345
419
|
throw new Error(`Token exchange failed: ${response.status} ${response.statusText} - ${error}`);
|
|
346
420
|
}
|
|
347
421
|
const data = await response.json();
|
|
348
|
-
await this.tokenStore.storeToken(data, actualUserId);
|
|
349
|
-
this.codeVerifiers.delete(
|
|
350
|
-
this.states.delete(
|
|
422
|
+
await this.tokenStore.storeToken(data, actualUserId, actualAccountId);
|
|
423
|
+
this.codeVerifiers.delete(mapKey);
|
|
424
|
+
this.states.delete(mapKey);
|
|
351
425
|
}
|
|
352
426
|
/**
|
|
353
427
|
* Get valid token (auto-refreshes if needed)
|
|
354
428
|
* @param userId - User identifier for multi-user support
|
|
429
|
+
* @param accountId - Account alias for multi-account support
|
|
355
430
|
*/
|
|
356
|
-
async getToken(userId) {
|
|
357
|
-
const
|
|
358
|
-
if (this.refreshLocks.has(
|
|
359
|
-
return this.refreshLocks.get(
|
|
431
|
+
async getToken(userId, accountId) {
|
|
432
|
+
const mapKey = this.getMapKey(userId, accountId);
|
|
433
|
+
if (this.refreshLocks.has(mapKey)) {
|
|
434
|
+
return this.refreshLocks.get(mapKey);
|
|
360
435
|
}
|
|
361
|
-
if (await this.tokenStore.isValid(this.config.refreshBeforeExpiry, userId)) {
|
|
362
|
-
return this.tokenStore.getAccessToken(userId);
|
|
436
|
+
if (await this.tokenStore.isValid(this.config.refreshBeforeExpiry, userId, accountId)) {
|
|
437
|
+
return this.tokenStore.getAccessToken(userId, accountId);
|
|
363
438
|
}
|
|
364
|
-
if (await this.tokenStore.hasRefreshToken(userId)) {
|
|
365
|
-
const refreshPromise = this.refreshToken(userId);
|
|
366
|
-
this.refreshLocks.set(
|
|
439
|
+
if (await this.tokenStore.hasRefreshToken(userId, accountId)) {
|
|
440
|
+
const refreshPromise = this.refreshToken(userId, accountId);
|
|
441
|
+
this.refreshLocks.set(mapKey, refreshPromise);
|
|
367
442
|
try {
|
|
368
443
|
return await refreshPromise;
|
|
369
444
|
} finally {
|
|
370
|
-
this.refreshLocks.delete(
|
|
445
|
+
this.refreshLocks.delete(mapKey);
|
|
371
446
|
}
|
|
372
447
|
}
|
|
373
|
-
|
|
448
|
+
const userLabel = userId ? `user: ${userId}` : "default user";
|
|
449
|
+
const accountLabel = accountId ? `, account: ${accountId}` : "";
|
|
450
|
+
throw new Error(`No valid token available for ${userLabel}${accountLabel}. User needs to authorize (call startAuthFlow).`);
|
|
374
451
|
}
|
|
375
452
|
/**
|
|
376
453
|
* Refresh access token using refresh token
|
|
377
454
|
* @param userId - User identifier for multi-user support
|
|
455
|
+
* @param accountId - Account alias for multi-account support
|
|
378
456
|
*/
|
|
379
|
-
async refreshToken(userId) {
|
|
380
|
-
const refreshToken = await this.tokenStore.getRefreshToken(userId);
|
|
457
|
+
async refreshToken(userId, accountId) {
|
|
458
|
+
const refreshToken = await this.tokenStore.getRefreshToken(userId, accountId);
|
|
381
459
|
const params = new URLSearchParams({
|
|
382
460
|
grant_type: "refresh_token",
|
|
383
461
|
refresh_token: refreshToken,
|
|
@@ -416,28 +494,30 @@ var AuthCodePKCEFlow = class {
|
|
|
416
494
|
throw new Error(`Token refresh failed: ${response.status} ${response.statusText} - ${error}`);
|
|
417
495
|
}
|
|
418
496
|
const data = await response.json();
|
|
419
|
-
await this.tokenStore.storeToken(data, userId);
|
|
497
|
+
await this.tokenStore.storeToken(data, userId, accountId);
|
|
420
498
|
return data.access_token;
|
|
421
499
|
}
|
|
422
500
|
/**
|
|
423
501
|
* Check if token is valid
|
|
424
502
|
* @param userId - User identifier for multi-user support
|
|
503
|
+
* @param accountId - Account alias for multi-account support
|
|
425
504
|
*/
|
|
426
|
-
async isTokenValid(userId) {
|
|
427
|
-
return this.tokenStore.isValid(this.config.refreshBeforeExpiry, userId);
|
|
505
|
+
async isTokenValid(userId, accountId) {
|
|
506
|
+
return this.tokenStore.isValid(this.config.refreshBeforeExpiry, userId, accountId);
|
|
428
507
|
}
|
|
429
508
|
/**
|
|
430
509
|
* Revoke token (if supported by provider)
|
|
431
510
|
* @param revocationUrl - Optional revocation endpoint
|
|
432
511
|
* @param userId - User identifier for multi-user support
|
|
512
|
+
* @param accountId - Account alias for multi-account support
|
|
433
513
|
*/
|
|
434
|
-
async revokeToken(revocationUrl, userId) {
|
|
514
|
+
async revokeToken(revocationUrl, userId, accountId) {
|
|
435
515
|
if (!revocationUrl) {
|
|
436
|
-
await this.tokenStore.clear(userId);
|
|
516
|
+
await this.tokenStore.clear(userId, accountId);
|
|
437
517
|
return;
|
|
438
518
|
}
|
|
439
519
|
try {
|
|
440
|
-
const token = await this.tokenStore.getAccessToken(userId);
|
|
520
|
+
const token = await this.tokenStore.getAccessToken(userId, accountId);
|
|
441
521
|
await fetch(revocationUrl, {
|
|
442
522
|
method: "POST",
|
|
443
523
|
headers: {
|
|
@@ -449,9 +529,16 @@ var AuthCodePKCEFlow = class {
|
|
|
449
529
|
})
|
|
450
530
|
});
|
|
451
531
|
} finally {
|
|
452
|
-
await this.tokenStore.clear(userId);
|
|
532
|
+
await this.tokenStore.clear(userId, accountId);
|
|
453
533
|
}
|
|
454
534
|
}
|
|
535
|
+
/**
|
|
536
|
+
* List account aliases for a user.
|
|
537
|
+
* @param userId - User identifier (optional)
|
|
538
|
+
*/
|
|
539
|
+
async listAccounts(userId) {
|
|
540
|
+
return this.tokenStore.listAccounts(userId);
|
|
541
|
+
}
|
|
455
542
|
/**
|
|
456
543
|
* Clean up expired PKCE data to prevent memory leaks
|
|
457
544
|
* Removes verifiers and states older than PKCE_TTL (15 minutes)
|
|
@@ -481,17 +568,19 @@ var ClientCredentialsFlow = class {
|
|
|
481
568
|
tokenStore;
|
|
482
569
|
/**
|
|
483
570
|
* Get token using client credentials
|
|
571
|
+
* @param userId - User identifier for multi-user support (optional, rarely used for client_credentials)
|
|
572
|
+
* @param accountId - Account alias for multi-account support (optional)
|
|
484
573
|
*/
|
|
485
|
-
async getToken() {
|
|
486
|
-
if (await this.tokenStore.isValid(this.config.refreshBeforeExpiry)) {
|
|
487
|
-
return this.tokenStore.getAccessToken();
|
|
574
|
+
async getToken(userId, accountId) {
|
|
575
|
+
if (await this.tokenStore.isValid(this.config.refreshBeforeExpiry, userId, accountId)) {
|
|
576
|
+
return this.tokenStore.getAccessToken(userId, accountId);
|
|
488
577
|
}
|
|
489
|
-
return this.requestToken();
|
|
578
|
+
return this.requestToken(userId, accountId);
|
|
490
579
|
}
|
|
491
580
|
/**
|
|
492
581
|
* Request a new token from the authorization server
|
|
493
582
|
*/
|
|
494
|
-
async requestToken() {
|
|
583
|
+
async requestToken(userId, accountId) {
|
|
495
584
|
const auth = Buffer.from(`${this.config.clientId}:${this.config.clientSecret}`).toString(
|
|
496
585
|
"base64"
|
|
497
586
|
);
|
|
@@ -514,22 +603,26 @@ var ClientCredentialsFlow = class {
|
|
|
514
603
|
throw new Error(`Token request failed: ${response.status} ${response.statusText} - ${error}`);
|
|
515
604
|
}
|
|
516
605
|
const data = await response.json();
|
|
517
|
-
await this.tokenStore.storeToken(data);
|
|
606
|
+
await this.tokenStore.storeToken(data, userId, accountId);
|
|
518
607
|
return data.access_token;
|
|
519
608
|
}
|
|
520
609
|
/**
|
|
521
610
|
* Refresh token (client credentials don't use refresh tokens)
|
|
522
611
|
* Just requests a new token
|
|
612
|
+
* @param userId - User identifier for multi-user support (optional)
|
|
613
|
+
* @param accountId - Account alias for multi-account support (optional)
|
|
523
614
|
*/
|
|
524
|
-
async refreshToken() {
|
|
525
|
-
await this.tokenStore.clear();
|
|
526
|
-
return this.requestToken();
|
|
615
|
+
async refreshToken(userId, accountId) {
|
|
616
|
+
await this.tokenStore.clear(userId, accountId);
|
|
617
|
+
return this.requestToken(userId, accountId);
|
|
527
618
|
}
|
|
528
619
|
/**
|
|
529
620
|
* Check if token is valid
|
|
621
|
+
* @param userId - User identifier for multi-user support (optional)
|
|
622
|
+
* @param accountId - Account alias for multi-account support (optional)
|
|
530
623
|
*/
|
|
531
|
-
async isTokenValid() {
|
|
532
|
-
return this.tokenStore.isValid(this.config.refreshBeforeExpiry);
|
|
624
|
+
async isTokenValid(userId, accountId) {
|
|
625
|
+
return this.tokenStore.isValid(this.config.refreshBeforeExpiry, userId, accountId);
|
|
533
626
|
}
|
|
534
627
|
};
|
|
535
628
|
var JWTBearerFlow = class {
|
|
@@ -565,17 +658,19 @@ var JWTBearerFlow = class {
|
|
|
565
658
|
}
|
|
566
659
|
/**
|
|
567
660
|
* Get token using JWT Bearer assertion
|
|
661
|
+
* @param userId - User identifier for multi-user support (optional)
|
|
662
|
+
* @param accountId - Account alias for multi-account support (optional)
|
|
568
663
|
*/
|
|
569
|
-
async getToken() {
|
|
570
|
-
if (await this.tokenStore.isValid(this.config.refreshBeforeExpiry)) {
|
|
571
|
-
return this.tokenStore.getAccessToken();
|
|
664
|
+
async getToken(userId, accountId) {
|
|
665
|
+
if (await this.tokenStore.isValid(this.config.refreshBeforeExpiry, userId, accountId)) {
|
|
666
|
+
return this.tokenStore.getAccessToken(userId, accountId);
|
|
572
667
|
}
|
|
573
|
-
return this.requestToken();
|
|
668
|
+
return this.requestToken(userId, accountId);
|
|
574
669
|
}
|
|
575
670
|
/**
|
|
576
671
|
* Request token using JWT assertion
|
|
577
672
|
*/
|
|
578
|
-
async requestToken() {
|
|
673
|
+
async requestToken(userId, accountId) {
|
|
579
674
|
const assertion = await this.generateJWT();
|
|
580
675
|
const params = new URLSearchParams({
|
|
581
676
|
grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
|
@@ -593,21 +688,25 @@ var JWTBearerFlow = class {
|
|
|
593
688
|
throw new Error(`JWT Bearer token request failed: ${response.status} ${response.statusText} - ${error}`);
|
|
594
689
|
}
|
|
595
690
|
const data = await response.json();
|
|
596
|
-
await this.tokenStore.storeToken(data);
|
|
691
|
+
await this.tokenStore.storeToken(data, userId, accountId);
|
|
597
692
|
return data.access_token;
|
|
598
693
|
}
|
|
599
694
|
/**
|
|
600
695
|
* Refresh token (generate new JWT and request new token)
|
|
696
|
+
* @param userId - User identifier for multi-user support (optional)
|
|
697
|
+
* @param accountId - Account alias for multi-account support (optional)
|
|
601
698
|
*/
|
|
602
|
-
async refreshToken() {
|
|
603
|
-
await this.tokenStore.clear();
|
|
604
|
-
return this.requestToken();
|
|
699
|
+
async refreshToken(userId, accountId) {
|
|
700
|
+
await this.tokenStore.clear(userId, accountId);
|
|
701
|
+
return this.requestToken(userId, accountId);
|
|
605
702
|
}
|
|
606
703
|
/**
|
|
607
704
|
* Check if token is valid
|
|
705
|
+
* @param userId - User identifier for multi-user support (optional)
|
|
706
|
+
* @param accountId - Account alias for multi-account support (optional)
|
|
608
707
|
*/
|
|
609
|
-
async isTokenValid() {
|
|
610
|
-
return this.tokenStore.isValid(this.config.refreshBeforeExpiry);
|
|
708
|
+
async isTokenValid(userId, accountId) {
|
|
709
|
+
return this.tokenStore.isValid(this.config.refreshBeforeExpiry, userId, accountId);
|
|
611
710
|
}
|
|
612
711
|
};
|
|
613
712
|
|
|
@@ -673,25 +772,28 @@ var OAuthManager = class {
|
|
|
673
772
|
* Automatically refreshes if expired
|
|
674
773
|
*
|
|
675
774
|
* @param userId - User identifier for multi-user support (optional)
|
|
775
|
+
* @param accountId - Account alias for multi-account support (optional)
|
|
676
776
|
*/
|
|
677
|
-
async getToken(userId) {
|
|
678
|
-
return this.flow.getToken(userId);
|
|
777
|
+
async getToken(userId, accountId) {
|
|
778
|
+
return this.flow.getToken(userId, accountId);
|
|
679
779
|
}
|
|
680
780
|
/**
|
|
681
781
|
* Force refresh the token
|
|
682
782
|
*
|
|
683
783
|
* @param userId - User identifier for multi-user support (optional)
|
|
784
|
+
* @param accountId - Account alias for multi-account support (optional)
|
|
684
785
|
*/
|
|
685
|
-
async refreshToken(userId) {
|
|
686
|
-
return this.flow.refreshToken(userId);
|
|
786
|
+
async refreshToken(userId, accountId) {
|
|
787
|
+
return this.flow.refreshToken(userId, accountId);
|
|
687
788
|
}
|
|
688
789
|
/**
|
|
689
790
|
* Check if current token is valid
|
|
690
791
|
*
|
|
691
792
|
* @param userId - User identifier for multi-user support (optional)
|
|
793
|
+
* @param accountId - Account alias for multi-account support (optional)
|
|
692
794
|
*/
|
|
693
|
-
async isTokenValid(userId) {
|
|
694
|
-
return this.flow.isTokenValid(userId);
|
|
795
|
+
async isTokenValid(userId, accountId) {
|
|
796
|
+
return this.flow.isTokenValid(userId, accountId);
|
|
695
797
|
}
|
|
696
798
|
// ==================== Authorization Code Flow Methods ====================
|
|
697
799
|
/**
|
|
@@ -699,13 +801,14 @@ var OAuthManager = class {
|
|
|
699
801
|
* Returns URL for user to visit
|
|
700
802
|
*
|
|
701
803
|
* @param userId - User identifier for multi-user support (optional)
|
|
804
|
+
* @param accountId - Account alias for multi-account support (optional)
|
|
702
805
|
* @returns Authorization URL for the user to visit
|
|
703
806
|
*/
|
|
704
|
-
async startAuthFlow(userId) {
|
|
807
|
+
async startAuthFlow(userId, accountId) {
|
|
705
808
|
if (!(this.flow instanceof AuthCodePKCEFlow)) {
|
|
706
809
|
throw new Error("startAuthFlow() is only available for authorization_code flow");
|
|
707
810
|
}
|
|
708
|
-
return this.flow.getAuthorizationUrl(userId);
|
|
811
|
+
return this.flow.getAuthorizationUrl(userId, accountId);
|
|
709
812
|
}
|
|
710
813
|
/**
|
|
711
814
|
* Handle OAuth callback (Authorization Code only)
|
|
@@ -713,8 +816,9 @@ var OAuthManager = class {
|
|
|
713
816
|
*
|
|
714
817
|
* @param callbackUrl - Full callback URL with code and state parameters
|
|
715
818
|
* @param userId - Optional user identifier (can be extracted from state if embedded)
|
|
819
|
+
* @param accountId - Optional account alias (can be extracted from state if embedded)
|
|
716
820
|
*/
|
|
717
|
-
async handleCallback(callbackUrl, userId) {
|
|
821
|
+
async handleCallback(callbackUrl, userId, accountId) {
|
|
718
822
|
if (!(this.flow instanceof AuthCodePKCEFlow)) {
|
|
719
823
|
throw new Error("handleCallback() is only available for authorization_code flow");
|
|
720
824
|
}
|
|
@@ -727,21 +831,34 @@ var OAuthManager = class {
|
|
|
727
831
|
if (!state) {
|
|
728
832
|
throw new Error("Missing state parameter in callback URL");
|
|
729
833
|
}
|
|
730
|
-
await this.flow.exchangeCode(code, state, userId);
|
|
834
|
+
await this.flow.exchangeCode(code, state, userId, accountId);
|
|
731
835
|
}
|
|
732
836
|
/**
|
|
733
837
|
* Revoke token (if supported by provider)
|
|
734
838
|
*
|
|
735
839
|
* @param revocationUrl - Optional revocation endpoint URL
|
|
736
840
|
* @param userId - User identifier for multi-user support (optional)
|
|
841
|
+
* @param accountId - Account alias for multi-account support (optional)
|
|
737
842
|
*/
|
|
738
|
-
async revokeToken(revocationUrl, userId) {
|
|
843
|
+
async revokeToken(revocationUrl, userId, accountId) {
|
|
739
844
|
if (this.flow instanceof AuthCodePKCEFlow) {
|
|
740
|
-
await this.flow.revokeToken(revocationUrl, userId);
|
|
845
|
+
await this.flow.revokeToken(revocationUrl, userId, accountId);
|
|
741
846
|
} else {
|
|
742
847
|
throw new Error("Token revocation not implemented for this flow");
|
|
743
848
|
}
|
|
744
849
|
}
|
|
850
|
+
/**
|
|
851
|
+
* List account aliases for a user (Authorization Code only)
|
|
852
|
+
*
|
|
853
|
+
* @param userId - User identifier (optional)
|
|
854
|
+
* @returns Array of account aliases (e.g., ['work', 'personal'])
|
|
855
|
+
*/
|
|
856
|
+
async listAccounts(userId) {
|
|
857
|
+
if (this.flow instanceof AuthCodePKCEFlow) {
|
|
858
|
+
return this.flow.listAccounts(userId);
|
|
859
|
+
}
|
|
860
|
+
return [];
|
|
861
|
+
}
|
|
745
862
|
// ==================== Validation ====================
|
|
746
863
|
validateConfig(config) {
|
|
747
864
|
if (!config.flow) {
|
|
@@ -1857,46 +1974,59 @@ var Connector = class _Connector {
|
|
|
1857
1974
|
/**
|
|
1858
1975
|
* Get the current access token (for OAuth, JWT, or API key)
|
|
1859
1976
|
* Handles automatic refresh if needed
|
|
1977
|
+
*
|
|
1978
|
+
* @param userId - Optional user identifier for multi-user support
|
|
1979
|
+
* @param accountId - Optional account alias for multi-account support (e.g., 'work', 'personal')
|
|
1860
1980
|
*/
|
|
1861
|
-
async getToken(userId) {
|
|
1981
|
+
async getToken(userId, accountId) {
|
|
1862
1982
|
if (this.config.auth.type === "api_key") {
|
|
1863
1983
|
return this.config.auth.apiKey;
|
|
1864
1984
|
}
|
|
1865
1985
|
if (!this.oauthManager) {
|
|
1866
1986
|
throw new Error(`OAuth manager not initialized for connector '${this.name}'`);
|
|
1867
1987
|
}
|
|
1868
|
-
return this.oauthManager.getToken(userId);
|
|
1988
|
+
return this.oauthManager.getToken(userId, accountId);
|
|
1869
1989
|
}
|
|
1870
1990
|
/**
|
|
1871
1991
|
* Start OAuth authorization flow
|
|
1872
1992
|
* Returns the URL to redirect the user to
|
|
1993
|
+
*
|
|
1994
|
+
* @param userId - Optional user identifier for multi-user support
|
|
1995
|
+
* @param accountId - Optional account alias for multi-account support (e.g., 'work', 'personal')
|
|
1873
1996
|
*/
|
|
1874
|
-
async startAuth(userId) {
|
|
1997
|
+
async startAuth(userId, accountId) {
|
|
1875
1998
|
if (!this.oauthManager) {
|
|
1876
1999
|
throw new Error(`Connector '${this.name}' is not an OAuth connector`);
|
|
1877
2000
|
}
|
|
1878
|
-
return this.oauthManager.startAuthFlow(userId);
|
|
2001
|
+
return this.oauthManager.startAuthFlow(userId, accountId);
|
|
1879
2002
|
}
|
|
1880
2003
|
/**
|
|
1881
2004
|
* Handle OAuth callback
|
|
1882
2005
|
* Call this after user is redirected back from OAuth provider
|
|
2006
|
+
*
|
|
2007
|
+
* @param callbackUrl - Full callback URL with code and state parameters
|
|
2008
|
+
* @param userId - Optional user identifier (can be extracted from state if embedded)
|
|
2009
|
+
* @param accountId - Optional account alias (can be extracted from state if embedded)
|
|
1883
2010
|
*/
|
|
1884
|
-
async handleCallback(callbackUrl, userId) {
|
|
2011
|
+
async handleCallback(callbackUrl, userId, accountId) {
|
|
1885
2012
|
if (!this.oauthManager) {
|
|
1886
2013
|
throw new Error(`Connector '${this.name}' is not an OAuth connector`);
|
|
1887
2014
|
}
|
|
1888
|
-
await this.oauthManager.handleCallback(callbackUrl, userId);
|
|
2015
|
+
await this.oauthManager.handleCallback(callbackUrl, userId, accountId);
|
|
1889
2016
|
}
|
|
1890
2017
|
/**
|
|
1891
2018
|
* Check if the connector has a valid token
|
|
2019
|
+
*
|
|
2020
|
+
* @param userId - Optional user identifier for multi-user support
|
|
2021
|
+
* @param accountId - Optional account alias for multi-account support
|
|
1892
2022
|
*/
|
|
1893
|
-
async hasValidToken(userId) {
|
|
2023
|
+
async hasValidToken(userId, accountId) {
|
|
1894
2024
|
try {
|
|
1895
2025
|
if (this.config.auth.type === "api_key") {
|
|
1896
2026
|
return true;
|
|
1897
2027
|
}
|
|
1898
2028
|
if (this.oauthManager) {
|
|
1899
|
-
const token = await this.oauthManager.getToken(userId);
|
|
2029
|
+
const token = await this.oauthManager.getToken(userId, accountId);
|
|
1900
2030
|
return !!token;
|
|
1901
2031
|
}
|
|
1902
2032
|
return false;
|
|
@@ -1904,6 +2034,19 @@ var Connector = class _Connector {
|
|
|
1904
2034
|
return false;
|
|
1905
2035
|
}
|
|
1906
2036
|
}
|
|
2037
|
+
/**
|
|
2038
|
+
* List account aliases for a user on this connector.
|
|
2039
|
+
* Only applicable for OAuth connectors with multi-account support.
|
|
2040
|
+
*
|
|
2041
|
+
* @param userId - Optional user identifier
|
|
2042
|
+
* @returns Array of account aliases (e.g., ['work', 'personal'])
|
|
2043
|
+
*/
|
|
2044
|
+
async listAccounts(userId) {
|
|
2045
|
+
if (!this.oauthManager) {
|
|
2046
|
+
return [];
|
|
2047
|
+
}
|
|
2048
|
+
return this.oauthManager.listAccounts(userId);
|
|
2049
|
+
}
|
|
1907
2050
|
/**
|
|
1908
2051
|
* Get vendor-specific options from config
|
|
1909
2052
|
*/
|
|
@@ -1947,9 +2090,10 @@ var Connector = class _Connector {
|
|
|
1947
2090
|
* @param endpoint - API endpoint (relative to baseURL) or full URL
|
|
1948
2091
|
* @param options - Fetch options with connector-specific settings
|
|
1949
2092
|
* @param userId - Optional user ID for multi-user OAuth
|
|
2093
|
+
* @param accountId - Optional account alias for multi-account OAuth
|
|
1950
2094
|
* @returns Fetch Response
|
|
1951
2095
|
*/
|
|
1952
|
-
async fetch(endpoint, options, userId) {
|
|
2096
|
+
async fetch(endpoint, options, userId, accountId) {
|
|
1953
2097
|
if (this.disposed) {
|
|
1954
2098
|
throw new Error(`Connector '${this.name}' has been disposed`);
|
|
1955
2099
|
}
|
|
@@ -1968,7 +2112,7 @@ var Connector = class _Connector {
|
|
|
1968
2112
|
this.logRequest(url, options);
|
|
1969
2113
|
}
|
|
1970
2114
|
const doFetch = async () => {
|
|
1971
|
-
const token = await this.getToken(userId);
|
|
2115
|
+
const token = await this.getToken(userId, accountId);
|
|
1972
2116
|
const auth = this.config.auth;
|
|
1973
2117
|
let headerName = "Authorization";
|
|
1974
2118
|
let headerValue = `Bearer ${token}`;
|
|
@@ -2080,10 +2224,11 @@ var Connector = class _Connector {
|
|
|
2080
2224
|
* @param endpoint - API endpoint (relative to baseURL) or full URL
|
|
2081
2225
|
* @param options - Fetch options with connector-specific settings
|
|
2082
2226
|
* @param userId - Optional user ID for multi-user OAuth
|
|
2227
|
+
* @param accountId - Optional account alias for multi-account OAuth
|
|
2083
2228
|
* @returns Parsed JSON response
|
|
2084
2229
|
*/
|
|
2085
|
-
async fetchJSON(endpoint, options, userId) {
|
|
2086
|
-
const response = await this.fetch(endpoint, options, userId);
|
|
2230
|
+
async fetchJSON(endpoint, options, userId, accountId) {
|
|
2231
|
+
const response = await this.fetch(endpoint, options, userId, accountId);
|
|
2087
2232
|
const text = await response.text();
|
|
2088
2233
|
let data;
|
|
2089
2234
|
try {
|