mcp-use 2.0.0-beta.35 → 2.0.0-beta.37
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/bin.js +1 -3
- package/dist/chunk-5Z6KY5ON.js +2 -0
- package/dist/{chunk-PPQL2WHL.js → chunk-GX6PGIEO.js} +32 -142
- package/dist/chunk-JMDHTN6N.js +1 -0
- package/dist/chunk-OJF6B5EU.js +1 -0
- package/dist/chunk-PSKSL567.js +1 -0
- package/dist/chunk-WYBJYZJB.js +1 -0
- package/dist/chunk-Y26DNVWA.js +3 -0
- package/dist/chunk-ZMU5USAZ.js +1 -0
- package/dist/index.js +9 -3100
- package/dist/internal/resource-completion.js +1 -6
- package/dist/internal/usage.js +1 -0
- package/dist/landing-handler-TG6LVQBI.js +1 -0
- package/dist/landing.js +1 -6
- package/dist/mcp-proxy-NETCUGUD.js +2 -0
- package/dist/mcp-proxy.d.ts +6 -0
- package/dist/next/index.js +1 -171
- package/dist/node-bridge.js +1 -150
- package/dist/oauth/auth0.js +1 -60
- package/dist/oauth/better-auth.js +1 -75
- package/dist/oauth/clerk.js +1 -69
- package/dist/oauth/index.js +1 -26
- package/dist/oauth/keycloak.js +1 -93
- package/dist/oauth/supabase.js +1 -100
- package/dist/oauth/workos.js +1 -76
- package/dist/openapi/index.js +1 -6
- package/dist/public-route-DAQJGS6K.js +1 -0
- package/dist/react/components/model-context.d.ts +9 -14
- package/dist/react/hooks/use-view-state.d.ts +23 -0
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.js +215 -81
- package/dist/react/runtime/model-context-store.d.ts +34 -68
- package/dist/react/runtime/view-runtime.d.ts +2 -2
- package/dist/usage.d.ts +11 -0
- package/package.json +6 -5
- package/dist/chunk-DFFP6QPR.js +0 -37
- package/dist/chunk-HDV7K2TE.js +0 -97
- package/dist/chunk-HNNQJLD5.js +0 -383
- package/dist/chunk-QCIREWRS.js +0 -64
- package/dist/chunk-QKJJ6DWK.js +0 -184
- package/dist/chunk-YF5X7FYF.js +0 -517
- package/dist/landing-handler-QEH2ATIR.js +0 -36
- package/dist/mcp-proxy-JK7IC5CR.js +0 -281
- package/dist/public-route-BAMZRI4R.js +0 -8
package/dist/chunk-QKJJ6DWK.js
DELETED
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getOAuthProviderOptions,
|
|
3
|
-
wrapOAuthTokenVerifier
|
|
4
|
-
} from "./chunk-YF5X7FYF.js";
|
|
5
|
-
|
|
6
|
-
// src/oauth/index.ts
|
|
7
|
-
import {
|
|
8
|
-
OAuthError,
|
|
9
|
-
OAuthErrorCode,
|
|
10
|
-
bearerAuthChallengeResponse,
|
|
11
|
-
getOAuthProtectedResourceMetadataUrl as getOAuthProtectedResourceMetadataUrl2,
|
|
12
|
-
oauthMetadataResponse as oauthMetadataResponse2,
|
|
13
|
-
requireBearerAuth as requireBearerAuth2,
|
|
14
|
-
verifyBearerToken
|
|
15
|
-
} from "@modelcontextprotocol/server";
|
|
16
|
-
|
|
17
|
-
// src/oauth/adapters.ts
|
|
18
|
-
import {
|
|
19
|
-
getOAuthProtectedResourceMetadataUrl,
|
|
20
|
-
oauthMetadataResponse,
|
|
21
|
-
requireBearerAuth
|
|
22
|
-
} from "@modelcontextprotocol/server";
|
|
23
|
-
|
|
24
|
-
// src/fetch-app.ts
|
|
25
|
-
import {
|
|
26
|
-
hostHeaderValidationResponse,
|
|
27
|
-
originValidationResponse
|
|
28
|
-
} from "@modelcontextprotocol/server";
|
|
29
|
-
var requestBags = /* @__PURE__ */ new WeakMap();
|
|
30
|
-
function getRequestBag(request) {
|
|
31
|
-
let bag = requestBags.get(request);
|
|
32
|
-
if (bag === void 0) {
|
|
33
|
-
bag = {};
|
|
34
|
-
requestBags.set(request, bag);
|
|
35
|
-
}
|
|
36
|
-
return bag;
|
|
37
|
-
}
|
|
38
|
-
function composeFetch(terminal, ...middlewares) {
|
|
39
|
-
return async (request) => {
|
|
40
|
-
let index = 0;
|
|
41
|
-
const dispatch = async () => {
|
|
42
|
-
if (index < middlewares.length) {
|
|
43
|
-
const middleware = middlewares[index];
|
|
44
|
-
index += 1;
|
|
45
|
-
return middleware(request, dispatch);
|
|
46
|
-
}
|
|
47
|
-
return terminal(request);
|
|
48
|
-
};
|
|
49
|
-
return dispatch();
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
function pathnameOf(request) {
|
|
53
|
-
return new URL(request.url).pathname;
|
|
54
|
-
}
|
|
55
|
-
function matchesPath(request, path) {
|
|
56
|
-
return pathnameOf(request) === path;
|
|
57
|
-
}
|
|
58
|
-
function matchesPathPrefix(request, prefix) {
|
|
59
|
-
const pathname = pathnameOf(request);
|
|
60
|
-
return pathname === prefix || pathname.startsWith(`${prefix}/`);
|
|
61
|
-
}
|
|
62
|
-
function pathUnderBase(basePath, childPath) {
|
|
63
|
-
const child = childPath.replace(/^\/+/, "");
|
|
64
|
-
return basePath === "/" ? `/${child}` : `${basePath}/${child}`;
|
|
65
|
-
}
|
|
66
|
-
function isHtmlNavigationRequest(request) {
|
|
67
|
-
if (request.method !== "GET" && request.method !== "HEAD") {
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
const accept = request.headers.get("accept");
|
|
71
|
-
if (accept === null) {
|
|
72
|
-
return false;
|
|
73
|
-
}
|
|
74
|
-
return accept.split(",").some((range) => {
|
|
75
|
-
const [mediaType, ...parameters] = range.split(";");
|
|
76
|
-
if (mediaType?.trim().toLowerCase() !== "text/html") {
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
79
|
-
return !parameters.some((parameter) => {
|
|
80
|
-
const [name, value] = parameter.trim().toLowerCase().split("=");
|
|
81
|
-
return name === "q" && Number(value) === 0;
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
function jsonBodyMiddleware() {
|
|
86
|
-
return async (request, next) => {
|
|
87
|
-
const bag = getRequestBag(request);
|
|
88
|
-
if (bag.parsedBody !== void 0) {
|
|
89
|
-
return next();
|
|
90
|
-
}
|
|
91
|
-
if (!(request.headers.get("content-type") ?? "").includes("application/json")) {
|
|
92
|
-
return next();
|
|
93
|
-
}
|
|
94
|
-
try {
|
|
95
|
-
bag.parsedBody = await request.clone().json();
|
|
96
|
-
} catch {
|
|
97
|
-
return new Response("Invalid JSON", { status: 400 });
|
|
98
|
-
}
|
|
99
|
-
return next();
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
function hostValidationMiddleware(hosts) {
|
|
103
|
-
return async (request, next) => {
|
|
104
|
-
const rejected = hostHeaderValidationResponse(request, hosts);
|
|
105
|
-
return rejected ?? next();
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
function originValidationMiddleware(origins, skipSafeMethods = true) {
|
|
109
|
-
return async (request, next) => {
|
|
110
|
-
if (skipSafeMethods && (request.method === "GET" || request.method === "HEAD")) {
|
|
111
|
-
return next();
|
|
112
|
-
}
|
|
113
|
-
const rejected = originValidationResponse(request, origins);
|
|
114
|
-
return rejected ?? next();
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// src/oauth/adapters.ts
|
|
119
|
-
function bearerAuth(provider, resource) {
|
|
120
|
-
const options = getOAuthProviderOptions(provider);
|
|
121
|
-
const gate = requireBearerAuth({
|
|
122
|
-
verifier: wrapOAuthTokenVerifier(provider, resource),
|
|
123
|
-
resourceMetadataUrl: getOAuthProtectedResourceMetadataUrl(resource),
|
|
124
|
-
...options.requiredScopes !== void 0 && {
|
|
125
|
-
requiredScopes: [...options.requiredScopes]
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
return async (request, next) => {
|
|
129
|
-
const result = await gate(request);
|
|
130
|
-
if (result instanceof Response) {
|
|
131
|
-
return result;
|
|
132
|
-
}
|
|
133
|
-
getRequestBag(request).authInfo = result;
|
|
134
|
-
return next();
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
function oauthMetadata(provider, resource) {
|
|
138
|
-
const options = getOAuthProviderOptions(provider);
|
|
139
|
-
return async (request, next) => {
|
|
140
|
-
const response = oauthMetadataResponse(request, {
|
|
141
|
-
oauthMetadata: options.oauthMetadata,
|
|
142
|
-
resourceServerUrl: resource,
|
|
143
|
-
...options.scopesSupported !== void 0 && {
|
|
144
|
-
scopesSupported: [...options.scopesSupported]
|
|
145
|
-
},
|
|
146
|
-
...options.resourceName !== void 0 && {
|
|
147
|
-
resourceName: options.resourceName
|
|
148
|
-
},
|
|
149
|
-
...options.serviceDocumentationUrl !== void 0 && {
|
|
150
|
-
serviceDocumentationUrl: options.serviceDocumentationUrl
|
|
151
|
-
}
|
|
152
|
-
});
|
|
153
|
-
if (response !== void 0) {
|
|
154
|
-
return response;
|
|
155
|
-
}
|
|
156
|
-
return next();
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
function authInfoFromRequest(request) {
|
|
160
|
-
return getRequestBag(request).authInfo;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
export {
|
|
164
|
-
getRequestBag,
|
|
165
|
-
composeFetch,
|
|
166
|
-
pathnameOf,
|
|
167
|
-
matchesPath,
|
|
168
|
-
matchesPathPrefix,
|
|
169
|
-
pathUnderBase,
|
|
170
|
-
isHtmlNavigationRequest,
|
|
171
|
-
jsonBodyMiddleware,
|
|
172
|
-
hostValidationMiddleware,
|
|
173
|
-
originValidationMiddleware,
|
|
174
|
-
bearerAuth,
|
|
175
|
-
oauthMetadata,
|
|
176
|
-
authInfoFromRequest,
|
|
177
|
-
OAuthError,
|
|
178
|
-
OAuthErrorCode,
|
|
179
|
-
bearerAuthChallengeResponse,
|
|
180
|
-
getOAuthProtectedResourceMetadataUrl2 as getOAuthProtectedResourceMetadataUrl,
|
|
181
|
-
oauthMetadataResponse2 as oauthMetadataResponse,
|
|
182
|
-
requireBearerAuth2 as requireBearerAuth,
|
|
183
|
-
verifyBearerToken
|
|
184
|
-
};
|
package/dist/chunk-YF5X7FYF.js
DELETED
|
@@ -1,517 +0,0 @@
|
|
|
1
|
-
// src/oauth/guards.ts
|
|
2
|
-
function isRecord(value) {
|
|
3
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
4
|
-
}
|
|
5
|
-
function isLocalhost(url) {
|
|
6
|
-
const hostname = url.hostname.toLowerCase();
|
|
7
|
-
return hostname === "localhost" || hostname.endsWith(".localhost") || hostname === "[::1]" || /^127(?:\.\d{1,3}){3}$/.test(hostname);
|
|
8
|
-
}
|
|
9
|
-
function parseAbsoluteUrl(value, name) {
|
|
10
|
-
let url;
|
|
11
|
-
try {
|
|
12
|
-
url = new URL(value);
|
|
13
|
-
} catch {
|
|
14
|
-
throw new Error(`${name} must be an absolute URL`);
|
|
15
|
-
}
|
|
16
|
-
if (url.origin === "null" || url.username !== "" || url.password !== "") {
|
|
17
|
-
throw new Error(`${name} must be an absolute URL without credentials`);
|
|
18
|
-
}
|
|
19
|
-
return url;
|
|
20
|
-
}
|
|
21
|
-
function assertSecureHttpUrl(url, name) {
|
|
22
|
-
if (url.protocol === "https:") {
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
if (url.protocol === "http:" && isLocalhost(url)) {
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
throw new Error(`${name} must use HTTPS, or HTTP for localhost`);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// src/oauth/jwt.ts
|
|
32
|
-
import {
|
|
33
|
-
OAuthError,
|
|
34
|
-
OAuthErrorCode
|
|
35
|
-
} from "@modelcontextprotocol/server";
|
|
36
|
-
import {
|
|
37
|
-
createRemoteJWKSet,
|
|
38
|
-
jwtVerify,
|
|
39
|
-
errors
|
|
40
|
-
} from "jose";
|
|
41
|
-
function createJwtVerifier(options) {
|
|
42
|
-
const key = options.key ?? createRemoteJWKSet(options.jwksUrl);
|
|
43
|
-
const configuredResource = canonicalUrl(options.resource);
|
|
44
|
-
return {
|
|
45
|
-
async verifyAccessToken(token) {
|
|
46
|
-
try {
|
|
47
|
-
const { payload } = await jwtVerify(token, key, {
|
|
48
|
-
issuer: options.issuer,
|
|
49
|
-
...options.algorithms !== void 0 && {
|
|
50
|
-
algorithms: [...options.algorithms]
|
|
51
|
-
},
|
|
52
|
-
requiredClaims: ["exp", "sub"]
|
|
53
|
-
});
|
|
54
|
-
const claims = { ...payload };
|
|
55
|
-
const clientId = requiredString(claims, "client_id") ?? requiredString(claims, "azp");
|
|
56
|
-
const expiresAt = requiredFutureNumber(claims, "exp");
|
|
57
|
-
const resource = verifiedResource(claims, configuredResource);
|
|
58
|
-
return {
|
|
59
|
-
token,
|
|
60
|
-
clientId: clientId ?? "",
|
|
61
|
-
scopes: normalizedStrings(claims["scope"]),
|
|
62
|
-
expiresAt,
|
|
63
|
-
extra: { payload: claims },
|
|
64
|
-
resource
|
|
65
|
-
};
|
|
66
|
-
} catch (error) {
|
|
67
|
-
if (error instanceof OAuthError) {
|
|
68
|
-
throw error;
|
|
69
|
-
}
|
|
70
|
-
if (isCredentialFailure(error)) {
|
|
71
|
-
if (process.env["MCP_USE_OAUTH_DEBUG"]) {
|
|
72
|
-
logJwtFailure(token, error);
|
|
73
|
-
}
|
|
74
|
-
throw invalidToken("JWT verification failed", error);
|
|
75
|
-
}
|
|
76
|
-
throw error;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
function normalizedProviderUrl(value, name) {
|
|
82
|
-
const raw = typeof value === "string" && !value.includes("://") ? `https://${value}` : value;
|
|
83
|
-
let url;
|
|
84
|
-
try {
|
|
85
|
-
url = new URL(raw);
|
|
86
|
-
} catch {
|
|
87
|
-
throw new TypeError(`${name} must be an absolute HTTP(S) URL`);
|
|
88
|
-
}
|
|
89
|
-
if (!isAllowedHttpUrl(url)) {
|
|
90
|
-
throw new TypeError(
|
|
91
|
-
`${name} must use HTTPS, or HTTP for localhost, without credentials, query, or fragment`
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
url.pathname = url.pathname.replace(/\/+$/, "") || "/";
|
|
95
|
-
return url;
|
|
96
|
-
}
|
|
97
|
-
function providerEndpoint(issuer, path) {
|
|
98
|
-
const base = new URL(issuer);
|
|
99
|
-
base.pathname = `${base.pathname.replace(/\/+$/, "")}/`;
|
|
100
|
-
return new URL(path.replace(/^\/+/, ""), base).href;
|
|
101
|
-
}
|
|
102
|
-
function payloadFromAuthInfo(authInfo) {
|
|
103
|
-
const payload = authInfo.extra?.["payload"];
|
|
104
|
-
if (!isRecord(payload)) {
|
|
105
|
-
throw invalidToken("Verified token payload is missing");
|
|
106
|
-
}
|
|
107
|
-
return payload;
|
|
108
|
-
}
|
|
109
|
-
function requiredString(claims, name) {
|
|
110
|
-
const value = claims[name];
|
|
111
|
-
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
112
|
-
}
|
|
113
|
-
function stringValue(claims, name) {
|
|
114
|
-
const value = claims[name];
|
|
115
|
-
return typeof value === "string" ? value : void 0;
|
|
116
|
-
}
|
|
117
|
-
function booleanValue(claims, name) {
|
|
118
|
-
const value = claims[name];
|
|
119
|
-
return typeof value === "boolean" ? value : void 0;
|
|
120
|
-
}
|
|
121
|
-
function numberValue(claims, name) {
|
|
122
|
-
const value = claims[name];
|
|
123
|
-
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
124
|
-
}
|
|
125
|
-
function recordValue(claims, name) {
|
|
126
|
-
const value = claims[name];
|
|
127
|
-
return isRecord(value) ? value : void 0;
|
|
128
|
-
}
|
|
129
|
-
function normalizedStrings(value) {
|
|
130
|
-
if (typeof value === "string") {
|
|
131
|
-
return value.split(/\s+/).filter(Boolean);
|
|
132
|
-
}
|
|
133
|
-
if (!Array.isArray(value)) {
|
|
134
|
-
return [];
|
|
135
|
-
}
|
|
136
|
-
return value.filter(
|
|
137
|
-
(item) => typeof item === "string" && item.length > 0
|
|
138
|
-
);
|
|
139
|
-
}
|
|
140
|
-
function requiredFutureNumber(claims, name) {
|
|
141
|
-
const value = numberValue(claims, name);
|
|
142
|
-
if (value === void 0 || value <= Date.now() / 1e3) {
|
|
143
|
-
throw invalidToken(`Missing or expired ${name} claim`);
|
|
144
|
-
}
|
|
145
|
-
return value;
|
|
146
|
-
}
|
|
147
|
-
function invalidToken(message, cause) {
|
|
148
|
-
const error = new OAuthError(OAuthErrorCode.InvalidToken, message);
|
|
149
|
-
if (cause !== void 0) {
|
|
150
|
-
error.cause = cause;
|
|
151
|
-
}
|
|
152
|
-
return error;
|
|
153
|
-
}
|
|
154
|
-
function verifiedResource(claims, configuredResource) {
|
|
155
|
-
const value = claims["resource"];
|
|
156
|
-
if (value !== void 0) {
|
|
157
|
-
if (typeof value !== "string") {
|
|
158
|
-
throw invalidToken("Token resource claim must be an absolute URL");
|
|
159
|
-
}
|
|
160
|
-
const resource = canonicalUrl(value);
|
|
161
|
-
if (resource.href !== configuredResource.href) {
|
|
162
|
-
throw invalidToken(
|
|
163
|
-
"Token resource claim does not match protected resource"
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
return configuredResource;
|
|
167
|
-
}
|
|
168
|
-
const audience = claims["aud"];
|
|
169
|
-
if (!validAudience(audience)) {
|
|
170
|
-
throw invalidToken("Token audience claim must be a string or string array");
|
|
171
|
-
}
|
|
172
|
-
const audiences = typeof audience === "string" ? [audience] : audience;
|
|
173
|
-
if (!audiences.includes(configuredResource.href)) {
|
|
174
|
-
throw invalidToken(
|
|
175
|
-
"Token audience does not include the protected resource"
|
|
176
|
-
);
|
|
177
|
-
}
|
|
178
|
-
return configuredResource;
|
|
179
|
-
}
|
|
180
|
-
function validAudience(value) {
|
|
181
|
-
return typeof value === "string" || Array.isArray(value) && value.length > 0 && value.every((item) => typeof item === "string");
|
|
182
|
-
}
|
|
183
|
-
function canonicalUrl(value) {
|
|
184
|
-
try {
|
|
185
|
-
const url = new URL(value);
|
|
186
|
-
if (!isAllowedHttpUrl(url)) {
|
|
187
|
-
throw new TypeError();
|
|
188
|
-
}
|
|
189
|
-
url.pathname = url.pathname === "/" ? "/" : url.pathname.replace(/\/+$/, "");
|
|
190
|
-
return url;
|
|
191
|
-
} catch {
|
|
192
|
-
throw invalidToken(
|
|
193
|
-
"Token resource claim must be an absolute HTTPS URL, or HTTP URL for localhost"
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
function isAllowedHttpUrl(url) {
|
|
198
|
-
return /^https?:$/.test(url.protocol) && !url.username && !url.password && !url.search && !url.hash && (url.protocol !== "http:" || isLocalhost(url));
|
|
199
|
-
}
|
|
200
|
-
function isCredentialFailure(error) {
|
|
201
|
-
return error instanceof errors.JWTClaimValidationFailed || error instanceof errors.JWTExpired || error instanceof errors.JOSEAlgNotAllowed || error instanceof errors.JOSENotSupported || error instanceof errors.JWSInvalid || error instanceof errors.JWTInvalid || error instanceof errors.JWSSignatureVerificationFailed || error instanceof errors.JWKSNoMatchingKey;
|
|
202
|
-
}
|
|
203
|
-
var JWT_DEBUG_CLAIMS = [
|
|
204
|
-
"iss",
|
|
205
|
-
"aud",
|
|
206
|
-
"azp",
|
|
207
|
-
"sub",
|
|
208
|
-
"exp",
|
|
209
|
-
"iat",
|
|
210
|
-
"nbf",
|
|
211
|
-
"client_id",
|
|
212
|
-
"scope",
|
|
213
|
-
"typ",
|
|
214
|
-
"jti"
|
|
215
|
-
];
|
|
216
|
-
function debugErrorText(error) {
|
|
217
|
-
return error instanceof Error ? error.message : String(error);
|
|
218
|
-
}
|
|
219
|
-
function logJwtFailure(token, error) {
|
|
220
|
-
try {
|
|
221
|
-
let decodeSegment2 = function(segment, label) {
|
|
222
|
-
try {
|
|
223
|
-
return Buffer.from(segment, "base64url").toString("utf8");
|
|
224
|
-
} catch (decodeError) {
|
|
225
|
-
console.error(
|
|
226
|
-
`[oauth-debug] ${label} decode failed: ${debugErrorText(decodeError)}`
|
|
227
|
-
);
|
|
228
|
-
return void 0;
|
|
229
|
-
}
|
|
230
|
-
};
|
|
231
|
-
var decodeSegment = decodeSegment2;
|
|
232
|
-
const name = error instanceof Error ? error.constructor.name : typeof error;
|
|
233
|
-
console.error(
|
|
234
|
-
`[oauth-debug] JWT verify failed: ${name}: ${debugErrorText(error)}`
|
|
235
|
-
);
|
|
236
|
-
if (error instanceof errors.JWTClaimValidationFailed) {
|
|
237
|
-
console.error(
|
|
238
|
-
`[oauth-debug] claim=${error.claim} reason=${error.reason}`
|
|
239
|
-
);
|
|
240
|
-
}
|
|
241
|
-
const segments = token.split(".");
|
|
242
|
-
console.error(`[oauth-debug] token segments: ${segments.length}`);
|
|
243
|
-
if (segments.length !== 3) {
|
|
244
|
-
const preview = token.length > 12 ? `${token.slice(0, 12)}\u2026` : token;
|
|
245
|
-
console.error(
|
|
246
|
-
`[oauth-debug] non-JWT token preview=${preview} length=${token.length}`
|
|
247
|
-
);
|
|
248
|
-
return;
|
|
249
|
-
}
|
|
250
|
-
const headerJson = decodeSegment2(segments[0], "header");
|
|
251
|
-
if (headerJson !== void 0) {
|
|
252
|
-
console.error(`[oauth-debug] header=${headerJson}`);
|
|
253
|
-
}
|
|
254
|
-
const payloadJson = decodeSegment2(segments[1], "payload");
|
|
255
|
-
if (payloadJson === void 0) {
|
|
256
|
-
return;
|
|
257
|
-
}
|
|
258
|
-
try {
|
|
259
|
-
const payload = JSON.parse(payloadJson);
|
|
260
|
-
const selected = {};
|
|
261
|
-
for (const claim of JWT_DEBUG_CLAIMS) {
|
|
262
|
-
if (claim in payload) {
|
|
263
|
-
selected[claim] = payload[claim];
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
selected["now"] = Math.floor(Date.now() / 1e3);
|
|
267
|
-
console.error(`[oauth-debug] payload=${JSON.stringify(selected)}`);
|
|
268
|
-
} catch (decodeError) {
|
|
269
|
-
console.error(
|
|
270
|
-
`[oauth-debug] payload decode failed: ${debugErrorText(decodeError)}`
|
|
271
|
-
);
|
|
272
|
-
}
|
|
273
|
-
} catch {
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
// src/oauth/internal.ts
|
|
278
|
-
function resolveConfiguredOAuthResource(options) {
|
|
279
|
-
const provider = options.provider;
|
|
280
|
-
const basePath = normalizeBasePath(options.basePath);
|
|
281
|
-
if (provider.resource !== void 0) {
|
|
282
|
-
return validateOAuthResource(provider.resource, basePath);
|
|
283
|
-
}
|
|
284
|
-
if (options.mcpUrl !== void 0) {
|
|
285
|
-
return validateOAuthResource(
|
|
286
|
-
appendBasePath(
|
|
287
|
-
requireAbsoluteOrigin(options.mcpUrl, "MCP_URL"),
|
|
288
|
-
basePath
|
|
289
|
-
),
|
|
290
|
-
basePath
|
|
291
|
-
);
|
|
292
|
-
}
|
|
293
|
-
return void 0;
|
|
294
|
-
}
|
|
295
|
-
function resolveLocalOAuthResource(listenOrigin, basePath) {
|
|
296
|
-
const listenOriginUrl = requireAbsoluteOrigin(listenOrigin, "listen origin");
|
|
297
|
-
if (!isLocalhost(listenOriginUrl)) {
|
|
298
|
-
throw new Error(
|
|
299
|
-
"OAuth listen origin must be localhost or a loopback address"
|
|
300
|
-
);
|
|
301
|
-
}
|
|
302
|
-
return validateOAuthResource(
|
|
303
|
-
appendBasePath(listenOriginUrl, normalizeBasePath(basePath)),
|
|
304
|
-
basePath
|
|
305
|
-
);
|
|
306
|
-
}
|
|
307
|
-
function validateOAuthResource(resource, basePath) {
|
|
308
|
-
const normalizedBasePath = normalizeBasePath(basePath);
|
|
309
|
-
const url = parseAbsoluteUrl(resource, "OAuth resource");
|
|
310
|
-
if (url.search !== "" || url.hash !== "") {
|
|
311
|
-
throw new Error(
|
|
312
|
-
"OAuth resource must not include a query string or fragment"
|
|
313
|
-
);
|
|
314
|
-
}
|
|
315
|
-
assertSecureHttpUrl(url, "OAuth resource");
|
|
316
|
-
if (normalizePathname(url.pathname) !== normalizedBasePath) {
|
|
317
|
-
throw new Error(
|
|
318
|
-
`OAuth resource path must exactly match basePath (${normalizedBasePath})`
|
|
319
|
-
);
|
|
320
|
-
}
|
|
321
|
-
url.pathname = normalizedBasePath;
|
|
322
|
-
return url;
|
|
323
|
-
}
|
|
324
|
-
function wrapOAuthTokenVerifier(provider, expectedResource) {
|
|
325
|
-
const canonicalResource = normalizeResourceUrl(expectedResource);
|
|
326
|
-
const tokenVerifier = provider.createTokenVerifier(
|
|
327
|
-
new URL(canonicalResource.href)
|
|
328
|
-
);
|
|
329
|
-
if (tokenVerifier === null || typeof tokenVerifier !== "object" || typeof tokenVerifier.verifyAccessToken !== "function") {
|
|
330
|
-
throw new TypeError(
|
|
331
|
-
"OAuth provider createTokenVerifier must return an OAuthTokenVerifier"
|
|
332
|
-
);
|
|
333
|
-
}
|
|
334
|
-
return {
|
|
335
|
-
async verifyAccessToken(token) {
|
|
336
|
-
const authInfo = await tokenVerifier.verifyAccessToken(token);
|
|
337
|
-
assertVerifiedAuthInfo(authInfo);
|
|
338
|
-
assertResourceBinding(authInfo, canonicalResource);
|
|
339
|
-
let mapped;
|
|
340
|
-
try {
|
|
341
|
-
mapped = provider.mapAuthInfo(authInfo);
|
|
342
|
-
} catch (error) {
|
|
343
|
-
throw invalidToken("Token identity mapping failed", error);
|
|
344
|
-
}
|
|
345
|
-
assertMappedExtra(mapped);
|
|
346
|
-
return {
|
|
347
|
-
...authInfo,
|
|
348
|
-
scopes: [...authInfo.scopes],
|
|
349
|
-
extra: { ...authInfo.extra, ...mapped }
|
|
350
|
-
};
|
|
351
|
-
}
|
|
352
|
-
};
|
|
353
|
-
}
|
|
354
|
-
function assertResourceBinding(authInfo, expectedResource) {
|
|
355
|
-
if (authInfo.resource === void 0) {
|
|
356
|
-
throw invalidToken(
|
|
357
|
-
"Token verifier did not return a validated protected resource"
|
|
358
|
-
);
|
|
359
|
-
}
|
|
360
|
-
const resource = parseTokenResource(authInfo.resource);
|
|
361
|
-
if (resource.href !== normalizeResourceUrl(expectedResource).href) {
|
|
362
|
-
throw invalidToken("Token resource does not match the protected resource");
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
function parseTokenResource(value) {
|
|
366
|
-
if (!(value instanceof URL)) {
|
|
367
|
-
throw invalidToken(
|
|
368
|
-
"Token resource must be an absolute HTTPS URL, or HTTP URL for localhost"
|
|
369
|
-
);
|
|
370
|
-
}
|
|
371
|
-
const resource = value;
|
|
372
|
-
if (!/^https?:$/.test(resource.protocol) || resource.username !== "" || resource.password !== "" || resource.search !== "" || resource.hash !== "" || resource.protocol === "http:" && !isLocalhost(resource)) {
|
|
373
|
-
throw invalidToken(
|
|
374
|
-
"Token resource must be an absolute HTTPS URL, or HTTP URL for localhost"
|
|
375
|
-
);
|
|
376
|
-
}
|
|
377
|
-
return normalizeResourceUrl(resource);
|
|
378
|
-
}
|
|
379
|
-
function normalizeResourceUrl(resource) {
|
|
380
|
-
const normalized = new URL(resource);
|
|
381
|
-
normalized.pathname = normalized.pathname === "/" ? "/" : normalized.pathname.replace(/\/+$/, "");
|
|
382
|
-
return normalized;
|
|
383
|
-
}
|
|
384
|
-
function getOAuthProviderOptions(provider) {
|
|
385
|
-
return {
|
|
386
|
-
oauthMetadata: provider.oauthMetadata,
|
|
387
|
-
...provider.requiredScopes !== void 0 && {
|
|
388
|
-
requiredScopes: [...provider.requiredScopes]
|
|
389
|
-
},
|
|
390
|
-
...provider.scopesSupported !== void 0 && {
|
|
391
|
-
scopesSupported: [...provider.scopesSupported]
|
|
392
|
-
},
|
|
393
|
-
...provider.resourceName !== void 0 && {
|
|
394
|
-
resourceName: provider.resourceName
|
|
395
|
-
},
|
|
396
|
-
...provider.serviceDocumentationUrl !== void 0 && {
|
|
397
|
-
serviceDocumentationUrl: provider.serviceDocumentationUrl
|
|
398
|
-
}
|
|
399
|
-
};
|
|
400
|
-
}
|
|
401
|
-
function assertVerifiedAuthInfo(authInfo) {
|
|
402
|
-
if (authInfo === null || typeof authInfo !== "object" || typeof authInfo.token !== "string" || authInfo.token.length === 0 || typeof authInfo.clientId !== "string" || !Array.isArray(authInfo.scopes) || !authInfo.scopes.every((scope) => typeof scope === "string") || typeof authInfo.expiresAt !== "number" || !Number.isFinite(authInfo.expiresAt) || authInfo.expiresAt <= Date.now() / 1e3) {
|
|
403
|
-
throw invalidToken(
|
|
404
|
-
"Token verifier returned invalid authentication information"
|
|
405
|
-
);
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
function assertMappedExtra(mapped) {
|
|
409
|
-
if (mapped === null || typeof mapped !== "object" || !("user" in mapped) || mapped.user === void 0 || !isRecord(mapped.payload) || !Array.isArray(mapped.permissions) || !mapped.permissions.every((permission) => typeof permission === "string")) {
|
|
410
|
-
throw invalidToken(
|
|
411
|
-
"Token identity mapping must return user, payload, and string permissions"
|
|
412
|
-
);
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
function requireAbsoluteOrigin(value, name) {
|
|
416
|
-
const url = parseAbsoluteUrl(value, name);
|
|
417
|
-
if (url.pathname !== "/" || url.search !== "" || url.hash !== "" || url.username !== "" || url.password !== "") {
|
|
418
|
-
throw new Error(`${name} must be an absolute origin without a path`);
|
|
419
|
-
}
|
|
420
|
-
return url;
|
|
421
|
-
}
|
|
422
|
-
function appendBasePath(origin, basePath) {
|
|
423
|
-
const resource = new URL(origin.origin);
|
|
424
|
-
resource.pathname = basePath;
|
|
425
|
-
return resource;
|
|
426
|
-
}
|
|
427
|
-
function normalizeBasePath(basePath) {
|
|
428
|
-
if (!basePath.startsWith("/") || basePath.includes("?") || basePath.includes("#")) {
|
|
429
|
-
throw new Error("basePath must be an absolute URL pathname");
|
|
430
|
-
}
|
|
431
|
-
return normalizePathname(basePath);
|
|
432
|
-
}
|
|
433
|
-
function normalizePathname(pathname) {
|
|
434
|
-
return pathname === "/" ? "/" : pathname.replace(/\/+$/, "");
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
// src/oauth/provider.ts
|
|
438
|
-
function oauthCustomProvider(options) {
|
|
439
|
-
if (options === null || typeof options !== "object" || typeof options.createTokenVerifier !== "function" || typeof options.mapAuthInfo !== "function") {
|
|
440
|
-
throw new TypeError(
|
|
441
|
-
"oauthCustomProvider requires createTokenVerifier, oauthMetadata, and mapAuthInfo"
|
|
442
|
-
);
|
|
443
|
-
}
|
|
444
|
-
assertOAuthMetadata(options.oauthMetadata);
|
|
445
|
-
if (options.resource !== void 0) {
|
|
446
|
-
assertResourceUrl(options.resource);
|
|
447
|
-
}
|
|
448
|
-
assertStringArray(options.requiredScopes, "requiredScopes");
|
|
449
|
-
assertStringArray(options.scopesSupported, "scopesSupported");
|
|
450
|
-
if (options.resourceName !== void 0 && (typeof options.resourceName !== "string" || options.resourceName.trim().length === 0)) {
|
|
451
|
-
throw new TypeError("resourceName must be a non-empty string");
|
|
452
|
-
}
|
|
453
|
-
if (options.serviceDocumentationUrl !== void 0) {
|
|
454
|
-
if (!(options.serviceDocumentationUrl instanceof URL)) {
|
|
455
|
-
throw new TypeError("serviceDocumentationUrl must be a URL");
|
|
456
|
-
}
|
|
457
|
-
assertSecureHttpUrl(
|
|
458
|
-
options.serviceDocumentationUrl,
|
|
459
|
-
"serviceDocumentationUrl"
|
|
460
|
-
);
|
|
461
|
-
}
|
|
462
|
-
const provider = {
|
|
463
|
-
createTokenVerifier: options.createTokenVerifier,
|
|
464
|
-
oauthMetadata: options.oauthMetadata,
|
|
465
|
-
mapAuthInfo: options.mapAuthInfo,
|
|
466
|
-
...options.resource !== void 0 && { resource: options.resource },
|
|
467
|
-
...options.requiredScopes !== void 0 && {
|
|
468
|
-
requiredScopes: [...options.requiredScopes]
|
|
469
|
-
},
|
|
470
|
-
...options.scopesSupported !== void 0 && {
|
|
471
|
-
scopesSupported: [...options.scopesSupported]
|
|
472
|
-
},
|
|
473
|
-
...options.resourceName !== void 0 && {
|
|
474
|
-
resourceName: options.resourceName
|
|
475
|
-
},
|
|
476
|
-
...options.serviceDocumentationUrl !== void 0 && {
|
|
477
|
-
serviceDocumentationUrl: options.serviceDocumentationUrl
|
|
478
|
-
}
|
|
479
|
-
};
|
|
480
|
-
return provider;
|
|
481
|
-
}
|
|
482
|
-
function assertOAuthMetadata(metadata) {
|
|
483
|
-
if (metadata === null || typeof metadata !== "object" || !("issuer" in metadata) || typeof metadata.issuer !== "string") {
|
|
484
|
-
throw new TypeError("oauthMetadata must include a string issuer");
|
|
485
|
-
}
|
|
486
|
-
assertSecureHttpUrl(
|
|
487
|
-
parseAbsoluteUrl(metadata.issuer, "oauthMetadata.issuer"),
|
|
488
|
-
"oauthMetadata.issuer"
|
|
489
|
-
);
|
|
490
|
-
}
|
|
491
|
-
function assertResourceUrl(resource) {
|
|
492
|
-
assertSecureHttpUrl(parseAbsoluteUrl(resource, "resource"), "resource");
|
|
493
|
-
}
|
|
494
|
-
function assertStringArray(value, name) {
|
|
495
|
-
if (value !== void 0 && (!Array.isArray(value) || !value.every((item) => typeof item === "string"))) {
|
|
496
|
-
throw new TypeError(`${name} must be an array of strings`);
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
export {
|
|
501
|
-
isRecord,
|
|
502
|
-
createJwtVerifier,
|
|
503
|
-
normalizedProviderUrl,
|
|
504
|
-
providerEndpoint,
|
|
505
|
-
payloadFromAuthInfo,
|
|
506
|
-
requiredString,
|
|
507
|
-
stringValue,
|
|
508
|
-
booleanValue,
|
|
509
|
-
recordValue,
|
|
510
|
-
normalizedStrings,
|
|
511
|
-
invalidToken,
|
|
512
|
-
resolveConfiguredOAuthResource,
|
|
513
|
-
resolveLocalOAuthResource,
|
|
514
|
-
wrapOAuthTokenVerifier,
|
|
515
|
-
getOAuthProviderOptions,
|
|
516
|
-
oauthCustomProvider
|
|
517
|
-
};
|