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.
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Next.js OAuth Callback Handler
3
+ * Provides a pre-built OAuth callback page component for Next.js App Router
4
+ *
5
+ * This eliminates the need for users to manually create callback pages.
6
+ */
7
+
8
+ 'use client';
9
+
10
+ import { useEffect } from 'react';
11
+ import type { OAuthCallbackHandlerConfig } from '../oauth/types.js';
12
+
13
+ /**
14
+ * OAuth Callback Page Component
15
+ *
16
+ * This component:
17
+ * 1. Extracts OAuth callback parameters (code, state, error) from URL
18
+ * 2. Sends them to the opener window (for popup mode) via postMessage
19
+ * 3. Stores them in sessionStorage (for redirect mode)
20
+ * 4. Redirects to the configured URL
21
+ *
22
+ * @param config - Callback handler configuration
23
+ *
24
+ * @example
25
+ * ```tsx
26
+ * // app/oauth/callback/page.tsx
27
+ * import { OAuthCallbackPage } from 'integrate-sdk/oauth-callback';
28
+ *
29
+ * export default function CallbackPage() {
30
+ * return <OAuthCallbackPage redirectUrl="/dashboard" />;
31
+ * }
32
+ * ```
33
+ */
34
+ export function OAuthCallbackPage(config?: OAuthCallbackHandlerConfig) {
35
+ const redirectUrl = config?.redirectUrl || '/';
36
+ const errorRedirectUrl = config?.errorRedirectUrl || '/auth-error';
37
+
38
+ useEffect(() => {
39
+ const params = new URLSearchParams(window.location.search);
40
+ const code = params.get('code');
41
+ const state = params.get('state');
42
+ const error = params.get('error');
43
+ const errorDescription = params.get('error_description');
44
+
45
+ // Handle error case
46
+ if (error) {
47
+ const errorMsg = errorDescription || error;
48
+ console.error('[OAuth Callback] Error:', errorMsg);
49
+ window.location.href = `${errorRedirectUrl}?error=${encodeURIComponent(errorMsg)}`;
50
+ return;
51
+ }
52
+
53
+ // Validate required params
54
+ if (!code || !state) {
55
+ console.error('[OAuth Callback] Missing code or state parameter');
56
+ window.location.href = `${errorRedirectUrl}?error=${encodeURIComponent('Invalid OAuth callback')}`;
57
+ return;
58
+ }
59
+
60
+ // For popup mode: send message to opener window
61
+ if (window.opener) {
62
+ // Send message immediately
63
+ window.opener.postMessage(
64
+ {
65
+ type: 'oauth_callback',
66
+ code,
67
+ state,
68
+ },
69
+ '*'
70
+ );
71
+
72
+ // Close popup after a brief delay to ensure message is received
73
+ setTimeout(() => {
74
+ window.close();
75
+ }, 100);
76
+ } else {
77
+ // For redirect mode: store in sessionStorage and redirect
78
+ try {
79
+ sessionStorage.setItem(
80
+ 'oauth_callback_params',
81
+ JSON.stringify({ code, state })
82
+ );
83
+ } catch (e) {
84
+ console.error('Failed to store OAuth callback params:', e);
85
+ }
86
+
87
+ // Redirect to the configured URL
88
+ setTimeout(() => {
89
+ window.location.href = redirectUrl;
90
+ }, 500);
91
+ }
92
+ }, [redirectUrl, errorRedirectUrl]);
93
+
94
+ return (
95
+ <div
96
+ style={{
97
+ fontFamily:
98
+ '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
99
+ display: 'flex',
100
+ alignItems: 'center',
101
+ justifyContent: 'center',
102
+ minHeight: '100vh',
103
+ margin: 0,
104
+ background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
105
+ color: 'white',
106
+ }}
107
+ >
108
+ <div style={{ textAlign: 'center', padding: '2rem' }}>
109
+ <div
110
+ style={{
111
+ border: '3px solid rgba(255, 255, 255, 0.3)',
112
+ borderRadius: '50%',
113
+ borderTop: '3px solid white',
114
+ width: '40px',
115
+ height: '40px',
116
+ animation: 'spin 1s linear infinite',
117
+ margin: '0 auto 1rem',
118
+ }}
119
+ />
120
+ <h1
121
+ style={{
122
+ margin: '0 0 0.5rem',
123
+ fontSize: '1.5rem',
124
+ fontWeight: 600,
125
+ }}
126
+ >
127
+ Authorization Complete
128
+ </h1>
129
+ <p style={{ margin: 0, opacity: 0.9, fontSize: '0.875rem' }}>
130
+ This window will close automatically...
131
+ </p>
132
+ <style>{`
133
+ @keyframes spin {
134
+ 0% { transform: rotate(0deg); }
135
+ 100% { transform: rotate(360deg); }
136
+ }
137
+ `}</style>
138
+ </div>
139
+ </div>
140
+ );
141
+ }
142
+
143
+ /**
144
+ * Create a default export wrapper for easier usage
145
+ *
146
+ * @example
147
+ * ```tsx
148
+ * // app/oauth/callback/page.tsx
149
+ * import { createOAuthCallbackPage } from 'integrate-sdk/oauth-callback';
150
+ *
151
+ * export default createOAuthCallbackPage({
152
+ * redirectUrl: '/dashboard',
153
+ * });
154
+ * ```
155
+ */
156
+ export function createOAuthCallbackPage(config?: OAuthCallbackHandlerConfig) {
157
+ return function OAuthCallback() {
158
+ return <OAuthCallbackPage {...config} />;
159
+ };
160
+ }
@@ -0,0 +1,318 @@
1
+ /**
2
+ * Next.js OAuth Route Adapter
3
+ * Provides OAuth route handlers for Next.js App Router
4
+ *
5
+ * Note: This file uses type imports only to avoid requiring Next.js at build time.
6
+ * The actual Next.js types are used at runtime when available.
7
+ */
8
+
9
+ import { OAuthHandler, type OAuthHandlerConfig } from './base-handler.js';
10
+
11
+ // Type-only imports to avoid requiring Next.js at build time
12
+ type NextRequest = any;
13
+ type NextResponse = any;
14
+
15
+ /**
16
+ * Create Next.js OAuth route handlers
17
+ *
18
+ * Use this to create secure OAuth API routes in your Next.js application
19
+ * that handle authorization with server-side secrets.
20
+ *
21
+ * @param config - OAuth handler configuration with provider credentials
22
+ * @returns Object with authorize, callback, and status route handlers, plus a unified handler
23
+ *
24
+ * @example
25
+ * **Simple Setup (Recommended)** - One route file handles everything:
26
+ *
27
+ * ```typescript
28
+ * // app/api/integrate/oauth/[action]/route.ts
29
+ * import { createNextOAuthHandler } from 'integrate-sdk';
30
+ *
31
+ * const handler = createNextOAuthHandler({
32
+ * providers: {
33
+ * github: {
34
+ * clientId: process.env.GITHUB_CLIENT_ID!,
35
+ * clientSecret: process.env.GITHUB_CLIENT_SECRET!,
36
+ * },
37
+ * },
38
+ * });
39
+ *
40
+ * export const { POST, GET } = handler.createRoutes();
41
+ * ```
42
+ *
43
+ * @example
44
+ * **Advanced Setup** - Separate routes for each action:
45
+ *
46
+ * ```typescript
47
+ * // app/api/integrate/oauth/authorize/route.ts
48
+ * export const POST = handler.authorize;
49
+ *
50
+ * // app/api/integrate/oauth/callback/route.ts
51
+ * export const POST = handler.callback;
52
+ *
53
+ * // app/api/integrate/oauth/status/route.ts
54
+ * export const GET = handler.status;
55
+ * ```
56
+ */
57
+ export function createNextOAuthHandler(config: OAuthHandlerConfig) {
58
+ const handler = new OAuthHandler(config);
59
+
60
+ const handlers = {
61
+ /**
62
+ * POST /api/integrate/oauth/authorize
63
+ *
64
+ * Request authorization URL from MCP server with server-side OAuth credentials
65
+ *
66
+ * Request body:
67
+ * ```json
68
+ * {
69
+ * "provider": "github",
70
+ * "scopes": ["repo", "user"],
71
+ * "state": "random-state-string",
72
+ * "codeChallenge": "pkce-code-challenge",
73
+ * "codeChallengeMethod": "S256",
74
+ * "redirectUri": "https://yourapp.com/oauth/callback"
75
+ * }
76
+ * ```
77
+ *
78
+ * Response:
79
+ * ```json
80
+ * {
81
+ * "authorizationUrl": "https://github.com/login/oauth/authorize?..."
82
+ * }
83
+ * ```
84
+ *
85
+ * @example
86
+ * ```typescript
87
+ * // app/api/integrate/oauth/authorize/route.ts
88
+ * import { createNextOAuthHandler } from 'integrate-sdk';
89
+ *
90
+ * const handler = createNextOAuthHandler({
91
+ * * providers: {
92
+ * github: {
93
+ * clientId: process.env.GITHUB_CLIENT_ID!,
94
+ * clientSecret: process.env.GITHUB_CLIENT_SECRET!,
95
+ * },
96
+ * },
97
+ * });
98
+ *
99
+ * export const POST = handler.authorize;
100
+ * ```
101
+ */
102
+ async authorize(req: NextRequest): Promise<NextResponse> {
103
+ try {
104
+ const body = await req.json();
105
+ const result = await handler.handleAuthorize(body);
106
+ return Response.json(result);
107
+ } catch (error: any) {
108
+ console.error('[OAuth Authorize] Error:', error);
109
+ return Response.json(
110
+ { error: error.message || 'Failed to get authorization URL' },
111
+ { status: 500 }
112
+ );
113
+ }
114
+ },
115
+
116
+ /**
117
+ * POST /api/integrate/oauth/callback
118
+ *
119
+ * Exchange authorization code for session token
120
+ *
121
+ * Request body:
122
+ * ```json
123
+ * {
124
+ * "provider": "github",
125
+ * "code": "authorization-code",
126
+ * "codeVerifier": "pkce-code-verifier",
127
+ * "state": "state-from-authorize"
128
+ * }
129
+ * ```
130
+ *
131
+ * Response:
132
+ * ```json
133
+ * {
134
+ * "sessionToken": "session-token-123",
135
+ * "provider": "github",
136
+ * "scopes": ["repo", "user"],
137
+ * "expiresAt": 1234567890
138
+ * }
139
+ * ```
140
+ *
141
+ * @example
142
+ * ```typescript
143
+ * // app/api/integrate/oauth/callback/route.ts
144
+ * import { createNextOAuthHandler } from 'integrate-sdk';
145
+ *
146
+ * const handler = createNextOAuthHandler({
147
+ * * providers: {
148
+ * github: {
149
+ * clientId: process.env.GITHUB_CLIENT_ID!,
150
+ * clientSecret: process.env.GITHUB_CLIENT_SECRET!,
151
+ * },
152
+ * },
153
+ * });
154
+ *
155
+ * export const POST = handler.callback;
156
+ * ```
157
+ */
158
+ async callback(req: NextRequest): Promise<NextResponse> {
159
+ try {
160
+ const body = await req.json();
161
+ const result = await handler.handleCallback(body);
162
+ return Response.json(result);
163
+ } catch (error: any) {
164
+ console.error('[OAuth Callback] Error:', error);
165
+ return Response.json(
166
+ { error: error.message || 'Failed to exchange authorization code' },
167
+ { status: 500 }
168
+ );
169
+ }
170
+ },
171
+
172
+ /**
173
+ * GET /api/integrate/oauth/status?provider=github
174
+ *
175
+ * Check if a provider is currently authorized
176
+ *
177
+ * Query parameters:
178
+ * - provider: Provider to check (e.g., "github")
179
+ *
180
+ * Headers:
181
+ * - X-Session-Token: Session token from previous authorization
182
+ *
183
+ * Response:
184
+ * ```json
185
+ * {
186
+ * "authorized": true,
187
+ * "provider": "github",
188
+ * "scopes": ["repo", "user"],
189
+ * "expiresAt": 1234567890
190
+ * }
191
+ * ```
192
+ *
193
+ * @example
194
+ * ```typescript
195
+ * // app/api/integrate/oauth/status/route.ts
196
+ * import { createNextOAuthHandler } from 'integrate-sdk';
197
+ *
198
+ * const handler = createNextOAuthHandler({
199
+ * * providers: {
200
+ * github: {
201
+ * clientId: process.env.GITHUB_CLIENT_ID!,
202
+ * clientSecret: process.env.GITHUB_CLIENT_SECRET!,
203
+ * },
204
+ * },
205
+ * });
206
+ *
207
+ * export const GET = handler.status;
208
+ * ```
209
+ */
210
+ async status(req: NextRequest): Promise<NextResponse> {
211
+ try {
212
+ const provider = req.nextUrl.searchParams.get('provider');
213
+ const sessionToken = req.headers.get('x-session-token');
214
+
215
+ if (!provider) {
216
+ return Response.json(
217
+ { error: 'Missing provider query parameter' },
218
+ { status: 400 }
219
+ );
220
+ }
221
+
222
+ if (!sessionToken) {
223
+ return Response.json(
224
+ { error: 'Missing X-Session-Token header' },
225
+ { status: 400 }
226
+ );
227
+ }
228
+
229
+ const result = await handler.handleStatus(provider, sessionToken);
230
+ return Response.json(result);
231
+ } catch (error: any) {
232
+ console.error('[OAuth Status] Error:', error);
233
+ return Response.json(
234
+ { error: error.message || 'Failed to check authorization status' },
235
+ { status: 500 }
236
+ );
237
+ }
238
+ },
239
+
240
+ /**
241
+ * Create unified route handlers for catch-all route
242
+ *
243
+ * This is the simplest way to set up OAuth routes - create a single catch-all
244
+ * route file that handles all OAuth actions.
245
+ *
246
+ * @returns Object with POST and GET handlers for Next.js dynamic routes
247
+ *
248
+ * @example
249
+ * ```typescript
250
+ * // app/api/integrate/oauth/[action]/route.ts
251
+ * import { createNextOAuthHandler } from 'integrate-sdk';
252
+ *
253
+ * const handler = createNextOAuthHandler({
254
+ * providers: {
255
+ * github: {
256
+ * clientId: process.env.GITHUB_CLIENT_ID!,
257
+ * clientSecret: process.env.GITHUB_CLIENT_SECRET!,
258
+ * },
259
+ * },
260
+ * });
261
+ *
262
+ * export const { POST, GET } = handler.createRoutes();
263
+ * ```
264
+ */
265
+ createRoutes() {
266
+ return {
267
+ /**
268
+ * POST handler for authorize and callback actions
269
+ */
270
+ async POST(
271
+ req: NextRequest,
272
+ context: { params: { action: string } | Promise<{ action: string }> }
273
+ ): Promise<NextResponse> {
274
+ // Handle both Next.js 14 (sync params) and Next.js 15+ (async params)
275
+ const params = context.params instanceof Promise ? await context.params : context.params;
276
+ const action = params.action;
277
+
278
+ if (action === 'authorize') {
279
+ return handlers.authorize(req);
280
+ }
281
+
282
+ if (action === 'callback') {
283
+ return handlers.callback(req);
284
+ }
285
+
286
+ return Response.json(
287
+ { error: `Unknown action: ${action}` },
288
+ { status: 404 }
289
+ );
290
+ },
291
+
292
+ /**
293
+ * GET handler for status action
294
+ */
295
+ async GET(
296
+ req: NextRequest,
297
+ context: { params: { action: string } | Promise<{ action: string }> }
298
+ ): Promise<NextResponse> {
299
+ // Handle both Next.js 14 (sync params) and Next.js 15+ (async params)
300
+ const params = context.params instanceof Promise ? await context.params : context.params;
301
+ const action = params.action;
302
+
303
+ if (action === 'status') {
304
+ return handlers.status(req);
305
+ }
306
+
307
+ return Response.json(
308
+ { error: `Unknown action: ${action}` },
309
+ { status: 404 }
310
+ );
311
+ },
312
+ };
313
+ },
314
+ };
315
+
316
+ return handlers;
317
+ }
318
+