integrate-sdk 0.3.6 → 0.3.7

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
@@ -1356,106 +1356,6 @@ async function clearClientCache() {
1356
1356
  // src/index.ts
1357
1357
  init_nextjs();
1358
1358
 
1359
- // src/adapters/nextjs-callback.tsx
1360
- import { useEffect } from "react";
1361
- import { jsxDEV } from "react/jsx-dev-runtime";
1362
- "use client";
1363
- function OAuthCallbackPage(config) {
1364
- const redirectUrl = config?.redirectUrl || "/";
1365
- const errorRedirectUrl = config?.errorRedirectUrl || "/auth-error";
1366
- useEffect(() => {
1367
- const params = new URLSearchParams(window.location.search);
1368
- const code = params.get("code");
1369
- const state = params.get("state");
1370
- const error = params.get("error");
1371
- const errorDescription = params.get("error_description");
1372
- if (error) {
1373
- const errorMsg = errorDescription || error;
1374
- console.error("[OAuth Callback] Error:", errorMsg);
1375
- window.location.href = `${errorRedirectUrl}?error=${encodeURIComponent(errorMsg)}`;
1376
- return;
1377
- }
1378
- if (!code || !state) {
1379
- console.error("[OAuth Callback] Missing code or state parameter");
1380
- window.location.href = `${errorRedirectUrl}?error=${encodeURIComponent("Invalid OAuth callback")}`;
1381
- return;
1382
- }
1383
- if (window.opener) {
1384
- window.opener.postMessage({
1385
- type: "oauth_callback",
1386
- code,
1387
- state
1388
- }, "*");
1389
- setTimeout(() => {
1390
- window.close();
1391
- }, 100);
1392
- } else {
1393
- try {
1394
- sessionStorage.setItem("oauth_callback_params", JSON.stringify({ code, state }));
1395
- } catch (e) {
1396
- console.error("Failed to store OAuth callback params:", e);
1397
- }
1398
- setTimeout(() => {
1399
- window.location.href = redirectUrl;
1400
- }, 500);
1401
- }
1402
- }, [redirectUrl, errorRedirectUrl]);
1403
- return /* @__PURE__ */ jsxDEV("div", {
1404
- style: {
1405
- fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
1406
- display: "flex",
1407
- alignItems: "center",
1408
- justifyContent: "center",
1409
- minHeight: "100vh",
1410
- margin: 0,
1411
- background: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
1412
- color: "white"
1413
- },
1414
- children: /* @__PURE__ */ jsxDEV("div", {
1415
- style: { textAlign: "center", padding: "2rem" },
1416
- children: [
1417
- /* @__PURE__ */ jsxDEV("div", {
1418
- style: {
1419
- border: "3px solid rgba(255, 255, 255, 0.3)",
1420
- borderRadius: "50%",
1421
- borderTop: "3px solid white",
1422
- width: "40px",
1423
- height: "40px",
1424
- animation: "spin 1s linear infinite",
1425
- margin: "0 auto 1rem"
1426
- }
1427
- }, undefined, false, undefined, this),
1428
- /* @__PURE__ */ jsxDEV("h1", {
1429
- style: {
1430
- margin: "0 0 0.5rem",
1431
- fontSize: "1.5rem",
1432
- fontWeight: 600
1433
- },
1434
- children: "Authorization Complete"
1435
- }, undefined, false, undefined, this),
1436
- /* @__PURE__ */ jsxDEV("p", {
1437
- style: { margin: 0, opacity: 0.9, fontSize: "0.875rem" },
1438
- children: "This window will close automatically..."
1439
- }, undefined, false, undefined, this),
1440
- /* @__PURE__ */ jsxDEV("style", {
1441
- children: `
1442
- @keyframes spin {
1443
- 0% { transform: rotate(0deg); }
1444
- 100% { transform: rotate(360deg); }
1445
- }
1446
- `
1447
- }, undefined, false, undefined, this)
1448
- ]
1449
- }, undefined, true, undefined, this)
1450
- }, undefined, false, undefined, this);
1451
- }
1452
- function createOAuthCallbackPage(config) {
1453
- return function OAuthCallback() {
1454
- return /* @__PURE__ */ jsxDEV(OAuthCallbackPage, {
1455
- ...config
1456
- }, undefined, false, undefined, this);
1457
- };
1458
- }
1459
1359
  // src/adapters/tanstack-start.ts
1460
1360
  function createTanStackOAuthHandler(config) {
1461
1361
  const handler = new OAuthHandler(config);
@@ -1690,7 +1590,6 @@ export {
1690
1590
  generateCodeChallenge,
1691
1591
  createTanStackOAuthHandler,
1692
1592
  createSimplePlugin,
1693
- createOAuthCallbackPage,
1694
1593
  createNextOAuthHandler,
1695
1594
  createMCPClient,
1696
1595
  convertMCPToolsToVercelAI,
@@ -1701,7 +1600,6 @@ export {
1701
1600
  OAuthWindowManager,
1702
1601
  OAuthManager,
1703
1602
  OAuthHandler,
1704
- OAuthCallbackPage,
1705
1603
  MCPMethod,
1706
1604
  MCPClient,
1707
1605
  IntegrateSDKError,
@@ -19,7 +19,7 @@ import type { OAuthCallbackHandlerConfig } from '../oauth/types.js';
19
19
  * @example
20
20
  * ```tsx
21
21
  * // app/oauth/callback/page.tsx
22
- * import { OAuthCallbackPage } from 'integrate-sdk';
22
+ * import { OAuthCallbackPage } from 'integrate-sdk/oauth-callback';
23
23
  *
24
24
  * export default function CallbackPage() {
25
25
  * return <OAuthCallbackPage redirectUrl="/dashboard" />;
@@ -33,7 +33,11 @@ export declare function OAuthCallbackPage(config?: OAuthCallbackHandlerConfig):
33
33
  * @example
34
34
  * ```tsx
35
35
  * // app/oauth/callback/page.tsx
36
- * export { default } from 'integrate-sdk/oauth-callback';
36
+ * import { createOAuthCallbackPage } from 'integrate-sdk/oauth-callback';
37
+ *
38
+ * export default createOAuthCallbackPage({
39
+ * redirectUrl: '/dashboard',
40
+ * });
37
41
  * ```
38
42
  */
39
43
  export declare function createOAuthCallbackPage(config?: OAuthCallbackHandlerConfig): () => import("react/jsx-runtime").JSX.Element;
@@ -1 +1 @@
1
- {"version":3,"file":"nextjs-callback.d.ts","sourceRoot":"","sources":["../../../src/adapters/nextjs-callback.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AAEpE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,CAAC,EAAE,0BAA0B,2CA2GpE;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,CAAC,EAAE,0BAA0B,iDAI1E"}
1
+ {"version":3,"file":"nextjs-callback.d.ts","sourceRoot":"","sources":["../../../src/adapters/nextjs-callback.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AAEpE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,CAAC,EAAE,0BAA0B,2CA2GpE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,CAAC,EAAE,0BAA0B,iDAI1E"}
@@ -11,7 +11,6 @@ export type { OAuthFlowConfig, PopupOptions, AuthStatus, PendingAuth, Authorizat
11
11
  export { OAuthHandler } from "./adapters/base-handler.js";
12
12
  export type { OAuthHandlerConfig, AuthorizeRequest, AuthorizeResponse, CallbackRequest, CallbackResponse, StatusResponse, } from "./adapters/base-handler.js";
13
13
  export { createNextOAuthHandler } from "./adapters/nextjs.js";
14
- export { OAuthCallbackPage, createOAuthCallbackPage } from "./adapters/nextjs-callback.js";
15
14
  export { createTanStackOAuthHandler } from "./adapters/tanstack-start.js";
16
15
  export type { MCPClientConfig, ReauthContext, ReauthHandler } from "./config/types.js";
17
16
  export { IntegrateSDKError, AuthenticationError, AuthorizationError, TokenExpiredError, ConnectionError, ToolCallError, isAuthError, isTokenExpiredError, isAuthorizationError, parseServerError, } from "./errors.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC3E,YAAY,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAGzD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACrF,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAC7F,YAAY,EACV,eAAe,EACf,YAAY,EACZ,UAAU,EACV,WAAW,EACX,wBAAwB,EACxB,qBAAqB,EACrB,mBAAmB,EACnB,0BAA0B,GAC3B,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,YAAY,EACV,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EAChB,cAAc,GACf,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAC3F,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAG1E,YAAY,EAAE,eAAe,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGvF,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,eAAe,EACf,aAAa,EACb,WAAW,EACX,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAGrB,YAAY,EACV,SAAS,EACT,WAAW,EACX,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,YAAY,EAAE,kBAAkB,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAE/F,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,YAAY,EAAE,iBAAiB,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAG3F,YAAY,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAErE,OAAO,EACL,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAGrE,OAAO,EACL,wBAAwB,EACxB,yBAAyB,EACzB,gBAAgB,GACjB,MAAM,6BAA6B,CAAC;AACrC,YAAY,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAGhE,YAAY,EACV,cAAc,EACd,eAAe,EACf,sBAAsB,EACtB,oBAAoB,EACpB,mBAAmB,EACnB,OAAO,EACP,oBAAoB,EACpB,iBAAiB,EACjB,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAGnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,YAAY,EACV,cAAc,EACd,2BAA2B,GAC5B,MAAM,6BAA6B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC3E,YAAY,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAGzD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACrF,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAC7F,YAAY,EACV,eAAe,EACf,YAAY,EACZ,UAAU,EACV,WAAW,EACX,wBAAwB,EACxB,qBAAqB,EACrB,mBAAmB,EACnB,0BAA0B,GAC3B,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,YAAY,EACV,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EAChB,cAAc,GACf,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAG1E,YAAY,EAAE,eAAe,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGvF,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,eAAe,EACf,aAAa,EACb,WAAW,EACX,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAGrB,YAAY,EACV,SAAS,EACT,WAAW,EACX,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,YAAY,EAAE,kBAAkB,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAE/F,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,YAAY,EAAE,iBAAiB,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAG3F,YAAY,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAErE,OAAO,EACL,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAGrE,OAAO,EACL,wBAAwB,EACxB,yBAAyB,EACzB,gBAAgB,GACjB,MAAM,6BAA6B,CAAC;AACrC,YAAY,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAGhE,YAAY,EACV,cAAc,EACd,eAAe,EACf,sBAAsB,EACtB,oBAAoB,EACpB,mBAAmB,EACnB,OAAO,EACP,oBAAoB,EACpB,iBAAiB,EACjB,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAGnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,YAAY,EACV,cAAc,EACd,2BAA2B,GAC5B,MAAM,6BAA6B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "integrate-sdk",
3
- "version": "0.3.6",
3
+ "version": "0.3.7",
4
4
  "description": "Type-safe TypeScript SDK for MCP Client with plugin-based OAuth provider configuration",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -26,10 +26,15 @@
26
26
  "./oauth": {
27
27
  "import": "./dist/oauth.js",
28
28
  "types": "./dist/oauth.d.ts"
29
+ },
30
+ "./oauth-callback": {
31
+ "import": "./src/adapters/nextjs-callback.tsx",
32
+ "types": "./dist/adapters/nextjs-callback.d.ts"
29
33
  }
30
34
  },
31
35
  "files": [
32
36
  "dist",
37
+ "src",
33
38
  "index.ts",
34
39
  "server.ts",
35
40
  "oauth.ts"
@@ -0,0 +1,217 @@
1
+ /**
2
+ * Auto-generated OAuth Routes
3
+ * Automatically creates the correct route handlers based on framework detection
4
+ */
5
+
6
+ import { OAuthHandler, type OAuthHandlerConfig } from './base-handler.js';
7
+
8
+ /**
9
+ * Global OAuth configuration
10
+ * Set by createMCPClient when oauthConfig is provided
11
+ */
12
+ let globalOAuthConfig: OAuthHandlerConfig | null = null;
13
+
14
+ /**
15
+ * Set the global OAuth configuration
16
+ * Called internally by createMCPClient
17
+ */
18
+ export function setGlobalOAuthConfig(config: OAuthHandlerConfig): void {
19
+ globalOAuthConfig = config;
20
+ }
21
+
22
+ /**
23
+ * Get the global OAuth configuration
24
+ */
25
+ export function getGlobalOAuthConfig(): OAuthHandlerConfig | null {
26
+ return globalOAuthConfig;
27
+ }
28
+
29
+ /**
30
+ * Universal OAuth route handler
31
+ * Automatically detects framework and handles all OAuth actions
32
+ *
33
+ * This is the magic function that makes everything "just work"
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * // app/api/integrate/oauth/[action]/route.ts (Next.js)
38
+ * export * from 'integrate-sdk/oauth';
39
+ * ```
40
+ *
41
+ * @example
42
+ * ```typescript
43
+ * // app/routes/api/integrate/oauth/[action].ts (TanStack Start)
44
+ * export * from 'integrate-sdk/oauth';
45
+ * ```
46
+ */
47
+
48
+ // Framework detection helpers (unused but kept for future enhancements)
49
+ // function isNextJS(request: any): boolean {
50
+ // return (
51
+ // request?.constructor?.name === 'NextRequest' ||
52
+ // typeof request?.nextUrl !== 'undefined' ||
53
+ // typeof (globalThis as any).NextResponse !== 'undefined'
54
+ // );
55
+ // }
56
+
57
+ // function isTanStackStart(request: any): boolean {
58
+ // return (
59
+ // request instanceof Request &&
60
+ // !isNextJS(request)
61
+ // );
62
+ // }
63
+
64
+ /**
65
+ * Universal POST handler
66
+ * Handles authorize and callback actions
67
+ */
68
+ export async function POST(
69
+ req: any,
70
+ context?: { params: { action: string } }
71
+ ): Promise<any> {
72
+ if (!globalOAuthConfig) {
73
+ throw new Error(
74
+ 'OAuth configuration not found. Did you configure oauthProviders in createMCPClient?'
75
+ );
76
+ }
77
+
78
+ const handler = new OAuthHandler(globalOAuthConfig);
79
+ const action = context?.params?.action;
80
+
81
+ if (!action) {
82
+ return createErrorResponse('Missing action parameter', 400);
83
+ }
84
+
85
+ try {
86
+ if (action === 'authorize') {
87
+ const body = await parseRequestBody(req);
88
+ const result = await handler.handleAuthorize(body);
89
+ return createSuccessResponse(result);
90
+ }
91
+
92
+ if (action === 'callback') {
93
+ const body = await parseRequestBody(req);
94
+ const result = await handler.handleCallback(body);
95
+ return createSuccessResponse(result);
96
+ }
97
+
98
+ return createErrorResponse(`Unknown action: ${action}`, 404);
99
+ } catch (error: any) {
100
+ console.error(`[OAuth ${action}] Error:`, error);
101
+ return createErrorResponse(error.message, 500);
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Universal GET handler
107
+ * Handles status action
108
+ */
109
+ export async function GET(
110
+ req: any,
111
+ context?: { params: { action: string } }
112
+ ): Promise<any> {
113
+ if (!globalOAuthConfig) {
114
+ throw new Error(
115
+ 'OAuth configuration not found. Did you configure oauthProviders in createMCPClient?'
116
+ );
117
+ }
118
+
119
+ const handler = new OAuthHandler(globalOAuthConfig);
120
+ const action = context?.params?.action;
121
+
122
+ if (!action) {
123
+ return createErrorResponse('Missing action parameter', 400);
124
+ }
125
+
126
+ try {
127
+ if (action === 'status') {
128
+ const { provider, sessionToken } = parseQueryParams(req);
129
+
130
+ if (!provider || !sessionToken) {
131
+ return createErrorResponse(
132
+ 'Missing provider or session token',
133
+ 400
134
+ );
135
+ }
136
+
137
+ const result = await handler.handleStatus(provider, sessionToken);
138
+ return createSuccessResponse(result);
139
+ }
140
+
141
+ return createErrorResponse(`Unknown action: ${action}`, 404);
142
+ } catch (error: any) {
143
+ console.error(`[OAuth ${action}] Error:`, error);
144
+ return createErrorResponse(error.message, 500);
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Parse request body (works for both Next.js and standard Request)
150
+ */
151
+ async function parseRequestBody(req: any): Promise<any> {
152
+ if (typeof req.json === 'function') {
153
+ return await req.json();
154
+ }
155
+ throw new Error('Unable to parse request body');
156
+ }
157
+
158
+ /**
159
+ * Parse query parameters (works for both Next.js and standard Request)
160
+ */
161
+ function parseQueryParams(req: any): { provider?: string; sessionToken?: string } {
162
+ let url: URL;
163
+
164
+ // Next.js
165
+ if (req.nextUrl) {
166
+ url = new URL(req.nextUrl);
167
+ }
168
+ // Standard Request
169
+ else if (req.url) {
170
+ url = new URL(req.url);
171
+ } else {
172
+ return {};
173
+ }
174
+
175
+ const provider = url.searchParams.get('provider') || undefined;
176
+ const sessionToken = req.headers?.get?.('x-session-token') || undefined;
177
+
178
+ return { provider, sessionToken };
179
+ }
180
+
181
+ /**
182
+ * Create success response (works for both frameworks)
183
+ */
184
+ function createSuccessResponse(data: any): any {
185
+ // Try Next.js first
186
+ if (typeof (globalThis as any).NextResponse !== 'undefined') {
187
+ const NextResponse = (globalThis as any).NextResponse;
188
+ return NextResponse.json(data);
189
+ }
190
+
191
+ // Fallback to standard Response
192
+ return new Response(JSON.stringify(data), {
193
+ status: 200,
194
+ headers: { 'Content-Type': 'application/json' },
195
+ });
196
+ }
197
+
198
+ /**
199
+ * Create error response (works for both frameworks)
200
+ */
201
+ function createErrorResponse(message: string, status: number): any {
202
+ // Try Next.js first
203
+ if (typeof (globalThis as any).NextResponse !== 'undefined') {
204
+ const NextResponse = (globalThis as any).NextResponse;
205
+ return NextResponse.json({ error: message }, { status });
206
+ }
207
+
208
+ // Fallback to standard Response
209
+ return new Response(
210
+ JSON.stringify({ error: message }),
211
+ {
212
+ status,
213
+ headers: { 'Content-Type': 'application/json' },
214
+ }
215
+ );
216
+ }
217
+
@@ -0,0 +1,212 @@
1
+ /**
2
+ * Base OAuth Handler
3
+ * Framework-agnostic OAuth route logic for secure server-side token management
4
+ */
5
+
6
+ /**
7
+ * MCP Server URL - managed by Integrate
8
+ */
9
+ const MCP_SERVER_URL = 'https://mcp.integrate.dev/api/v1/mcp';
10
+
11
+ /**
12
+ * OAuth handler configuration
13
+ * OAuth credentials for each provider
14
+ */
15
+ export interface OAuthHandlerConfig {
16
+ /** OAuth configurations by provider */
17
+ providers: Record<string, {
18
+ /** OAuth client ID from environment variables */
19
+ clientId: string;
20
+ /** OAuth client secret from environment variables */
21
+ clientSecret: string;
22
+ /** Optional redirect URI override */
23
+ redirectUri?: string;
24
+ }>;
25
+ }
26
+
27
+ /**
28
+ * Request body for authorize endpoint
29
+ */
30
+ export interface AuthorizeRequest {
31
+ provider: string;
32
+ scopes: string[];
33
+ state: string;
34
+ codeChallenge: string;
35
+ codeChallengeMethod: string;
36
+ redirectUri?: string;
37
+ }
38
+
39
+ /**
40
+ * Response from authorize endpoint
41
+ */
42
+ export interface AuthorizeResponse {
43
+ authorizationUrl: string;
44
+ }
45
+
46
+ /**
47
+ * Request body for callback endpoint
48
+ */
49
+ export interface CallbackRequest {
50
+ provider: string;
51
+ code: string;
52
+ codeVerifier: string;
53
+ state: string;
54
+ }
55
+
56
+ /**
57
+ * Response from callback endpoint
58
+ */
59
+ export interface CallbackResponse {
60
+ sessionToken: string;
61
+ provider: string;
62
+ scopes: string[];
63
+ expiresAt?: number;
64
+ }
65
+
66
+ /**
67
+ * Response from status endpoint
68
+ */
69
+ export interface StatusResponse {
70
+ authorized: boolean;
71
+ provider: string;
72
+ scopes?: string[];
73
+ expiresAt?: number;
74
+ }
75
+
76
+ /**
77
+ * OAuth Handler
78
+ * Handles OAuth authorization flows by proxying requests to MCP server
79
+ * with server-side OAuth credentials from environment variables
80
+ */
81
+ export class OAuthHandler {
82
+ private readonly serverUrl = MCP_SERVER_URL;
83
+
84
+ constructor(private config: OAuthHandlerConfig) {}
85
+
86
+ /**
87
+ * Handle authorization URL request
88
+ * Gets authorization URL from MCP server with full OAuth credentials
89
+ *
90
+ * @param request - Authorization request from client
91
+ * @returns Authorization URL to redirect/open for user
92
+ *
93
+ * @throws Error if provider is not configured
94
+ * @throws Error if MCP server request fails
95
+ */
96
+ async handleAuthorize(request: AuthorizeRequest): Promise<AuthorizeResponse> {
97
+ // Get OAuth config from environment (server-side)
98
+ const providerConfig = this.config.providers[request.provider];
99
+ if (!providerConfig) {
100
+ throw new Error(`Provider ${request.provider} not configured. Add OAuth credentials to your API route configuration.`);
101
+ }
102
+
103
+ // Validate required fields
104
+ if (!providerConfig.clientId || !providerConfig.clientSecret) {
105
+ throw new Error(`Missing OAuth credentials for ${request.provider}. Check your environment variables.`);
106
+ }
107
+
108
+ // Build URL to MCP server
109
+ const url = new URL('/oauth/authorize', this.serverUrl);
110
+ url.searchParams.set('provider', request.provider);
111
+ url.searchParams.set('client_id', providerConfig.clientId);
112
+ url.searchParams.set('client_secret', providerConfig.clientSecret);
113
+ url.searchParams.set('scope', request.scopes.join(','));
114
+ url.searchParams.set('state', request.state);
115
+ url.searchParams.set('code_challenge', request.codeChallenge);
116
+ url.searchParams.set('code_challenge_method', request.codeChallengeMethod);
117
+
118
+ // Use request redirect URI or fallback to provider config
119
+ const redirectUri = request.redirectUri || providerConfig.redirectUri;
120
+ if (redirectUri) {
121
+ url.searchParams.set('redirect_uri', redirectUri);
122
+ }
123
+
124
+ // Forward to MCP server
125
+ const response = await fetch(url.toString(), {
126
+ method: 'GET',
127
+ });
128
+
129
+ if (!response.ok) {
130
+ const error = await response.text();
131
+ throw new Error(`MCP server failed to generate authorization URL: ${error}`);
132
+ }
133
+
134
+ const data = await response.json();
135
+ return data as AuthorizeResponse;
136
+ }
137
+
138
+ /**
139
+ * Handle OAuth callback
140
+ * Exchanges authorization code for session token
141
+ *
142
+ * @param request - Callback request with authorization code
143
+ * @returns Session token and authorization details
144
+ *
145
+ * @throws Error if MCP server request fails
146
+ */
147
+ async handleCallback(request: CallbackRequest): Promise<CallbackResponse> {
148
+ // Forward to MCP server for token exchange
149
+ const url = new URL('/oauth/callback', this.serverUrl);
150
+
151
+ const response = await fetch(url.toString(), {
152
+ method: 'POST',
153
+ headers: {
154
+ 'Content-Type': 'application/json',
155
+ },
156
+ body: JSON.stringify({
157
+ provider: request.provider,
158
+ code: request.code,
159
+ code_verifier: request.codeVerifier,
160
+ state: request.state,
161
+ }),
162
+ });
163
+
164
+ if (!response.ok) {
165
+ const error = await response.text();
166
+ throw new Error(`MCP server failed to exchange authorization code: ${error}`);
167
+ }
168
+
169
+ const data = await response.json();
170
+ return data as CallbackResponse;
171
+ }
172
+
173
+ /**
174
+ * Handle authorization status check
175
+ * Checks if a provider is currently authorized
176
+ *
177
+ * @param provider - Provider to check
178
+ * @param sessionToken - Session token from client
179
+ * @returns Authorization status
180
+ *
181
+ * @throws Error if MCP server request fails
182
+ */
183
+ async handleStatus(provider: string, sessionToken: string): Promise<StatusResponse> {
184
+ // Forward to MCP server
185
+ const url = new URL('/oauth/status', this.serverUrl);
186
+ url.searchParams.set('provider', provider);
187
+
188
+ const response = await fetch(url.toString(), {
189
+ method: 'GET',
190
+ headers: {
191
+ 'X-Session-Token': sessionToken,
192
+ },
193
+ });
194
+
195
+ if (!response.ok) {
196
+ // If unauthorized, return not authorized status
197
+ if (response.status === 401) {
198
+ return {
199
+ authorized: false,
200
+ provider,
201
+ };
202
+ }
203
+
204
+ const error = await response.text();
205
+ throw new Error(`MCP server failed to check authorization status: ${error}`);
206
+ }
207
+
208
+ const data = await response.json();
209
+ return data as StatusResponse;
210
+ }
211
+ }
212
+