integrate-sdk 0.3.14 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -223,20 +223,19 @@ class OAuthHandler {
223
223
  const data = await response.json();
224
224
  return data;
225
225
  }
226
- async handleStatus(provider, sessionToken) {
226
+ async handleStatus(provider, accessToken) {
227
227
  const url = new URL("/oauth/status", this.serverUrl);
228
228
  url.searchParams.set("provider", provider);
229
229
  const response = await fetch(url.toString(), {
230
230
  method: "GET",
231
231
  headers: {
232
- "X-Session-Token": sessionToken
232
+ Authorization: `Bearer ${accessToken}`
233
233
  }
234
234
  });
235
235
  if (!response.ok) {
236
236
  if (response.status === 401) {
237
237
  return {
238
- authorized: false,
239
- provider
238
+ authorized: false
240
239
  };
241
240
  }
242
241
  const error = await response.text();
@@ -245,16 +244,16 @@ class OAuthHandler {
245
244
  const data = await response.json();
246
245
  return data;
247
246
  }
248
- async handleDisconnect(request, sessionToken) {
249
- if (!sessionToken) {
250
- throw new Error("No session token provided. Cannot disconnect provider.");
247
+ async handleDisconnect(request, accessToken) {
248
+ if (!accessToken) {
249
+ throw new Error("No access token provided. Cannot disconnect provider.");
251
250
  }
252
251
  const url = new URL("/oauth/disconnect", this.serverUrl);
253
252
  const response = await fetch(url.toString(), {
254
253
  method: "POST",
255
254
  headers: {
256
255
  "Content-Type": "application/json",
257
- "X-Session-Token": sessionToken
256
+ Authorization: `Bearer ${accessToken}`
258
257
  },
259
258
  body: JSON.stringify({
260
259
  provider: request.provider
@@ -301,14 +300,15 @@ function createNextOAuthHandler(config) {
301
300
  async status(req) {
302
301
  try {
303
302
  const provider = req.nextUrl.searchParams.get("provider");
304
- const sessionToken = req.headers.get("x-session-token");
303
+ const authHeader = req.headers.get("authorization");
305
304
  if (!provider) {
306
305
  return Response.json({ error: "Missing provider query parameter" }, { status: 400 });
307
306
  }
308
- if (!sessionToken) {
309
- return Response.json({ error: "Missing X-Session-Token header" }, { status: 400 });
307
+ if (!authHeader || !authHeader.startsWith("Bearer ")) {
308
+ return Response.json({ error: "Missing or invalid Authorization header" }, { status: 400 });
310
309
  }
311
- const result = await handler.handleStatus(provider, sessionToken);
310
+ const accessToken = authHeader.substring(7);
311
+ const result = await handler.handleStatus(provider, accessToken);
312
312
  return Response.json(result);
313
313
  } catch (error) {
314
314
  console.error("[OAuth Status] Error:", error);
@@ -317,16 +317,17 @@ function createNextOAuthHandler(config) {
317
317
  },
318
318
  async disconnect(req) {
319
319
  try {
320
- const sessionToken = req.headers.get("x-session-token");
321
- if (!sessionToken) {
322
- return Response.json({ error: "Missing X-Session-Token header" }, { status: 400 });
320
+ const authHeader = req.headers.get("authorization");
321
+ if (!authHeader || !authHeader.startsWith("Bearer ")) {
322
+ return Response.json({ error: "Missing or invalid Authorization header" }, { status: 400 });
323
323
  }
324
+ const accessToken = authHeader.substring(7);
324
325
  const body = await req.json();
325
326
  const { provider } = body;
326
327
  if (!provider) {
327
328
  return Response.json({ error: "Missing provider in request body" }, { status: 400 });
328
329
  }
329
- const result = await handler.handleDisconnect({ provider }, sessionToken);
330
+ const result = await handler.handleDisconnect({ provider }, accessToken);
330
331
  return Response.json(result);
331
332
  } catch (error) {
332
333
  console.error("[OAuth Disconnect] Error:", error);
@@ -824,7 +825,7 @@ function sendCallbackToOpener(params) {
824
825
  // src/oauth/manager.ts
825
826
  class OAuthManager {
826
827
  pendingAuths = new Map;
827
- sessionToken;
828
+ providerTokens = new Map;
828
829
  windowManager;
829
830
  flowConfig;
830
831
  oauthApiBase;
@@ -890,11 +891,19 @@ class OAuthManager {
890
891
  }
891
892
  try {
892
893
  const response = await this.exchangeCodeForToken(pendingAuth.provider, code, pendingAuth.codeVerifier, state);
893
- this.sessionToken = response.sessionToken;
894
- this.saveSessionToken(response.sessionToken);
894
+ const tokenData = {
895
+ accessToken: response.accessToken,
896
+ refreshToken: response.refreshToken,
897
+ tokenType: response.tokenType,
898
+ expiresIn: response.expiresIn,
899
+ expiresAt: response.expiresAt,
900
+ scopes: response.scopes
901
+ };
902
+ this.providerTokens.set(pendingAuth.provider, tokenData);
903
+ this.saveProviderToken(pendingAuth.provider, tokenData);
895
904
  this.pendingAuths.delete(state);
896
905
  this.removePendingAuthFromStorage(state);
897
- return response.sessionToken;
906
+ return { ...tokenData, provider: pendingAuth.provider };
898
907
  } catch (error) {
899
908
  this.pendingAuths.delete(state);
900
909
  this.removePendingAuthFromStorage(state);
@@ -902,7 +911,8 @@ class OAuthManager {
902
911
  }
903
912
  }
904
913
  async checkAuthStatus(provider) {
905
- if (!this.sessionToken) {
914
+ const tokenData = this.providerTokens.get(provider);
915
+ if (!tokenData) {
906
916
  return {
907
917
  authorized: false,
908
918
  provider
@@ -913,7 +923,7 @@ class OAuthManager {
913
923
  const response = await fetch(url, {
914
924
  method: "GET",
915
925
  headers: {
916
- "X-Session-Token": this.sessionToken
926
+ Authorization: `Bearer ${tokenData.accessToken}`
917
927
  }
918
928
  });
919
929
  if (!response.ok) {
@@ -923,7 +933,7 @@ class OAuthManager {
923
933
  };
924
934
  }
925
935
  const status = await response.json();
926
- return status;
936
+ return { ...status, provider };
927
937
  } catch (error) {
928
938
  console.error("Failed to check auth status:", error);
929
939
  return {
@@ -933,8 +943,9 @@ class OAuthManager {
933
943
  }
934
944
  }
935
945
  async disconnectProvider(provider) {
936
- if (!this.sessionToken) {
937
- throw new Error("No session token available. Cannot disconnect provider.");
946
+ const tokenData = this.providerTokens.get(provider);
947
+ if (!tokenData) {
948
+ throw new Error(`No access token available for provider "${provider}". Cannot disconnect provider.`);
938
949
  }
939
950
  try {
940
951
  const url = `${this.oauthApiBase}/disconnect`;
@@ -942,7 +953,7 @@ class OAuthManager {
942
953
  method: "POST",
943
954
  headers: {
944
955
  "Content-Type": "application/json",
945
- "X-Session-Token": this.sessionToken
956
+ Authorization: `Bearer ${tokenData.accessToken}`
946
957
  },
947
958
  body: JSON.stringify({ provider })
948
959
  });
@@ -950,25 +961,43 @@ class OAuthManager {
950
961
  const errorText = await response.text();
951
962
  throw new Error(`Failed to disconnect provider: ${errorText}`);
952
963
  }
964
+ this.providerTokens.delete(provider);
965
+ this.clearProviderToken(provider);
953
966
  } catch (error) {
954
967
  console.error("Failed to disconnect provider:", error);
955
968
  throw error;
956
969
  }
957
970
  }
958
- getSessionToken() {
959
- return this.sessionToken;
971
+ getProviderToken(provider) {
972
+ return this.providerTokens.get(provider);
960
973
  }
961
- setSessionToken(token) {
962
- this.sessionToken = token;
963
- this.saveSessionToken(token);
974
+ getAllProviderTokens() {
975
+ return new Map(this.providerTokens);
964
976
  }
965
- clearSessionToken() {
966
- this.sessionToken = undefined;
967
- if (typeof window !== "undefined" && window.sessionStorage) {
977
+ setProviderToken(provider, tokenData) {
978
+ this.providerTokens.set(provider, tokenData);
979
+ this.saveProviderToken(provider, tokenData);
980
+ }
981
+ clearProviderToken(provider) {
982
+ this.providerTokens.delete(provider);
983
+ if (typeof window !== "undefined" && window.localStorage) {
968
984
  try {
969
- window.sessionStorage.removeItem("integrate_session_token");
985
+ window.localStorage.removeItem(`integrate_token_${provider}`);
970
986
  } catch (error) {
971
- console.error("Failed to clear session token from sessionStorage:", error);
987
+ console.error(`Failed to clear token for ${provider} from localStorage:`, error);
988
+ }
989
+ }
990
+ }
991
+ clearAllProviderTokens() {
992
+ const providers = Array.from(this.providerTokens.keys());
993
+ this.providerTokens.clear();
994
+ if (typeof window !== "undefined" && window.localStorage) {
995
+ for (const provider of providers) {
996
+ try {
997
+ window.localStorage.removeItem(`integrate_token_${provider}`);
998
+ } catch (error) {
999
+ console.error(`Failed to clear token for ${provider} from localStorage:`, error);
1000
+ }
972
1001
  }
973
1002
  }
974
1003
  }
@@ -990,12 +1019,35 @@ class OAuthManager {
990
1019
  }
991
1020
  }
992
1021
  }
993
- saveSessionToken(token) {
994
- if (typeof window !== "undefined" && window.sessionStorage) {
1022
+ saveProviderToken(provider, tokenData) {
1023
+ if (typeof window !== "undefined" && window.localStorage) {
1024
+ try {
1025
+ const key = `integrate_token_${provider}`;
1026
+ window.localStorage.setItem(key, JSON.stringify(tokenData));
1027
+ } catch (error) {
1028
+ console.error(`Failed to save token for ${provider} to localStorage:`, error);
1029
+ }
1030
+ }
1031
+ }
1032
+ loadProviderToken(provider) {
1033
+ if (typeof window !== "undefined" && window.localStorage) {
995
1034
  try {
996
- window.sessionStorage.setItem("integrate_session_token", token);
1035
+ const key = `integrate_token_${provider}`;
1036
+ const stored = window.localStorage.getItem(key);
1037
+ if (stored) {
1038
+ return JSON.parse(stored);
1039
+ }
997
1040
  } catch (error) {
998
- console.error("Failed to save session token to sessionStorage:", error);
1041
+ console.error(`Failed to load token for ${provider} from localStorage:`, error);
1042
+ }
1043
+ }
1044
+ return;
1045
+ }
1046
+ loadAllProviderTokens(providers) {
1047
+ for (const provider of providers) {
1048
+ const tokenData = this.loadProviderToken(provider);
1049
+ if (tokenData) {
1050
+ this.providerTokens.set(provider, tokenData);
999
1051
  }
1000
1052
  }
1001
1053
  }
@@ -1183,23 +1235,15 @@ class MCPClient {
1183
1235
  this.maxReauthRetries = config.maxReauthRetries ?? 1;
1184
1236
  this.connectionMode = config.connectionMode ?? "lazy";
1185
1237
  this.oauthManager = new OAuthManager(config.oauthApiBase || "/api/integrate/oauth", config.oauthFlow);
1186
- let sessionToken = config.sessionToken;
1187
- if (!sessionToken) {
1188
- const storedToken = this.loadSessionToken();
1189
- if (storedToken) {
1190
- sessionToken = storedToken;
1191
- }
1192
- }
1193
- if (sessionToken) {
1194
- this.oauthManager.setSessionToken(sessionToken);
1195
- this.transport.setHeader("X-Session-Token", sessionToken);
1196
- }
1238
+ const providers = this.plugins.filter((p) => p.oauth).map((p) => p.oauth.provider);
1239
+ this.oauthManager.loadAllProviderTokens(providers);
1197
1240
  for (const plugin of this.plugins) {
1198
1241
  for (const toolName of plugin.tools) {
1199
1242
  this.enabledToolNames.add(toolName);
1200
1243
  }
1201
1244
  if (plugin.oauth) {
1202
- this.authState.set(plugin.oauth.provider, { authenticated: true });
1245
+ const hasToken = this.oauthManager.getProviderToken(plugin.oauth.provider) !== undefined;
1246
+ this.authState.set(plugin.oauth.provider, { authenticated: hasToken });
1203
1247
  }
1204
1248
  }
1205
1249
  this.github = this.createPluginProxy("github");
@@ -1340,30 +1384,36 @@ class MCPClient {
1340
1384
  if (!this.availableTools.has(name)) {
1341
1385
  throw new Error(`Tool "${name}" is not available on the server. Available tools: ${Array.from(this.availableTools.keys()).join(", ")}`);
1342
1386
  }
1387
+ const provider = this.getProviderForTool(name);
1388
+ if (provider) {
1389
+ const tokenData = this.oauthManager.getProviderToken(provider);
1390
+ if (tokenData) {
1391
+ this.transport.setHeader("Authorization", `Bearer ${tokenData.accessToken}`);
1392
+ }
1393
+ }
1343
1394
  const params = {
1344
1395
  name,
1345
1396
  arguments: args
1346
1397
  };
1347
1398
  try {
1348
1399
  const response = await this.transport.sendRequest("tools/call" /* TOOLS_CALL */, params);
1349
- const provider = this.getProviderForTool(name);
1350
1400
  if (provider) {
1351
1401
  this.authState.set(provider, { authenticated: true });
1352
1402
  }
1353
1403
  return response;
1354
1404
  } catch (error) {
1355
- const provider = this.getProviderForTool(name);
1356
- const parsedError = parseServerError(error, { toolName: name, provider });
1405
+ const provider2 = this.getProviderForTool(name);
1406
+ const parsedError = parseServerError(error, { toolName: name, provider: provider2 });
1357
1407
  if (isAuthError(parsedError) && retryCount < this.maxReauthRetries) {
1358
- if (provider) {
1359
- this.authState.set(provider, {
1408
+ if (provider2) {
1409
+ this.authState.set(provider2, {
1360
1410
  authenticated: false,
1361
1411
  lastError: parsedError
1362
1412
  });
1363
1413
  }
1364
- if (this.onReauthRequired && provider) {
1414
+ if (this.onReauthRequired && provider2) {
1365
1415
  const reauthSuccess = await this.onReauthRequired({
1366
- provider,
1416
+ provider: provider2,
1367
1417
  error: parsedError,
1368
1418
  toolName: name
1369
1419
  });
@@ -1414,35 +1464,8 @@ class MCPClient {
1414
1464
  off(event, handler) {
1415
1465
  this.eventEmitter.off(event, handler);
1416
1466
  }
1417
- saveSessionToken(token) {
1418
- if (typeof window !== "undefined" && window.sessionStorage) {
1419
- try {
1420
- window.sessionStorage.setItem("integrate_session_token", token);
1421
- } catch (error) {
1422
- console.error("Failed to save session token to sessionStorage:", error);
1423
- }
1424
- }
1425
- }
1426
- loadSessionToken() {
1427
- if (typeof window !== "undefined" && window.sessionStorage) {
1428
- try {
1429
- return window.sessionStorage.getItem("integrate_session_token") || undefined;
1430
- } catch (error) {
1431
- console.error("Failed to load session token from sessionStorage:", error);
1432
- return;
1433
- }
1434
- }
1435
- return;
1436
- }
1437
1467
  clearSessionToken() {
1438
- if (typeof window !== "undefined" && window.sessionStorage) {
1439
- try {
1440
- window.sessionStorage.removeItem("integrate_session_token");
1441
- } catch (error) {
1442
- console.error("Failed to clear session token from sessionStorage:", error);
1443
- }
1444
- }
1445
- this.oauthManager.clearSessionToken();
1468
+ this.oauthManager.clearAllProviderTokens();
1446
1469
  }
1447
1470
  async disconnectProvider(provider) {
1448
1471
  const plugin = this.plugins.find((p) => p.oauth?.provider === provider);
@@ -1522,10 +1545,13 @@ class MCPClient {
1522
1545
  this.eventEmitter.emit("auth:started", { provider });
1523
1546
  try {
1524
1547
  await this.oauthManager.initiateFlow(provider, plugin.oauth);
1525
- const sessionToken = this.oauthManager.getSessionToken();
1526
- if (sessionToken) {
1527
- this.saveSessionToken(sessionToken);
1528
- this.eventEmitter.emit("auth:complete", { provider, sessionToken });
1548
+ const tokenData = this.oauthManager.getProviderToken(provider);
1549
+ if (tokenData) {
1550
+ this.eventEmitter.emit("auth:complete", {
1551
+ provider,
1552
+ accessToken: tokenData.accessToken,
1553
+ expiresAt: tokenData.expiresAt
1554
+ });
1529
1555
  }
1530
1556
  this.authState.set(provider, { authenticated: true });
1531
1557
  } catch (error) {
@@ -1535,18 +1561,13 @@ class MCPClient {
1535
1561
  }
1536
1562
  async handleOAuthCallback(params) {
1537
1563
  try {
1538
- const sessionToken = await this.oauthManager.handleCallback(params.code, params.state);
1539
- this.saveSessionToken(sessionToken);
1540
- this.transport.setHeader("X-Session-Token", sessionToken);
1541
- for (const plugin of this.plugins) {
1542
- if (plugin.oauth) {
1543
- this.authState.set(plugin.oauth.provider, { authenticated: true });
1544
- this.eventEmitter.emit("auth:complete", {
1545
- provider: plugin.oauth.provider,
1546
- sessionToken
1547
- });
1548
- }
1549
- }
1564
+ const result = await this.oauthManager.handleCallback(params.code, params.state);
1565
+ this.authState.set(result.provider, { authenticated: true });
1566
+ this.eventEmitter.emit("auth:complete", {
1567
+ provider: result.provider,
1568
+ accessToken: result.accessToken,
1569
+ expiresAt: result.expiresAt
1570
+ });
1550
1571
  } catch (error) {
1551
1572
  this.eventEmitter.emit("auth:error", {
1552
1573
  provider: "unknown",
@@ -1555,12 +1576,12 @@ class MCPClient {
1555
1576
  throw error;
1556
1577
  }
1557
1578
  }
1558
- getSessionToken() {
1559
- return this.oauthManager.getSessionToken();
1579
+ getProviderToken(provider) {
1580
+ return this.oauthManager.getProviderToken(provider);
1560
1581
  }
1561
- setSessionToken(token) {
1562
- this.oauthManager.setSessionToken(token);
1563
- this.transport.setHeader("X-Session-Token", token);
1582
+ setProviderToken(provider, tokenData) {
1583
+ this.oauthManager.setProviderToken(provider, tokenData);
1584
+ this.authState.set(provider, { authenticated: true });
1564
1585
  }
1565
1586
  async reauthenticate(provider) {
1566
1587
  const state = this.authState.get(provider);
package/dist/oauth.js CHANGED
@@ -85,20 +85,19 @@ class OAuthHandler {
85
85
  const data = await response.json();
86
86
  return data;
87
87
  }
88
- async handleStatus(provider, sessionToken) {
88
+ async handleStatus(provider, accessToken) {
89
89
  const url = new URL("/oauth/status", this.serverUrl);
90
90
  url.searchParams.set("provider", provider);
91
91
  const response = await fetch(url.toString(), {
92
92
  method: "GET",
93
93
  headers: {
94
- "X-Session-Token": sessionToken
94
+ Authorization: `Bearer ${accessToken}`
95
95
  }
96
96
  });
97
97
  if (!response.ok) {
98
98
  if (response.status === 401) {
99
99
  return {
100
- authorized: false,
101
- provider
100
+ authorized: false
102
101
  };
103
102
  }
104
103
  const error = await response.text();
@@ -107,16 +106,16 @@ class OAuthHandler {
107
106
  const data = await response.json();
108
107
  return data;
109
108
  }
110
- async handleDisconnect(request, sessionToken) {
111
- if (!sessionToken) {
112
- throw new Error("No session token provided. Cannot disconnect provider.");
109
+ async handleDisconnect(request, accessToken) {
110
+ if (!accessToken) {
111
+ throw new Error("No access token provided. Cannot disconnect provider.");
113
112
  }
114
113
  const url = new URL("/oauth/disconnect", this.serverUrl);
115
114
  const response = await fetch(url.toString(), {
116
115
  method: "POST",
117
116
  headers: {
118
117
  "Content-Type": "application/json",
119
- "X-Session-Token": sessionToken
118
+ Authorization: `Bearer ${accessToken}`
120
119
  },
121
120
  body: JSON.stringify({
122
121
  provider: request.provider
@@ -156,14 +155,14 @@ async function POST(req, context) {
156
155
  }
157
156
  if (action === "disconnect") {
158
157
  const body = await parseRequestBody(req);
159
- const sessionToken = extractSessionToken(req);
160
- if (!sessionToken) {
161
- return createErrorResponse("Missing X-Session-Token header", 400);
158
+ const accessToken = extractAccessToken(req);
159
+ if (!accessToken) {
160
+ return createErrorResponse("Missing or invalid Authorization header", 400);
162
161
  }
163
162
  if (!body.provider) {
164
163
  return createErrorResponse("Missing provider in request body", 400);
165
164
  }
166
- const result = await handler.handleDisconnect({ provider: body.provider }, sessionToken);
165
+ const result = await handler.handleDisconnect({ provider: body.provider }, accessToken);
167
166
  return createSuccessResponse(result);
168
167
  }
169
168
  return createErrorResponse(`Unknown action: ${action}`, 404);
@@ -183,11 +182,15 @@ async function GET(req, context) {
183
182
  }
184
183
  try {
185
184
  if (action === "status") {
186
- const { provider, sessionToken } = parseQueryParams(req);
187
- if (!provider || !sessionToken) {
188
- return createErrorResponse("Missing provider or session token", 400);
185
+ const provider = extractProvider(req);
186
+ const accessToken = extractAccessToken(req);
187
+ if (!provider) {
188
+ return createErrorResponse("Missing provider parameter", 400);
189
189
  }
190
- const result = await handler.handleStatus(provider, sessionToken);
190
+ if (!accessToken) {
191
+ return createErrorResponse("Missing or invalid Authorization header", 400);
192
+ }
193
+ const result = await handler.handleStatus(provider, accessToken);
191
194
  return createSuccessResponse(result);
192
195
  }
193
196
  return createErrorResponse(`Unknown action: ${action}`, 404);
@@ -202,24 +205,25 @@ async function parseRequestBody(req) {
202
205
  }
203
206
  throw new Error("Unable to parse request body");
204
207
  }
205
- function extractSessionToken(req) {
208
+ function extractAccessToken(req) {
206
209
  if (req.headers?.get) {
207
- return req.headers.get("x-session-token") || undefined;
210
+ const authHeader = req.headers.get("authorization");
211
+ if (authHeader && authHeader.startsWith("Bearer ")) {
212
+ return authHeader.substring(7);
213
+ }
208
214
  }
209
215
  return;
210
216
  }
211
- function parseQueryParams(req) {
217
+ function extractProvider(req) {
212
218
  let url;
213
219
  if (req.nextUrl) {
214
220
  url = new URL(req.nextUrl);
215
221
  } else if (req.url) {
216
222
  url = new URL(req.url);
217
223
  } else {
218
- return {};
224
+ return;
219
225
  }
220
- const provider = url.searchParams.get("provider") || undefined;
221
- const sessionToken = extractSessionToken(req);
222
- return { provider, sessionToken };
226
+ return url.searchParams.get("provider") || undefined;
223
227
  }
224
228
  function createSuccessResponse(data) {
225
229
  if (typeof globalThis.NextResponse !== "undefined") {