integrate-sdk 0.3.14 → 0.4.2
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 +139 -109
- package/dist/oauth.js +37 -24
- package/dist/server.js +139 -109
- package/dist/src/adapters/auto-routes.d.ts.map +1 -1
- package/dist/src/adapters/base-handler.d.ts +16 -14
- package/dist/src/adapters/base-handler.d.ts.map +1 -1
- package/dist/src/adapters/nextjs.d.ts +3 -4
- package/dist/src/adapters/nextjs.d.ts.map +1 -1
- package/dist/src/client.d.ts +11 -21
- package/dist/src/client.d.ts.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/oauth/manager.d.ts +33 -13
- package/dist/src/oauth/manager.d.ts.map +1 -1
- package/dist/src/oauth/types.d.ts +33 -6
- package/dist/src/oauth/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -203,6 +203,13 @@ class OAuthHandler {
|
|
|
203
203
|
return data;
|
|
204
204
|
}
|
|
205
205
|
async handleCallback(request) {
|
|
206
|
+
const providerConfig = this.config.providers[request.provider];
|
|
207
|
+
if (!providerConfig) {
|
|
208
|
+
throw new Error(`Provider ${request.provider} not configured. Add OAuth credentials to your API route configuration.`);
|
|
209
|
+
}
|
|
210
|
+
if (!providerConfig.clientId || !providerConfig.clientSecret) {
|
|
211
|
+
throw new Error(`Missing OAuth credentials for ${request.provider}. Check your environment variables.`);
|
|
212
|
+
}
|
|
206
213
|
const url = new URL("/oauth/callback", this.serverUrl);
|
|
207
214
|
const response = await fetch(url.toString(), {
|
|
208
215
|
method: "POST",
|
|
@@ -213,7 +220,9 @@ class OAuthHandler {
|
|
|
213
220
|
provider: request.provider,
|
|
214
221
|
code: request.code,
|
|
215
222
|
code_verifier: request.codeVerifier,
|
|
216
|
-
state: request.state
|
|
223
|
+
state: request.state,
|
|
224
|
+
client_id: providerConfig.clientId,
|
|
225
|
+
client_secret: providerConfig.clientSecret
|
|
217
226
|
})
|
|
218
227
|
});
|
|
219
228
|
if (!response.ok) {
|
|
@@ -223,20 +232,19 @@ class OAuthHandler {
|
|
|
223
232
|
const data = await response.json();
|
|
224
233
|
return data;
|
|
225
234
|
}
|
|
226
|
-
async handleStatus(provider,
|
|
235
|
+
async handleStatus(provider, accessToken) {
|
|
227
236
|
const url = new URL("/oauth/status", this.serverUrl);
|
|
228
237
|
url.searchParams.set("provider", provider);
|
|
229
238
|
const response = await fetch(url.toString(), {
|
|
230
239
|
method: "GET",
|
|
231
240
|
headers: {
|
|
232
|
-
|
|
241
|
+
Authorization: `Bearer ${accessToken}`
|
|
233
242
|
}
|
|
234
243
|
});
|
|
235
244
|
if (!response.ok) {
|
|
236
245
|
if (response.status === 401) {
|
|
237
246
|
return {
|
|
238
|
-
authorized: false
|
|
239
|
-
provider
|
|
247
|
+
authorized: false
|
|
240
248
|
};
|
|
241
249
|
}
|
|
242
250
|
const error = await response.text();
|
|
@@ -245,16 +253,16 @@ class OAuthHandler {
|
|
|
245
253
|
const data = await response.json();
|
|
246
254
|
return data;
|
|
247
255
|
}
|
|
248
|
-
async handleDisconnect(request,
|
|
249
|
-
if (!
|
|
250
|
-
throw new Error("No
|
|
256
|
+
async handleDisconnect(request, accessToken) {
|
|
257
|
+
if (!accessToken) {
|
|
258
|
+
throw new Error("No access token provided. Cannot disconnect provider.");
|
|
251
259
|
}
|
|
252
260
|
const url = new URL("/oauth/disconnect", this.serverUrl);
|
|
253
261
|
const response = await fetch(url.toString(), {
|
|
254
262
|
method: "POST",
|
|
255
263
|
headers: {
|
|
256
264
|
"Content-Type": "application/json",
|
|
257
|
-
|
|
265
|
+
Authorization: `Bearer ${accessToken}`
|
|
258
266
|
},
|
|
259
267
|
body: JSON.stringify({
|
|
260
268
|
provider: request.provider
|
|
@@ -301,14 +309,15 @@ function createNextOAuthHandler(config) {
|
|
|
301
309
|
async status(req) {
|
|
302
310
|
try {
|
|
303
311
|
const provider = req.nextUrl.searchParams.get("provider");
|
|
304
|
-
const
|
|
312
|
+
const authHeader = req.headers.get("authorization");
|
|
305
313
|
if (!provider) {
|
|
306
314
|
return Response.json({ error: "Missing provider query parameter" }, { status: 400 });
|
|
307
315
|
}
|
|
308
|
-
if (!
|
|
309
|
-
return Response.json({ error: "Missing
|
|
316
|
+
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
317
|
+
return Response.json({ error: "Missing or invalid Authorization header" }, { status: 400 });
|
|
310
318
|
}
|
|
311
|
-
const
|
|
319
|
+
const accessToken = authHeader.substring(7);
|
|
320
|
+
const result = await handler.handleStatus(provider, accessToken);
|
|
312
321
|
return Response.json(result);
|
|
313
322
|
} catch (error) {
|
|
314
323
|
console.error("[OAuth Status] Error:", error);
|
|
@@ -317,16 +326,17 @@ function createNextOAuthHandler(config) {
|
|
|
317
326
|
},
|
|
318
327
|
async disconnect(req) {
|
|
319
328
|
try {
|
|
320
|
-
const
|
|
321
|
-
if (!
|
|
322
|
-
return Response.json({ error: "Missing
|
|
329
|
+
const authHeader = req.headers.get("authorization");
|
|
330
|
+
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
331
|
+
return Response.json({ error: "Missing or invalid Authorization header" }, { status: 400 });
|
|
323
332
|
}
|
|
333
|
+
const accessToken = authHeader.substring(7);
|
|
324
334
|
const body = await req.json();
|
|
325
335
|
const { provider } = body;
|
|
326
336
|
if (!provider) {
|
|
327
337
|
return Response.json({ error: "Missing provider in request body" }, { status: 400 });
|
|
328
338
|
}
|
|
329
|
-
const result = await handler.handleDisconnect({ provider },
|
|
339
|
+
const result = await handler.handleDisconnect({ provider }, accessToken);
|
|
330
340
|
return Response.json(result);
|
|
331
341
|
} catch (error) {
|
|
332
342
|
console.error("[OAuth Disconnect] Error:", error);
|
|
@@ -824,7 +834,7 @@ function sendCallbackToOpener(params) {
|
|
|
824
834
|
// src/oauth/manager.ts
|
|
825
835
|
class OAuthManager {
|
|
826
836
|
pendingAuths = new Map;
|
|
827
|
-
|
|
837
|
+
providerTokens = new Map;
|
|
828
838
|
windowManager;
|
|
829
839
|
flowConfig;
|
|
830
840
|
oauthApiBase;
|
|
@@ -890,11 +900,19 @@ class OAuthManager {
|
|
|
890
900
|
}
|
|
891
901
|
try {
|
|
892
902
|
const response = await this.exchangeCodeForToken(pendingAuth.provider, code, pendingAuth.codeVerifier, state);
|
|
893
|
-
|
|
894
|
-
|
|
903
|
+
const tokenData = {
|
|
904
|
+
accessToken: response.accessToken,
|
|
905
|
+
refreshToken: response.refreshToken,
|
|
906
|
+
tokenType: response.tokenType,
|
|
907
|
+
expiresIn: response.expiresIn,
|
|
908
|
+
expiresAt: response.expiresAt,
|
|
909
|
+
scopes: response.scopes
|
|
910
|
+
};
|
|
911
|
+
this.providerTokens.set(pendingAuth.provider, tokenData);
|
|
912
|
+
this.saveProviderToken(pendingAuth.provider, tokenData);
|
|
895
913
|
this.pendingAuths.delete(state);
|
|
896
914
|
this.removePendingAuthFromStorage(state);
|
|
897
|
-
return
|
|
915
|
+
return { ...tokenData, provider: pendingAuth.provider };
|
|
898
916
|
} catch (error) {
|
|
899
917
|
this.pendingAuths.delete(state);
|
|
900
918
|
this.removePendingAuthFromStorage(state);
|
|
@@ -902,7 +920,8 @@ class OAuthManager {
|
|
|
902
920
|
}
|
|
903
921
|
}
|
|
904
922
|
async checkAuthStatus(provider) {
|
|
905
|
-
|
|
923
|
+
const tokenData = this.providerTokens.get(provider);
|
|
924
|
+
if (!tokenData) {
|
|
906
925
|
return {
|
|
907
926
|
authorized: false,
|
|
908
927
|
provider
|
|
@@ -913,7 +932,7 @@ class OAuthManager {
|
|
|
913
932
|
const response = await fetch(url, {
|
|
914
933
|
method: "GET",
|
|
915
934
|
headers: {
|
|
916
|
-
|
|
935
|
+
Authorization: `Bearer ${tokenData.accessToken}`
|
|
917
936
|
}
|
|
918
937
|
});
|
|
919
938
|
if (!response.ok) {
|
|
@@ -923,7 +942,7 @@ class OAuthManager {
|
|
|
923
942
|
};
|
|
924
943
|
}
|
|
925
944
|
const status = await response.json();
|
|
926
|
-
return status;
|
|
945
|
+
return { ...status, provider };
|
|
927
946
|
} catch (error) {
|
|
928
947
|
console.error("Failed to check auth status:", error);
|
|
929
948
|
return {
|
|
@@ -933,8 +952,9 @@ class OAuthManager {
|
|
|
933
952
|
}
|
|
934
953
|
}
|
|
935
954
|
async disconnectProvider(provider) {
|
|
936
|
-
|
|
937
|
-
|
|
955
|
+
const tokenData = this.providerTokens.get(provider);
|
|
956
|
+
if (!tokenData) {
|
|
957
|
+
throw new Error(`No access token available for provider "${provider}". Cannot disconnect provider.`);
|
|
938
958
|
}
|
|
939
959
|
try {
|
|
940
960
|
const url = `${this.oauthApiBase}/disconnect`;
|
|
@@ -942,7 +962,7 @@ class OAuthManager {
|
|
|
942
962
|
method: "POST",
|
|
943
963
|
headers: {
|
|
944
964
|
"Content-Type": "application/json",
|
|
945
|
-
|
|
965
|
+
Authorization: `Bearer ${tokenData.accessToken}`
|
|
946
966
|
},
|
|
947
967
|
body: JSON.stringify({ provider })
|
|
948
968
|
});
|
|
@@ -950,25 +970,43 @@ class OAuthManager {
|
|
|
950
970
|
const errorText = await response.text();
|
|
951
971
|
throw new Error(`Failed to disconnect provider: ${errorText}`);
|
|
952
972
|
}
|
|
973
|
+
this.providerTokens.delete(provider);
|
|
974
|
+
this.clearProviderToken(provider);
|
|
953
975
|
} catch (error) {
|
|
954
976
|
console.error("Failed to disconnect provider:", error);
|
|
955
977
|
throw error;
|
|
956
978
|
}
|
|
957
979
|
}
|
|
958
|
-
|
|
959
|
-
return this.
|
|
980
|
+
getProviderToken(provider) {
|
|
981
|
+
return this.providerTokens.get(provider);
|
|
960
982
|
}
|
|
961
|
-
|
|
962
|
-
this.
|
|
963
|
-
this.saveSessionToken(token);
|
|
983
|
+
getAllProviderTokens() {
|
|
984
|
+
return new Map(this.providerTokens);
|
|
964
985
|
}
|
|
965
|
-
|
|
966
|
-
this.
|
|
967
|
-
|
|
986
|
+
setProviderToken(provider, tokenData) {
|
|
987
|
+
this.providerTokens.set(provider, tokenData);
|
|
988
|
+
this.saveProviderToken(provider, tokenData);
|
|
989
|
+
}
|
|
990
|
+
clearProviderToken(provider) {
|
|
991
|
+
this.providerTokens.delete(provider);
|
|
992
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
968
993
|
try {
|
|
969
|
-
window.
|
|
994
|
+
window.localStorage.removeItem(`integrate_token_${provider}`);
|
|
970
995
|
} catch (error) {
|
|
971
|
-
console.error(
|
|
996
|
+
console.error(`Failed to clear token for ${provider} from localStorage:`, error);
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
clearAllProviderTokens() {
|
|
1001
|
+
const providers = Array.from(this.providerTokens.keys());
|
|
1002
|
+
this.providerTokens.clear();
|
|
1003
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
1004
|
+
for (const provider of providers) {
|
|
1005
|
+
try {
|
|
1006
|
+
window.localStorage.removeItem(`integrate_token_${provider}`);
|
|
1007
|
+
} catch (error) {
|
|
1008
|
+
console.error(`Failed to clear token for ${provider} from localStorage:`, error);
|
|
1009
|
+
}
|
|
972
1010
|
}
|
|
973
1011
|
}
|
|
974
1012
|
}
|
|
@@ -990,12 +1028,35 @@ class OAuthManager {
|
|
|
990
1028
|
}
|
|
991
1029
|
}
|
|
992
1030
|
}
|
|
993
|
-
|
|
994
|
-
if (typeof window !== "undefined" && window.
|
|
1031
|
+
saveProviderToken(provider, tokenData) {
|
|
1032
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
1033
|
+
try {
|
|
1034
|
+
const key = `integrate_token_${provider}`;
|
|
1035
|
+
window.localStorage.setItem(key, JSON.stringify(tokenData));
|
|
1036
|
+
} catch (error) {
|
|
1037
|
+
console.error(`Failed to save token for ${provider} to localStorage:`, error);
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
loadProviderToken(provider) {
|
|
1042
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
995
1043
|
try {
|
|
996
|
-
|
|
1044
|
+
const key = `integrate_token_${provider}`;
|
|
1045
|
+
const stored = window.localStorage.getItem(key);
|
|
1046
|
+
if (stored) {
|
|
1047
|
+
return JSON.parse(stored);
|
|
1048
|
+
}
|
|
997
1049
|
} catch (error) {
|
|
998
|
-
console.error(
|
|
1050
|
+
console.error(`Failed to load token for ${provider} from localStorage:`, error);
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
return;
|
|
1054
|
+
}
|
|
1055
|
+
loadAllProviderTokens(providers) {
|
|
1056
|
+
for (const provider of providers) {
|
|
1057
|
+
const tokenData = this.loadProviderToken(provider);
|
|
1058
|
+
if (tokenData) {
|
|
1059
|
+
this.providerTokens.set(provider, tokenData);
|
|
999
1060
|
}
|
|
1000
1061
|
}
|
|
1001
1062
|
}
|
|
@@ -1183,23 +1244,15 @@ class MCPClient {
|
|
|
1183
1244
|
this.maxReauthRetries = config.maxReauthRetries ?? 1;
|
|
1184
1245
|
this.connectionMode = config.connectionMode ?? "lazy";
|
|
1185
1246
|
this.oauthManager = new OAuthManager(config.oauthApiBase || "/api/integrate/oauth", config.oauthFlow);
|
|
1186
|
-
|
|
1187
|
-
|
|
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
|
-
}
|
|
1247
|
+
const providers = this.plugins.filter((p) => p.oauth).map((p) => p.oauth.provider);
|
|
1248
|
+
this.oauthManager.loadAllProviderTokens(providers);
|
|
1197
1249
|
for (const plugin of this.plugins) {
|
|
1198
1250
|
for (const toolName of plugin.tools) {
|
|
1199
1251
|
this.enabledToolNames.add(toolName);
|
|
1200
1252
|
}
|
|
1201
1253
|
if (plugin.oauth) {
|
|
1202
|
-
this.
|
|
1254
|
+
const hasToken = this.oauthManager.getProviderToken(plugin.oauth.provider) !== undefined;
|
|
1255
|
+
this.authState.set(plugin.oauth.provider, { authenticated: hasToken });
|
|
1203
1256
|
}
|
|
1204
1257
|
}
|
|
1205
1258
|
this.github = this.createPluginProxy("github");
|
|
@@ -1340,30 +1393,36 @@ class MCPClient {
|
|
|
1340
1393
|
if (!this.availableTools.has(name)) {
|
|
1341
1394
|
throw new Error(`Tool "${name}" is not available on the server. Available tools: ${Array.from(this.availableTools.keys()).join(", ")}`);
|
|
1342
1395
|
}
|
|
1396
|
+
const provider = this.getProviderForTool(name);
|
|
1397
|
+
if (provider) {
|
|
1398
|
+
const tokenData = this.oauthManager.getProviderToken(provider);
|
|
1399
|
+
if (tokenData) {
|
|
1400
|
+
this.transport.setHeader("Authorization", `Bearer ${tokenData.accessToken}`);
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1343
1403
|
const params = {
|
|
1344
1404
|
name,
|
|
1345
1405
|
arguments: args
|
|
1346
1406
|
};
|
|
1347
1407
|
try {
|
|
1348
1408
|
const response = await this.transport.sendRequest("tools/call" /* TOOLS_CALL */, params);
|
|
1349
|
-
const provider = this.getProviderForTool(name);
|
|
1350
1409
|
if (provider) {
|
|
1351
1410
|
this.authState.set(provider, { authenticated: true });
|
|
1352
1411
|
}
|
|
1353
1412
|
return response;
|
|
1354
1413
|
} catch (error) {
|
|
1355
|
-
const
|
|
1356
|
-
const parsedError = parseServerError(error, { toolName: name, provider });
|
|
1414
|
+
const provider2 = this.getProviderForTool(name);
|
|
1415
|
+
const parsedError = parseServerError(error, { toolName: name, provider: provider2 });
|
|
1357
1416
|
if (isAuthError(parsedError) && retryCount < this.maxReauthRetries) {
|
|
1358
|
-
if (
|
|
1359
|
-
this.authState.set(
|
|
1417
|
+
if (provider2) {
|
|
1418
|
+
this.authState.set(provider2, {
|
|
1360
1419
|
authenticated: false,
|
|
1361
1420
|
lastError: parsedError
|
|
1362
1421
|
});
|
|
1363
1422
|
}
|
|
1364
|
-
if (this.onReauthRequired &&
|
|
1423
|
+
if (this.onReauthRequired && provider2) {
|
|
1365
1424
|
const reauthSuccess = await this.onReauthRequired({
|
|
1366
|
-
provider,
|
|
1425
|
+
provider: provider2,
|
|
1367
1426
|
error: parsedError,
|
|
1368
1427
|
toolName: name
|
|
1369
1428
|
});
|
|
@@ -1414,35 +1473,8 @@ class MCPClient {
|
|
|
1414
1473
|
off(event, handler) {
|
|
1415
1474
|
this.eventEmitter.off(event, handler);
|
|
1416
1475
|
}
|
|
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
1476
|
clearSessionToken() {
|
|
1438
|
-
|
|
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();
|
|
1477
|
+
this.oauthManager.clearAllProviderTokens();
|
|
1446
1478
|
}
|
|
1447
1479
|
async disconnectProvider(provider) {
|
|
1448
1480
|
const plugin = this.plugins.find((p) => p.oauth?.provider === provider);
|
|
@@ -1522,10 +1554,13 @@ class MCPClient {
|
|
|
1522
1554
|
this.eventEmitter.emit("auth:started", { provider });
|
|
1523
1555
|
try {
|
|
1524
1556
|
await this.oauthManager.initiateFlow(provider, plugin.oauth);
|
|
1525
|
-
const
|
|
1526
|
-
if (
|
|
1527
|
-
this.
|
|
1528
|
-
|
|
1557
|
+
const tokenData = this.oauthManager.getProviderToken(provider);
|
|
1558
|
+
if (tokenData) {
|
|
1559
|
+
this.eventEmitter.emit("auth:complete", {
|
|
1560
|
+
provider,
|
|
1561
|
+
accessToken: tokenData.accessToken,
|
|
1562
|
+
expiresAt: tokenData.expiresAt
|
|
1563
|
+
});
|
|
1529
1564
|
}
|
|
1530
1565
|
this.authState.set(provider, { authenticated: true });
|
|
1531
1566
|
} catch (error) {
|
|
@@ -1535,18 +1570,13 @@ class MCPClient {
|
|
|
1535
1570
|
}
|
|
1536
1571
|
async handleOAuthCallback(params) {
|
|
1537
1572
|
try {
|
|
1538
|
-
const
|
|
1539
|
-
this.
|
|
1540
|
-
this.
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
provider: plugin.oauth.provider,
|
|
1546
|
-
sessionToken
|
|
1547
|
-
});
|
|
1548
|
-
}
|
|
1549
|
-
}
|
|
1573
|
+
const result = await this.oauthManager.handleCallback(params.code, params.state);
|
|
1574
|
+
this.authState.set(result.provider, { authenticated: true });
|
|
1575
|
+
this.eventEmitter.emit("auth:complete", {
|
|
1576
|
+
provider: result.provider,
|
|
1577
|
+
accessToken: result.accessToken,
|
|
1578
|
+
expiresAt: result.expiresAt
|
|
1579
|
+
});
|
|
1550
1580
|
} catch (error) {
|
|
1551
1581
|
this.eventEmitter.emit("auth:error", {
|
|
1552
1582
|
provider: "unknown",
|
|
@@ -1555,12 +1585,12 @@ class MCPClient {
|
|
|
1555
1585
|
throw error;
|
|
1556
1586
|
}
|
|
1557
1587
|
}
|
|
1558
|
-
|
|
1559
|
-
return this.oauthManager.
|
|
1588
|
+
getProviderToken(provider) {
|
|
1589
|
+
return this.oauthManager.getProviderToken(provider);
|
|
1560
1590
|
}
|
|
1561
|
-
|
|
1562
|
-
this.oauthManager.
|
|
1563
|
-
this.
|
|
1591
|
+
setProviderToken(provider, tokenData) {
|
|
1592
|
+
this.oauthManager.setProviderToken(provider, tokenData);
|
|
1593
|
+
this.authState.set(provider, { authenticated: true });
|
|
1564
1594
|
}
|
|
1565
1595
|
async reauthenticate(provider) {
|
|
1566
1596
|
const state = this.authState.get(provider);
|
package/dist/oauth.js
CHANGED
|
@@ -65,6 +65,13 @@ class OAuthHandler {
|
|
|
65
65
|
return data;
|
|
66
66
|
}
|
|
67
67
|
async handleCallback(request) {
|
|
68
|
+
const providerConfig = this.config.providers[request.provider];
|
|
69
|
+
if (!providerConfig) {
|
|
70
|
+
throw new Error(`Provider ${request.provider} not configured. Add OAuth credentials to your API route configuration.`);
|
|
71
|
+
}
|
|
72
|
+
if (!providerConfig.clientId || !providerConfig.clientSecret) {
|
|
73
|
+
throw new Error(`Missing OAuth credentials for ${request.provider}. Check your environment variables.`);
|
|
74
|
+
}
|
|
68
75
|
const url = new URL("/oauth/callback", this.serverUrl);
|
|
69
76
|
const response = await fetch(url.toString(), {
|
|
70
77
|
method: "POST",
|
|
@@ -75,7 +82,9 @@ class OAuthHandler {
|
|
|
75
82
|
provider: request.provider,
|
|
76
83
|
code: request.code,
|
|
77
84
|
code_verifier: request.codeVerifier,
|
|
78
|
-
state: request.state
|
|
85
|
+
state: request.state,
|
|
86
|
+
client_id: providerConfig.clientId,
|
|
87
|
+
client_secret: providerConfig.clientSecret
|
|
79
88
|
})
|
|
80
89
|
});
|
|
81
90
|
if (!response.ok) {
|
|
@@ -85,20 +94,19 @@ class OAuthHandler {
|
|
|
85
94
|
const data = await response.json();
|
|
86
95
|
return data;
|
|
87
96
|
}
|
|
88
|
-
async handleStatus(provider,
|
|
97
|
+
async handleStatus(provider, accessToken) {
|
|
89
98
|
const url = new URL("/oauth/status", this.serverUrl);
|
|
90
99
|
url.searchParams.set("provider", provider);
|
|
91
100
|
const response = await fetch(url.toString(), {
|
|
92
101
|
method: "GET",
|
|
93
102
|
headers: {
|
|
94
|
-
|
|
103
|
+
Authorization: `Bearer ${accessToken}`
|
|
95
104
|
}
|
|
96
105
|
});
|
|
97
106
|
if (!response.ok) {
|
|
98
107
|
if (response.status === 401) {
|
|
99
108
|
return {
|
|
100
|
-
authorized: false
|
|
101
|
-
provider
|
|
109
|
+
authorized: false
|
|
102
110
|
};
|
|
103
111
|
}
|
|
104
112
|
const error = await response.text();
|
|
@@ -107,16 +115,16 @@ class OAuthHandler {
|
|
|
107
115
|
const data = await response.json();
|
|
108
116
|
return data;
|
|
109
117
|
}
|
|
110
|
-
async handleDisconnect(request,
|
|
111
|
-
if (!
|
|
112
|
-
throw new Error("No
|
|
118
|
+
async handleDisconnect(request, accessToken) {
|
|
119
|
+
if (!accessToken) {
|
|
120
|
+
throw new Error("No access token provided. Cannot disconnect provider.");
|
|
113
121
|
}
|
|
114
122
|
const url = new URL("/oauth/disconnect", this.serverUrl);
|
|
115
123
|
const response = await fetch(url.toString(), {
|
|
116
124
|
method: "POST",
|
|
117
125
|
headers: {
|
|
118
126
|
"Content-Type": "application/json",
|
|
119
|
-
|
|
127
|
+
Authorization: `Bearer ${accessToken}`
|
|
120
128
|
},
|
|
121
129
|
body: JSON.stringify({
|
|
122
130
|
provider: request.provider
|
|
@@ -156,14 +164,14 @@ async function POST(req, context) {
|
|
|
156
164
|
}
|
|
157
165
|
if (action === "disconnect") {
|
|
158
166
|
const body = await parseRequestBody(req);
|
|
159
|
-
const
|
|
160
|
-
if (!
|
|
161
|
-
return createErrorResponse("Missing
|
|
167
|
+
const accessToken = extractAccessToken(req);
|
|
168
|
+
if (!accessToken) {
|
|
169
|
+
return createErrorResponse("Missing or invalid Authorization header", 400);
|
|
162
170
|
}
|
|
163
171
|
if (!body.provider) {
|
|
164
172
|
return createErrorResponse("Missing provider in request body", 400);
|
|
165
173
|
}
|
|
166
|
-
const result = await handler.handleDisconnect({ provider: body.provider },
|
|
174
|
+
const result = await handler.handleDisconnect({ provider: body.provider }, accessToken);
|
|
167
175
|
return createSuccessResponse(result);
|
|
168
176
|
}
|
|
169
177
|
return createErrorResponse(`Unknown action: ${action}`, 404);
|
|
@@ -183,11 +191,15 @@ async function GET(req, context) {
|
|
|
183
191
|
}
|
|
184
192
|
try {
|
|
185
193
|
if (action === "status") {
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
194
|
+
const provider = extractProvider(req);
|
|
195
|
+
const accessToken = extractAccessToken(req);
|
|
196
|
+
if (!provider) {
|
|
197
|
+
return createErrorResponse("Missing provider parameter", 400);
|
|
189
198
|
}
|
|
190
|
-
|
|
199
|
+
if (!accessToken) {
|
|
200
|
+
return createErrorResponse("Missing or invalid Authorization header", 400);
|
|
201
|
+
}
|
|
202
|
+
const result = await handler.handleStatus(provider, accessToken);
|
|
191
203
|
return createSuccessResponse(result);
|
|
192
204
|
}
|
|
193
205
|
return createErrorResponse(`Unknown action: ${action}`, 404);
|
|
@@ -202,24 +214,25 @@ async function parseRequestBody(req) {
|
|
|
202
214
|
}
|
|
203
215
|
throw new Error("Unable to parse request body");
|
|
204
216
|
}
|
|
205
|
-
function
|
|
217
|
+
function extractAccessToken(req) {
|
|
206
218
|
if (req.headers?.get) {
|
|
207
|
-
|
|
219
|
+
const authHeader = req.headers.get("authorization");
|
|
220
|
+
if (authHeader && authHeader.startsWith("Bearer ")) {
|
|
221
|
+
return authHeader.substring(7);
|
|
222
|
+
}
|
|
208
223
|
}
|
|
209
224
|
return;
|
|
210
225
|
}
|
|
211
|
-
function
|
|
226
|
+
function extractProvider(req) {
|
|
212
227
|
let url;
|
|
213
228
|
if (req.nextUrl) {
|
|
214
229
|
url = new URL(req.nextUrl);
|
|
215
230
|
} else if (req.url) {
|
|
216
231
|
url = new URL(req.url);
|
|
217
232
|
} else {
|
|
218
|
-
return
|
|
233
|
+
return;
|
|
219
234
|
}
|
|
220
|
-
|
|
221
|
-
const sessionToken = extractSessionToken(req);
|
|
222
|
-
return { provider, sessionToken };
|
|
235
|
+
return url.searchParams.get("provider") || undefined;
|
|
223
236
|
}
|
|
224
237
|
function createSuccessResponse(data) {
|
|
225
238
|
if (typeof globalThis.NextResponse !== "undefined") {
|