strapi-oauth-mcp-manager 0.1.2 → 0.1.3
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/server/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
const node_crypto = require("node:crypto");
|
|
3
3
|
const PLUGIN_ID = "strapi-oauth-mcp-manager";
|
|
4
4
|
const PLUGIN_UID$3 = `plugin::${PLUGIN_ID}`;
|
|
5
|
+
const MCP_ENDPOINT_PATTERN = /^\/api\/[^/]+\/mcp(\/.*)?$/;
|
|
5
6
|
function extractBearerToken(authHeader) {
|
|
6
7
|
if (!authHeader?.startsWith("Bearer ")) {
|
|
7
8
|
return null;
|
|
@@ -45,32 +46,15 @@ async function validateOAuthToken(token, strapi) {
|
|
|
45
46
|
return { valid: false, error: "Token validation failed" };
|
|
46
47
|
}
|
|
47
48
|
}
|
|
49
|
+
function isMcpEndpoint(path) {
|
|
50
|
+
return MCP_ENDPOINT_PATTERN.test(path);
|
|
51
|
+
}
|
|
48
52
|
const mcpOauthMiddleware = (config2, { strapi }) => {
|
|
49
|
-
let endpointCache = [];
|
|
50
|
-
let lastCacheUpdate = 0;
|
|
51
|
-
const CACHE_TTL = 6e4;
|
|
52
|
-
async function refreshEndpointCache() {
|
|
53
|
-
const now = Date.now();
|
|
54
|
-
if (now - lastCacheUpdate > CACHE_TTL) {
|
|
55
|
-
try {
|
|
56
|
-
const endpoints = await strapi.documents(`${PLUGIN_UID$3}.mcp-endpoint`).findMany({
|
|
57
|
-
filters: { active: true }
|
|
58
|
-
});
|
|
59
|
-
endpointCache = endpoints.map((e) => e.path);
|
|
60
|
-
lastCacheUpdate = now;
|
|
61
|
-
} catch (error) {
|
|
62
|
-
strapi.log.error(`[${PLUGIN_ID}] Error refreshing endpoint cache`, { error });
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
function isProtectedPath(path) {
|
|
67
|
-
return endpointCache.some((endpoint) => path.includes(endpoint));
|
|
68
|
-
}
|
|
69
53
|
return async (ctx, next) => {
|
|
70
|
-
|
|
71
|
-
if (!isProtectedPath(ctx.path)) {
|
|
54
|
+
if (!isMcpEndpoint(ctx.path)) {
|
|
72
55
|
return next();
|
|
73
56
|
}
|
|
57
|
+
strapi.log.debug(`[${PLUGIN_ID}] Protecting MCP endpoint: ${ctx.path}`);
|
|
74
58
|
const authHeader = ctx.request.headers.authorization;
|
|
75
59
|
const token = extractBearerToken(authHeader);
|
|
76
60
|
if (!token) {
|
package/dist/server/index.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
2
|
const PLUGIN_ID = "strapi-oauth-mcp-manager";
|
|
3
3
|
const PLUGIN_UID$3 = `plugin::${PLUGIN_ID}`;
|
|
4
|
+
const MCP_ENDPOINT_PATTERN = /^\/api\/[^/]+\/mcp(\/.*)?$/;
|
|
4
5
|
function extractBearerToken(authHeader) {
|
|
5
6
|
if (!authHeader?.startsWith("Bearer ")) {
|
|
6
7
|
return null;
|
|
@@ -44,32 +45,15 @@ async function validateOAuthToken(token, strapi) {
|
|
|
44
45
|
return { valid: false, error: "Token validation failed" };
|
|
45
46
|
}
|
|
46
47
|
}
|
|
48
|
+
function isMcpEndpoint(path) {
|
|
49
|
+
return MCP_ENDPOINT_PATTERN.test(path);
|
|
50
|
+
}
|
|
47
51
|
const mcpOauthMiddleware = (config2, { strapi }) => {
|
|
48
|
-
let endpointCache = [];
|
|
49
|
-
let lastCacheUpdate = 0;
|
|
50
|
-
const CACHE_TTL = 6e4;
|
|
51
|
-
async function refreshEndpointCache() {
|
|
52
|
-
const now = Date.now();
|
|
53
|
-
if (now - lastCacheUpdate > CACHE_TTL) {
|
|
54
|
-
try {
|
|
55
|
-
const endpoints = await strapi.documents(`${PLUGIN_UID$3}.mcp-endpoint`).findMany({
|
|
56
|
-
filters: { active: true }
|
|
57
|
-
});
|
|
58
|
-
endpointCache = endpoints.map((e) => e.path);
|
|
59
|
-
lastCacheUpdate = now;
|
|
60
|
-
} catch (error) {
|
|
61
|
-
strapi.log.error(`[${PLUGIN_ID}] Error refreshing endpoint cache`, { error });
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
function isProtectedPath(path) {
|
|
66
|
-
return endpointCache.some((endpoint) => path.includes(endpoint));
|
|
67
|
-
}
|
|
68
52
|
return async (ctx, next) => {
|
|
69
|
-
|
|
70
|
-
if (!isProtectedPath(ctx.path)) {
|
|
53
|
+
if (!isMcpEndpoint(ctx.path)) {
|
|
71
54
|
return next();
|
|
72
55
|
}
|
|
56
|
+
strapi.log.debug(`[${PLUGIN_ID}] Protecting MCP endpoint: ${ctx.path}`);
|
|
73
57
|
const authHeader = ctx.request.headers.authorization;
|
|
74
58
|
const token = extractBearerToken(authHeader);
|
|
75
59
|
if (!token) {
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* MCP OAuth Authentication Middleware
|
|
3
3
|
*
|
|
4
|
-
* This middleware provides OAuth 2.0 authentication for all
|
|
4
|
+
* This middleware provides OAuth 2.0 authentication for all MCP endpoints.
|
|
5
|
+
* Uses convention-based protection: any route matching /api/{plugin}/mcp is protected.
|
|
6
|
+
*
|
|
5
7
|
* It supports dual authentication:
|
|
6
8
|
* - OAuth 2.0 tokens (for ChatGPT and other OAuth clients)
|
|
7
9
|
* - Direct Strapi API tokens (for Claude Desktop and scripts)
|
package/package.json
CHANGED