@context-engine-bridge/context-engine-mcp-bridge 0.0.86 → 0.0.88
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/package.json +2 -1
- package/src/oauthHandler.js +41 -21
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@context-engine-bridge/context-engine-mcp-bridge",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.88",
|
|
4
4
|
"description": "Context Engine MCP bridge (http/stdio proxy combining indexer + memory servers)",
|
|
5
5
|
"bin": {
|
|
6
6
|
"ctxce": "bin/ctxce.js",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"test:e2e:ui": "playwright test --ui"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
+
"@context-engine-bridge/context-engine-mcp-bridge": "^0.0.87",
|
|
20
21
|
"@modelcontextprotocol/sdk": "^1.24.3",
|
|
21
22
|
"ignore": "^7.0.5",
|
|
22
23
|
"tar": "^7.5.9",
|
package/src/oauthHandler.js
CHANGED
|
@@ -458,6 +458,9 @@ export function handleOAuthStoreSession(req, res) {
|
|
|
458
458
|
code_challenge_method,
|
|
459
459
|
client_id,
|
|
460
460
|
} = data;
|
|
461
|
+
const normalizedCodeChallengeMethod = code_challenge
|
|
462
|
+
? (code_challenge_method || "S256")
|
|
463
|
+
: (code_challenge_method || null);
|
|
461
464
|
|
|
462
465
|
if (!session_id || !backend_url) {
|
|
463
466
|
res.statusCode = 400;
|
|
@@ -488,6 +491,15 @@ export function handleOAuthStoreSession(req, res) {
|
|
|
488
491
|
return;
|
|
489
492
|
}
|
|
490
493
|
|
|
494
|
+
if (code_challenge && normalizedCodeChallengeMethod !== "S256") {
|
|
495
|
+
res.statusCode = 400;
|
|
496
|
+
res.end(JSON.stringify({
|
|
497
|
+
error: "invalid_request",
|
|
498
|
+
error_description: "Unsupported code_challenge_method",
|
|
499
|
+
}));
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
|
|
491
503
|
// Additional CSRF protection: verify request came from a local browser origin
|
|
492
504
|
// Require Origin or Referer header to be present and from localhost
|
|
493
505
|
const origin = req.headers["origin"] || req.headers["referer"];
|
|
@@ -528,7 +540,7 @@ export function handleOAuthStoreSession(req, res) {
|
|
|
528
540
|
sessionId: session_id,
|
|
529
541
|
backendUrl: backend_url,
|
|
530
542
|
codeChallenge: code_challenge,
|
|
531
|
-
codeChallengeMethod:
|
|
543
|
+
codeChallengeMethod: normalizedCodeChallengeMethod,
|
|
532
544
|
redirectUri: redirect_uri,
|
|
533
545
|
createdAt: Date.now(),
|
|
534
546
|
});
|
|
@@ -560,8 +572,7 @@ export function handleOAuthToken(req, res) {
|
|
|
560
572
|
const code = data.get("code");
|
|
561
573
|
const redirectUri = data.get("redirect_uri");
|
|
562
574
|
const clientId = data.get("client_id");
|
|
563
|
-
|
|
564
|
-
data.get("code_verifier");
|
|
575
|
+
const codeVerifier = data.get("code_verifier");
|
|
565
576
|
const grantType = data.get("grant_type");
|
|
566
577
|
|
|
567
578
|
res.setHeader("Content-Type", "application/json");
|
|
@@ -605,24 +616,33 @@ export function handleOAuthToken(req, res) {
|
|
|
605
616
|
return;
|
|
606
617
|
}
|
|
607
618
|
|
|
608
|
-
//
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
619
|
+
// PKCE validation (RFC 7636)
|
|
620
|
+
if (pendingData.codeChallenge) {
|
|
621
|
+
const codeChallengeMethod = pendingData.codeChallengeMethod || "S256";
|
|
622
|
+
if (codeChallengeMethod !== "S256") {
|
|
623
|
+
pendingCodes.delete(code);
|
|
624
|
+
res.statusCode = 400;
|
|
625
|
+
res.end(JSON.stringify({
|
|
626
|
+
error: "invalid_grant",
|
|
627
|
+
error_description: "Unsupported code_challenge_method",
|
|
628
|
+
}));
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
if (!codeVerifier) {
|
|
632
|
+
pendingCodes.delete(code);
|
|
633
|
+
res.statusCode = 400;
|
|
634
|
+
res.end(JSON.stringify({ error: "invalid_grant", error_description: "code_verifier required for PKCE" }));
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
const crypto = await import("node:crypto");
|
|
638
|
+
const expectedChallenge = crypto.createHash("sha256").update(codeVerifier).digest("base64url");
|
|
639
|
+
if (expectedChallenge !== pendingData.codeChallenge) {
|
|
640
|
+
pendingCodes.delete(code);
|
|
641
|
+
res.statusCode = 400;
|
|
642
|
+
res.end(JSON.stringify({ error: "invalid_grant", error_description: "code_verifier validation failed" }));
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
626
646
|
|
|
627
647
|
// Clean up expired tokens periodically to prevent unbounded growth
|
|
628
648
|
cleanupExpiredTokens();
|