integrate-sdk 0.7.48 → 0.7.49
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/adapters/auto-routes.d.ts.map +1 -1
- package/dist/adapters/auto-routes.js +380 -24
- package/dist/adapters/base-handler.d.ts +50 -6
- package/dist/adapters/base-handler.d.ts.map +1 -1
- package/dist/adapters/base-handler.js +368 -18
- package/dist/adapters/context-cookie.d.ts +85 -0
- package/dist/adapters/context-cookie.d.ts.map +1 -0
- package/dist/adapters/context-cookie.js +152 -0
- package/dist/adapters/index.js +399 -37
- package/dist/adapters/nextjs.d.ts.map +1 -1
- package/dist/adapters/nextjs.js +387 -31
- package/dist/adapters/node.d.ts.map +1 -1
- package/dist/adapters/node.js +380 -24
- package/dist/adapters/session-detector.d.ts +37 -0
- package/dist/adapters/session-detector.d.ts.map +1 -0
- package/dist/adapters/session-detector.js +157 -0
- package/dist/adapters/solid-start.js +387 -31
- package/dist/adapters/svelte-kit.js +387 -31
- package/dist/index.js +392 -30
- package/dist/oauth.js +380 -24
- package/dist/server.js +401 -33
- package/dist/src/adapters/auto-routes.d.ts.map +1 -1
- package/dist/src/adapters/base-handler.d.ts +50 -6
- package/dist/src/adapters/base-handler.d.ts.map +1 -1
- package/dist/src/adapters/context-cookie.d.ts +85 -0
- package/dist/src/adapters/context-cookie.d.ts.map +1 -0
- package/dist/src/adapters/nextjs.d.ts.map +1 -1
- package/dist/src/adapters/node.d.ts.map +1 -1
- package/dist/src/adapters/session-detector.d.ts +37 -0
- package/dist/src/adapters/session-detector.d.ts.map +1 -0
- package/dist/src/config/types.d.ts +24 -0
- package/dist/src/config/types.d.ts.map +1 -1
- package/dist/src/server.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/adapters/node.js
CHANGED
|
@@ -10,6 +10,280 @@ var __export = (target, all) => {
|
|
|
10
10
|
};
|
|
11
11
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
12
12
|
|
|
13
|
+
// session-detector.ts
|
|
14
|
+
var exports_session_detector = {};
|
|
15
|
+
__export(exports_session_detector, {
|
|
16
|
+
tryDecodeJWT: () => tryDecodeJWT,
|
|
17
|
+
detectSessionContext: () => detectSessionContext
|
|
18
|
+
});
|
|
19
|
+
function tryDecodeJWT(token) {
|
|
20
|
+
try {
|
|
21
|
+
const parts = token.split(".");
|
|
22
|
+
if (parts.length !== 3) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const payloadPart = parts[1];
|
|
26
|
+
if (!payloadPart) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const base64 = payloadPart.replace(/-/g, "+").replace(/_/g, "/");
|
|
30
|
+
const jsonPayload = decodeURIComponent(atob(base64).split("").map((c) => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2)).join(""));
|
|
31
|
+
return JSON.parse(jsonPayload);
|
|
32
|
+
} catch (error) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function getCookies(request) {
|
|
37
|
+
const cookies = new Map;
|
|
38
|
+
const cookieHeader = request.headers.get("cookie");
|
|
39
|
+
if (!cookieHeader) {
|
|
40
|
+
return cookies;
|
|
41
|
+
}
|
|
42
|
+
const pairs = cookieHeader.split(";");
|
|
43
|
+
for (const pair of pairs) {
|
|
44
|
+
const [name, ...valueParts] = pair.split("=");
|
|
45
|
+
if (name && valueParts.length > 0) {
|
|
46
|
+
const trimmedName = name.trim();
|
|
47
|
+
const value = valueParts.join("=").trim();
|
|
48
|
+
cookies.set(trimmedName, value);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return cookies;
|
|
52
|
+
}
|
|
53
|
+
function tryBetterAuth(cookies) {
|
|
54
|
+
const sessionToken = cookies.get("better-auth.session_token");
|
|
55
|
+
if (!sessionToken) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const payload = tryDecodeJWT(sessionToken);
|
|
59
|
+
if (!payload) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const userId = payload.sub || payload.userId || payload.user_id || payload.id;
|
|
63
|
+
if (!userId) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
userId,
|
|
68
|
+
sessionId: payload.jti || payload.sessionId
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function tryNextAuth(cookies) {
|
|
72
|
+
const sessionToken = cookies.get("__Secure-next-auth.session-token") || cookies.get("next-auth.session-token");
|
|
73
|
+
if (!sessionToken) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (sessionToken.includes(".")) {
|
|
77
|
+
const payload = tryDecodeJWT(sessionToken);
|
|
78
|
+
if (payload) {
|
|
79
|
+
return {
|
|
80
|
+
userId: payload.sub || payload.userId || payload.user_id || payload.id,
|
|
81
|
+
sessionId: payload.jti
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
function tryClerk(cookies) {
|
|
88
|
+
const sessionToken = cookies.get("__session");
|
|
89
|
+
if (!sessionToken) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const payload = tryDecodeJWT(sessionToken);
|
|
93
|
+
if (!payload) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
userId: payload.sub || payload.userId,
|
|
98
|
+
organizationId: payload.org_id || payload.organizationId,
|
|
99
|
+
sessionId: payload.sid || payload.sessionId
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function tryLucia(cookies) {
|
|
103
|
+
const sessionToken = cookies.get("lucia_session");
|
|
104
|
+
if (!sessionToken) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
sessionId: sessionToken
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function tryGenericSession(cookies) {
|
|
112
|
+
const sessionToken = cookies.get("auth_session") || cookies.get("session");
|
|
113
|
+
if (!sessionToken) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (sessionToken.includes(".")) {
|
|
117
|
+
const payload = tryDecodeJWT(sessionToken);
|
|
118
|
+
if (payload) {
|
|
119
|
+
return {
|
|
120
|
+
userId: payload.sub || payload.userId || payload.user_id || payload.id,
|
|
121
|
+
sessionId: payload.jti || payload.sessionId || payload.sid
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
sessionId: sessionToken
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
async function detectSessionContext(request) {
|
|
130
|
+
const cookies = getCookies(request);
|
|
131
|
+
let context;
|
|
132
|
+
context = tryBetterAuth(cookies);
|
|
133
|
+
if (context?.userId) {
|
|
134
|
+
return context;
|
|
135
|
+
}
|
|
136
|
+
context = tryNextAuth(cookies);
|
|
137
|
+
if (context?.userId) {
|
|
138
|
+
return context;
|
|
139
|
+
}
|
|
140
|
+
context = tryClerk(cookies);
|
|
141
|
+
if (context?.userId) {
|
|
142
|
+
return context;
|
|
143
|
+
}
|
|
144
|
+
context = tryLucia(cookies);
|
|
145
|
+
if (context?.userId) {
|
|
146
|
+
return context;
|
|
147
|
+
}
|
|
148
|
+
context = tryGenericSession(cookies);
|
|
149
|
+
if (context?.userId) {
|
|
150
|
+
return context;
|
|
151
|
+
}
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// context-cookie.ts
|
|
156
|
+
var exports_context_cookie = {};
|
|
157
|
+
__export(exports_context_cookie, {
|
|
158
|
+
readContextCookie: () => readContextCookie,
|
|
159
|
+
getSetCookieHeader: () => getSetCookieHeader,
|
|
160
|
+
getContextCookieFromRequest: () => getContextCookieFromRequest,
|
|
161
|
+
getClearCookieHeader: () => getClearCookieHeader,
|
|
162
|
+
createContextCookie: () => createContextCookie,
|
|
163
|
+
CONTEXT_COOKIE_NAME: () => CONTEXT_COOKIE_NAME,
|
|
164
|
+
CONTEXT_COOKIE_MAX_AGE: () => CONTEXT_COOKIE_MAX_AGE
|
|
165
|
+
});
|
|
166
|
+
async function deriveKey(secret) {
|
|
167
|
+
const encoder = new TextEncoder;
|
|
168
|
+
const secretData = encoder.encode(secret);
|
|
169
|
+
const keyMaterial = await crypto.subtle.importKey("raw", secretData, { name: "PBKDF2" }, false, ["deriveBits", "deriveKey"]);
|
|
170
|
+
const salt = encoder.encode("integrate-oauth-context-v1");
|
|
171
|
+
return await crypto.subtle.deriveKey({
|
|
172
|
+
name: "PBKDF2",
|
|
173
|
+
salt,
|
|
174
|
+
iterations: 1e5,
|
|
175
|
+
hash: "SHA-256"
|
|
176
|
+
}, keyMaterial, { name: "AES-GCM", length: 256 }, false, ["encrypt", "decrypt"]);
|
|
177
|
+
}
|
|
178
|
+
async function encryptPayload(payload, secret) {
|
|
179
|
+
const key = await deriveKey(secret);
|
|
180
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
181
|
+
const encoder = new TextEncoder;
|
|
182
|
+
const data = encoder.encode(JSON.stringify(payload));
|
|
183
|
+
const encrypted = await crypto.subtle.encrypt({
|
|
184
|
+
name: "AES-GCM",
|
|
185
|
+
iv
|
|
186
|
+
}, key, data);
|
|
187
|
+
const combined = new Uint8Array(iv.length + encrypted.byteLength);
|
|
188
|
+
combined.set(iv, 0);
|
|
189
|
+
combined.set(new Uint8Array(encrypted), iv.length);
|
|
190
|
+
return base64UrlEncode(combined);
|
|
191
|
+
}
|
|
192
|
+
async function decryptPayload(cookieValue, secret) {
|
|
193
|
+
try {
|
|
194
|
+
const combined = base64UrlDecode(cookieValue);
|
|
195
|
+
const iv = combined.slice(0, 12);
|
|
196
|
+
const encrypted = combined.slice(12);
|
|
197
|
+
const key = await deriveKey(secret);
|
|
198
|
+
const decrypted = await crypto.subtle.decrypt({
|
|
199
|
+
name: "AES-GCM",
|
|
200
|
+
iv
|
|
201
|
+
}, key, encrypted);
|
|
202
|
+
const decoder = new TextDecoder;
|
|
203
|
+
const json = decoder.decode(decrypted);
|
|
204
|
+
const payload = JSON.parse(json);
|
|
205
|
+
const age = Date.now() - payload.timestamp;
|
|
206
|
+
if (age > CONTEXT_COOKIE_MAX_AGE * 1000) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
return payload;
|
|
210
|
+
} catch (error) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function base64UrlEncode(data) {
|
|
215
|
+
const base64 = btoa(String.fromCharCode(...data));
|
|
216
|
+
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
217
|
+
}
|
|
218
|
+
function base64UrlDecode(str) {
|
|
219
|
+
let base64 = str.replace(/-/g, "+").replace(/_/g, "/");
|
|
220
|
+
const padding = base64.length % 4;
|
|
221
|
+
if (padding) {
|
|
222
|
+
base64 += "=".repeat(4 - padding);
|
|
223
|
+
}
|
|
224
|
+
const binary = atob(base64);
|
|
225
|
+
const bytes = new Uint8Array(binary.length);
|
|
226
|
+
for (let i = 0;i < binary.length; i++) {
|
|
227
|
+
bytes[i] = binary.charCodeAt(i);
|
|
228
|
+
}
|
|
229
|
+
return bytes;
|
|
230
|
+
}
|
|
231
|
+
async function createContextCookie(context, provider, secret) {
|
|
232
|
+
const payload = {
|
|
233
|
+
context,
|
|
234
|
+
provider,
|
|
235
|
+
timestamp: Date.now()
|
|
236
|
+
};
|
|
237
|
+
return await encryptPayload(payload, secret);
|
|
238
|
+
}
|
|
239
|
+
async function readContextCookie(cookieValue, secret) {
|
|
240
|
+
const payload = await decryptPayload(cookieValue, secret);
|
|
241
|
+
if (!payload) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
return {
|
|
245
|
+
context: payload.context,
|
|
246
|
+
provider: payload.provider
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
function getSetCookieHeader(cookieValue, maxAge = CONTEXT_COOKIE_MAX_AGE) {
|
|
250
|
+
const attributes = [
|
|
251
|
+
`${CONTEXT_COOKIE_NAME}=${cookieValue}`,
|
|
252
|
+
`Max-Age=${maxAge}`,
|
|
253
|
+
"HttpOnly",
|
|
254
|
+
"Secure",
|
|
255
|
+
"SameSite=Lax",
|
|
256
|
+
"Path=/"
|
|
257
|
+
];
|
|
258
|
+
return attributes.join("; ");
|
|
259
|
+
}
|
|
260
|
+
function getClearCookieHeader() {
|
|
261
|
+
const attributes = [
|
|
262
|
+
`${CONTEXT_COOKIE_NAME}=`,
|
|
263
|
+
"Max-Age=0",
|
|
264
|
+
"HttpOnly",
|
|
265
|
+
"Secure",
|
|
266
|
+
"SameSite=Lax",
|
|
267
|
+
"Path=/"
|
|
268
|
+
];
|
|
269
|
+
return attributes.join("; ");
|
|
270
|
+
}
|
|
271
|
+
function getContextCookieFromRequest(request) {
|
|
272
|
+
const cookieHeader = request.headers.get("cookie");
|
|
273
|
+
if (!cookieHeader) {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
const cookies = cookieHeader.split(";");
|
|
277
|
+
for (const cookie of cookies) {
|
|
278
|
+
const [name, ...valueParts] = cookie.split("=");
|
|
279
|
+
if (name && name.trim() === CONTEXT_COOKIE_NAME) {
|
|
280
|
+
return valueParts.join("=").trim();
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
var CONTEXT_COOKIE_NAME = "__integrate_oauth_ctx", CONTEXT_COOKIE_MAX_AGE = 300;
|
|
286
|
+
|
|
13
287
|
// base-handler.ts
|
|
14
288
|
class OAuthHandler {
|
|
15
289
|
config;
|
|
@@ -33,25 +307,35 @@ class OAuthHandler {
|
|
|
33
307
|
return headers;
|
|
34
308
|
}
|
|
35
309
|
async handleAuthorize(request) {
|
|
36
|
-
|
|
310
|
+
let webRequest;
|
|
311
|
+
let authorizeRequest;
|
|
312
|
+
if (request instanceof Request) {
|
|
313
|
+
webRequest = request;
|
|
314
|
+
authorizeRequest = await request.json();
|
|
315
|
+
} else if (typeof request === "object" && "json" in request && typeof request.json === "function") {
|
|
316
|
+
authorizeRequest = await request.json();
|
|
317
|
+
} else {
|
|
318
|
+
authorizeRequest = request;
|
|
319
|
+
}
|
|
320
|
+
const providerConfig = this.config.providers[authorizeRequest.provider];
|
|
37
321
|
if (!providerConfig) {
|
|
38
|
-
throw new Error(`Provider ${
|
|
322
|
+
throw new Error(`Provider ${authorizeRequest.provider} not configured. Add OAuth credentials to your API route configuration.`);
|
|
39
323
|
}
|
|
40
324
|
if (!providerConfig.clientId || !providerConfig.clientSecret) {
|
|
41
|
-
throw new Error(`Missing OAuth credentials for ${
|
|
325
|
+
throw new Error(`Missing OAuth credentials for ${authorizeRequest.provider}. Check your environment variables.`);
|
|
42
326
|
}
|
|
43
327
|
const url = new URL("/oauth/authorize", this.serverUrl);
|
|
44
|
-
url.searchParams.set("provider",
|
|
328
|
+
url.searchParams.set("provider", authorizeRequest.provider);
|
|
45
329
|
url.searchParams.set("client_id", providerConfig.clientId);
|
|
46
330
|
url.searchParams.set("client_secret", providerConfig.clientSecret);
|
|
47
|
-
const scopes =
|
|
331
|
+
const scopes = authorizeRequest.scopes || providerConfig.scopes || [];
|
|
48
332
|
if (scopes.length > 0) {
|
|
49
333
|
url.searchParams.set("scope", scopes.join(","));
|
|
50
334
|
}
|
|
51
|
-
url.searchParams.set("state",
|
|
52
|
-
url.searchParams.set("code_challenge",
|
|
53
|
-
url.searchParams.set("code_challenge_method",
|
|
54
|
-
const redirectUri =
|
|
335
|
+
url.searchParams.set("state", authorizeRequest.state);
|
|
336
|
+
url.searchParams.set("code_challenge", authorizeRequest.codeChallenge);
|
|
337
|
+
url.searchParams.set("code_challenge_method", authorizeRequest.codeChallengeMethod);
|
|
338
|
+
const redirectUri = authorizeRequest.redirectUri || providerConfig.redirectUri;
|
|
55
339
|
if (redirectUri) {
|
|
56
340
|
url.searchParams.set("redirect_uri", redirectUri);
|
|
57
341
|
}
|
|
@@ -64,15 +348,62 @@ class OAuthHandler {
|
|
|
64
348
|
throw new Error(`MCP server failed to generate authorization URL: ${error}`);
|
|
65
349
|
}
|
|
66
350
|
const data = await response.json();
|
|
67
|
-
|
|
351
|
+
const result = data;
|
|
352
|
+
if (webRequest) {
|
|
353
|
+
try {
|
|
354
|
+
const { detectSessionContext: detectSessionContext2 } = await Promise.resolve().then(() => exports_session_detector);
|
|
355
|
+
const { createContextCookie: createContextCookie2, getSetCookieHeader: getSetCookieHeader2 } = await Promise.resolve().then(() => exports_context_cookie);
|
|
356
|
+
let context;
|
|
357
|
+
if (this.config.getSessionContext) {
|
|
358
|
+
context = await this.config.getSessionContext(webRequest);
|
|
359
|
+
}
|
|
360
|
+
if (!context || !context.userId) {
|
|
361
|
+
context = await detectSessionContext2(webRequest);
|
|
362
|
+
}
|
|
363
|
+
if (context && context.userId) {
|
|
364
|
+
const secret = this.apiKey || providerConfig.clientSecret;
|
|
365
|
+
const cookieValue = await createContextCookie2(context, authorizeRequest.provider, secret);
|
|
366
|
+
result.setCookie = getSetCookieHeader2(cookieValue);
|
|
367
|
+
}
|
|
368
|
+
} catch (error) {
|
|
369
|
+
console.warn("[OAuth] Failed to capture user context:", error);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return result;
|
|
68
373
|
}
|
|
69
374
|
async handleCallback(request) {
|
|
70
|
-
|
|
375
|
+
let webRequest;
|
|
376
|
+
let callbackRequest;
|
|
377
|
+
if (request instanceof Request) {
|
|
378
|
+
webRequest = request;
|
|
379
|
+
callbackRequest = await request.json();
|
|
380
|
+
} else if (typeof request === "object" && "json" in request && typeof request.json === "function") {
|
|
381
|
+
callbackRequest = await request.json();
|
|
382
|
+
} else {
|
|
383
|
+
callbackRequest = request;
|
|
384
|
+
}
|
|
385
|
+
const providerConfig = this.config.providers[callbackRequest.provider];
|
|
71
386
|
if (!providerConfig) {
|
|
72
|
-
throw new Error(`Provider ${
|
|
387
|
+
throw new Error(`Provider ${callbackRequest.provider} not configured. Add OAuth credentials to your API route configuration.`);
|
|
73
388
|
}
|
|
74
389
|
if (!providerConfig.clientId || !providerConfig.clientSecret) {
|
|
75
|
-
throw new Error(`Missing OAuth credentials for ${
|
|
390
|
+
throw new Error(`Missing OAuth credentials for ${callbackRequest.provider}. Check your environment variables.`);
|
|
391
|
+
}
|
|
392
|
+
let context;
|
|
393
|
+
if (webRequest) {
|
|
394
|
+
try {
|
|
395
|
+
const { getContextCookieFromRequest: getContextCookieFromRequest2, readContextCookie: readContextCookie2 } = await Promise.resolve().then(() => exports_context_cookie);
|
|
396
|
+
const cookieValue = getContextCookieFromRequest2(webRequest);
|
|
397
|
+
if (cookieValue) {
|
|
398
|
+
const secret = this.apiKey || providerConfig.clientSecret;
|
|
399
|
+
const contextData = await readContextCookie2(cookieValue, secret);
|
|
400
|
+
if (contextData && contextData.provider === callbackRequest.provider) {
|
|
401
|
+
context = contextData.context;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
} catch (error) {
|
|
405
|
+
console.warn("[OAuth] Failed to restore user context:", error);
|
|
406
|
+
}
|
|
76
407
|
}
|
|
77
408
|
const url = new URL("/oauth/callback", this.serverUrl);
|
|
78
409
|
const response = await fetch(url.toString(), {
|
|
@@ -81,10 +412,10 @@ class OAuthHandler {
|
|
|
81
412
|
"Content-Type": "application/json"
|
|
82
413
|
}),
|
|
83
414
|
body: JSON.stringify({
|
|
84
|
-
provider:
|
|
85
|
-
code:
|
|
86
|
-
code_verifier:
|
|
87
|
-
state:
|
|
415
|
+
provider: callbackRequest.provider,
|
|
416
|
+
code: callbackRequest.code,
|
|
417
|
+
code_verifier: callbackRequest.codeVerifier,
|
|
418
|
+
state: callbackRequest.state,
|
|
88
419
|
client_id: providerConfig.clientId,
|
|
89
420
|
client_secret: providerConfig.clientSecret,
|
|
90
421
|
redirect_uri: providerConfig.redirectUri
|
|
@@ -95,7 +426,26 @@ class OAuthHandler {
|
|
|
95
426
|
throw new Error(`MCP server failed to exchange authorization code: ${error}`);
|
|
96
427
|
}
|
|
97
428
|
const data = await response.json();
|
|
98
|
-
|
|
429
|
+
const result = data;
|
|
430
|
+
if (this.config.setProviderToken && context) {
|
|
431
|
+
try {
|
|
432
|
+
const tokenData = {
|
|
433
|
+
accessToken: result.accessToken,
|
|
434
|
+
refreshToken: result.refreshToken,
|
|
435
|
+
tokenType: result.tokenType,
|
|
436
|
+
expiresIn: result.expiresIn,
|
|
437
|
+
expiresAt: result.expiresAt
|
|
438
|
+
};
|
|
439
|
+
await this.config.setProviderToken(callbackRequest.provider, tokenData, context);
|
|
440
|
+
} catch (error) {
|
|
441
|
+
console.error("[OAuth] Failed to save provider token:", error);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
if (webRequest) {
|
|
445
|
+
const { getClearCookieHeader: getClearCookieHeader2 } = await Promise.resolve().then(() => exports_context_cookie);
|
|
446
|
+
result.clearCookie = getClearCookieHeader2();
|
|
447
|
+
}
|
|
448
|
+
return result;
|
|
99
449
|
}
|
|
100
450
|
async handleStatus(provider, accessToken) {
|
|
101
451
|
const url = new URL("/oauth/status", this.serverUrl);
|
|
@@ -231,18 +581,24 @@ function toNodeHandler(config) {
|
|
|
231
581
|
let webRes;
|
|
232
582
|
if (req.method === "POST") {
|
|
233
583
|
if (action === "authorize") {
|
|
234
|
-
const
|
|
235
|
-
const
|
|
584
|
+
const result = await oauthHandler.handleAuthorize(webReq);
|
|
585
|
+
const headers = { "Content-Type": "application/json" };
|
|
586
|
+
if (result.setCookie) {
|
|
587
|
+
headers["Set-Cookie"] = result.setCookie;
|
|
588
|
+
}
|
|
236
589
|
webRes = new Response(JSON.stringify(result), {
|
|
237
590
|
status: 200,
|
|
238
|
-
headers
|
|
591
|
+
headers
|
|
239
592
|
});
|
|
240
593
|
} else if (action === "callback") {
|
|
241
|
-
const
|
|
242
|
-
const
|
|
594
|
+
const result = await oauthHandler.handleCallback(webReq);
|
|
595
|
+
const headers = { "Content-Type": "application/json" };
|
|
596
|
+
if (result.clearCookie) {
|
|
597
|
+
headers["Set-Cookie"] = result.clearCookie;
|
|
598
|
+
}
|
|
243
599
|
webRes = new Response(JSON.stringify(result), {
|
|
244
600
|
status: 200,
|
|
245
|
-
headers
|
|
601
|
+
headers
|
|
246
602
|
});
|
|
247
603
|
} else if (action === "disconnect") {
|
|
248
604
|
const authHeader = webReq.headers.get("authorization");
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Detection Utilities
|
|
3
|
+
* Auto-detect user context from common auth library cookies and sessions
|
|
4
|
+
*/
|
|
5
|
+
import type { MCPContext } from '../config/types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Common session cookie names used by popular auth libraries
|
|
8
|
+
* Note: Currently auto-detected based on cookie names in specific functions
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Try to decode a JWT token without verification (for reading claims only)
|
|
12
|
+
* This is safe because we only read user ID, not verify authenticity
|
|
13
|
+
*
|
|
14
|
+
* @param token - JWT token string
|
|
15
|
+
* @returns Decoded payload or undefined if invalid
|
|
16
|
+
*/
|
|
17
|
+
export declare function tryDecodeJWT(token: string): any | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Automatically detect user context from request cookies
|
|
20
|
+
* Tries to extract userId, organizationId, etc. from common auth library sessions
|
|
21
|
+
*
|
|
22
|
+
* This is a best-effort attempt - if automatic detection fails, users should
|
|
23
|
+
* provide a custom getSessionContext callback
|
|
24
|
+
*
|
|
25
|
+
* @param request - Web Request object
|
|
26
|
+
* @returns User context or undefined if not detected
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* const context = await detectSessionContext(request);
|
|
31
|
+
* if (context?.userId) {
|
|
32
|
+
* console.log('User ID:', context.userId);
|
|
33
|
+
* }
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare function detectSessionContext(request: Request): Promise<MCPContext | undefined>;
|
|
37
|
+
//# sourceMappingURL=session-detector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-detector.d.ts","sourceRoot":"","sources":["../../../src/adapters/session-detector.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD;;;GAGG;AAWH;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,SAAS,CA4B3D;AA2KD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAsC5F"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __export = (target, all) => {
|
|
3
|
+
for (var name in all)
|
|
4
|
+
__defProp(target, name, {
|
|
5
|
+
get: all[name],
|
|
6
|
+
enumerable: true,
|
|
7
|
+
configurable: true,
|
|
8
|
+
set: (newValue) => all[name] = () => newValue
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
12
|
+
|
|
13
|
+
// session-detector.ts
|
|
14
|
+
var exports_session_detector = {};
|
|
15
|
+
__export(exports_session_detector, {
|
|
16
|
+
tryDecodeJWT: () => tryDecodeJWT,
|
|
17
|
+
detectSessionContext: () => detectSessionContext
|
|
18
|
+
});
|
|
19
|
+
function tryDecodeJWT(token) {
|
|
20
|
+
try {
|
|
21
|
+
const parts = token.split(".");
|
|
22
|
+
if (parts.length !== 3) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const payloadPart = parts[1];
|
|
26
|
+
if (!payloadPart) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const base64 = payloadPart.replace(/-/g, "+").replace(/_/g, "/");
|
|
30
|
+
const jsonPayload = decodeURIComponent(atob(base64).split("").map((c) => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2)).join(""));
|
|
31
|
+
return JSON.parse(jsonPayload);
|
|
32
|
+
} catch (error) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function getCookies(request) {
|
|
37
|
+
const cookies = new Map;
|
|
38
|
+
const cookieHeader = request.headers.get("cookie");
|
|
39
|
+
if (!cookieHeader) {
|
|
40
|
+
return cookies;
|
|
41
|
+
}
|
|
42
|
+
const pairs = cookieHeader.split(";");
|
|
43
|
+
for (const pair of pairs) {
|
|
44
|
+
const [name, ...valueParts] = pair.split("=");
|
|
45
|
+
if (name && valueParts.length > 0) {
|
|
46
|
+
const trimmedName = name.trim();
|
|
47
|
+
const value = valueParts.join("=").trim();
|
|
48
|
+
cookies.set(trimmedName, value);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return cookies;
|
|
52
|
+
}
|
|
53
|
+
function tryBetterAuth(cookies) {
|
|
54
|
+
const sessionToken = cookies.get("better-auth.session_token");
|
|
55
|
+
if (!sessionToken) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const payload = tryDecodeJWT(sessionToken);
|
|
59
|
+
if (!payload) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const userId = payload.sub || payload.userId || payload.user_id || payload.id;
|
|
63
|
+
if (!userId) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
userId,
|
|
68
|
+
sessionId: payload.jti || payload.sessionId
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function tryNextAuth(cookies) {
|
|
72
|
+
const sessionToken = cookies.get("__Secure-next-auth.session-token") || cookies.get("next-auth.session-token");
|
|
73
|
+
if (!sessionToken) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (sessionToken.includes(".")) {
|
|
77
|
+
const payload = tryDecodeJWT(sessionToken);
|
|
78
|
+
if (payload) {
|
|
79
|
+
return {
|
|
80
|
+
userId: payload.sub || payload.userId || payload.user_id || payload.id,
|
|
81
|
+
sessionId: payload.jti
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
function tryClerk(cookies) {
|
|
88
|
+
const sessionToken = cookies.get("__session");
|
|
89
|
+
if (!sessionToken) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const payload = tryDecodeJWT(sessionToken);
|
|
93
|
+
if (!payload) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
userId: payload.sub || payload.userId,
|
|
98
|
+
organizationId: payload.org_id || payload.organizationId,
|
|
99
|
+
sessionId: payload.sid || payload.sessionId
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function tryLucia(cookies) {
|
|
103
|
+
const sessionToken = cookies.get("lucia_session");
|
|
104
|
+
if (!sessionToken) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
sessionId: sessionToken
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function tryGenericSession(cookies) {
|
|
112
|
+
const sessionToken = cookies.get("auth_session") || cookies.get("session");
|
|
113
|
+
if (!sessionToken) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (sessionToken.includes(".")) {
|
|
117
|
+
const payload = tryDecodeJWT(sessionToken);
|
|
118
|
+
if (payload) {
|
|
119
|
+
return {
|
|
120
|
+
userId: payload.sub || payload.userId || payload.user_id || payload.id,
|
|
121
|
+
sessionId: payload.jti || payload.sessionId || payload.sid
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
sessionId: sessionToken
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
async function detectSessionContext(request) {
|
|
130
|
+
const cookies = getCookies(request);
|
|
131
|
+
let context;
|
|
132
|
+
context = tryBetterAuth(cookies);
|
|
133
|
+
if (context?.userId) {
|
|
134
|
+
return context;
|
|
135
|
+
}
|
|
136
|
+
context = tryNextAuth(cookies);
|
|
137
|
+
if (context?.userId) {
|
|
138
|
+
return context;
|
|
139
|
+
}
|
|
140
|
+
context = tryClerk(cookies);
|
|
141
|
+
if (context?.userId) {
|
|
142
|
+
return context;
|
|
143
|
+
}
|
|
144
|
+
context = tryLucia(cookies);
|
|
145
|
+
if (context?.userId) {
|
|
146
|
+
return context;
|
|
147
|
+
}
|
|
148
|
+
context = tryGenericSession(cookies);
|
|
149
|
+
if (context?.userId) {
|
|
150
|
+
return context;
|
|
151
|
+
}
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
export {
|
|
155
|
+
tryDecodeJWT,
|
|
156
|
+
detectSessionContext
|
|
157
|
+
};
|