mcp-trellis 0.2.0 → 1.0.0
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/README.md +41 -561
- package/dist/adapters/node.d.ts +15 -6
- package/dist/adapters/node.d.ts.map +1 -1
- package/dist/adapters/node.js +56 -29
- package/dist/adapters/node.js.map +1 -1
- package/dist/adapters/origins.d.ts.map +1 -1
- package/dist/adapters/origins.js +1 -4
- package/dist/adapters/origins.js.map +1 -1
- package/dist/app.d.ts +27 -4
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +31 -18
- package/dist/app.js.map +1 -1
- package/dist/auth/bearer.d.ts.map +1 -1
- package/dist/auth/bearer.js +6 -1
- package/dist/auth/bearer.js.map +1 -1
- package/dist/body.d.ts +48 -0
- package/dist/body.d.ts.map +1 -0
- package/dist/body.js +145 -0
- package/dist/body.js.map +1 -0
- package/dist/clients.d.ts.map +1 -1
- package/dist/clients.js +2 -5
- package/dist/clients.js.map +1 -1
- package/dist/dispatch.d.ts +1 -1
- package/dist/dispatch.d.ts.map +1 -1
- package/dist/dispatch.js +58 -18
- package/dist/dispatch.js.map +1 -1
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js.map +1 -1
- package/dist/jsonrpc.d.ts +1 -0
- package/dist/jsonrpc.d.ts.map +1 -1
- package/dist/jsonrpc.js +1 -0
- package/dist/jsonrpc.js.map +1 -1
- package/dist/methods.d.ts +38 -15
- package/dist/methods.d.ts.map +1 -1
- package/dist/methods.js +38 -22
- package/dist/methods.js.map +1 -1
- package/dist/oauth/authorize.d.ts.map +1 -1
- package/dist/oauth/authorize.js +100 -68
- package/dist/oauth/authorize.js.map +1 -1
- package/dist/oauth/base64url.d.ts +1 -1
- package/dist/oauth/base64url.d.ts.map +1 -1
- package/dist/oauth/base64url.js +1 -4
- package/dist/oauth/base64url.js.map +1 -1
- package/dist/oauth/clientauth.d.ts +7 -2
- package/dist/oauth/clientauth.d.ts.map +1 -1
- package/dist/oauth/clientauth.js +59 -16
- package/dist/oauth/clientauth.js.map +1 -1
- package/dist/oauth/codes.d.ts +4 -4
- package/dist/oauth/codes.d.ts.map +1 -1
- package/dist/oauth/codes.js +43 -51
- package/dist/oauth/codes.js.map +1 -1
- package/dist/oauth/consent.d.ts +65 -0
- package/dist/oauth/consent.d.ts.map +1 -0
- package/dist/oauth/consent.js +173 -0
- package/dist/oauth/consent.js.map +1 -0
- package/dist/oauth/constants.d.ts +4 -0
- package/dist/oauth/constants.d.ts.map +1 -1
- package/dist/oauth/constants.js +4 -0
- package/dist/oauth/constants.js.map +1 -1
- package/dist/oauth/metadata.d.ts +6 -6
- package/dist/oauth/metadata.d.ts.map +1 -1
- package/dist/oauth/metadata.js.map +1 -1
- package/dist/oauth/pkce.d.ts.map +1 -1
- package/dist/oauth/pkce.js +1 -1
- package/dist/oauth/pkce.js.map +1 -1
- package/dist/oauth/redirect.d.ts +6 -1
- package/dist/oauth/redirect.d.ts.map +1 -1
- package/dist/oauth/redirect.js +23 -10
- package/dist/oauth/redirect.js.map +1 -1
- package/dist/oauth/reqbody.d.ts +10 -0
- package/dist/oauth/reqbody.d.ts.map +1 -0
- package/dist/oauth/reqbody.js +29 -0
- package/dist/oauth/reqbody.js.map +1 -0
- package/dist/oauth/resource.d.ts +30 -0
- package/dist/oauth/resource.d.ts.map +1 -1
- package/dist/oauth/resource.js +38 -8
- package/dist/oauth/resource.js.map +1 -1
- package/dist/oauth/revoke.d.ts.map +1 -1
- package/dist/oauth/revoke.js +15 -3
- package/dist/oauth/revoke.js.map +1 -1
- package/dist/oauth/router.d.ts +14 -11
- package/dist/oauth/router.d.ts.map +1 -1
- package/dist/oauth/router.js +62 -22
- package/dist/oauth/router.js.map +1 -1
- package/dist/oauth/scope.d.ts +11 -2
- package/dist/oauth/scope.d.ts.map +1 -1
- package/dist/oauth/scope.js +18 -7
- package/dist/oauth/scope.js.map +1 -1
- package/dist/oauth/sealed.d.ts +17 -0
- package/dist/oauth/sealed.d.ts.map +1 -0
- package/dist/oauth/sealed.js +57 -0
- package/dist/oauth/sealed.js.map +1 -0
- package/dist/oauth/secrethash.d.ts +44 -0
- package/dist/oauth/secrethash.d.ts.map +1 -0
- package/dist/oauth/secrethash.js +66 -0
- package/dist/oauth/secrethash.js.map +1 -0
- package/dist/oauth/token.d.ts.map +1 -1
- package/dist/oauth/token.js +13 -34
- package/dist/oauth/token.js.map +1 -1
- package/dist/oauth/types.d.ts +106 -9
- package/dist/oauth/types.d.ts.map +1 -1
- package/dist/oauth/types.js +67 -4
- package/dist/oauth/types.js.map +1 -1
- package/dist/oauth/wellknown.d.ts.map +1 -1
- package/dist/oauth/wellknown.js +6 -4
- package/dist/oauth/wellknown.js.map +1 -1
- package/dist/protocol.d.ts +15 -1
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +16 -6
- package/dist/protocol.js.map +1 -1
- package/dist/registry.d.ts +4 -4
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +15 -7
- package/dist/registry.js.map +1 -1
- package/dist/server.d.ts +14 -10
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +8 -8
- package/dist/server.js.map +1 -1
- package/dist/tools.d.ts +11 -2
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +14 -13
- package/dist/tools.js.map +1 -1
- package/dist/validate.d.ts +16 -1
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +46 -8
- package/dist/validate.js.map +1 -1
- package/docs/ROADMAP.md +295 -0
- package/docs/guide.md +417 -0
- package/docs/reference.md +178 -0
- package/docs/security.md +63 -0
- package/package.json +7 -3
package/dist/methods.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { wwwAuthenticateHeader } from "./auth/bearer.js";
|
|
2
2
|
import { emptyResponse, jsonResponse } from "./http.js";
|
|
3
|
+
import { JSONRPC_METHOD_NOT_FOUND, JSONRPC_UNAUTHORIZED, rpcError, rpcResult, } from "./jsonrpc.js";
|
|
3
4
|
import { pickProtocolVersion } from "./protocol.js";
|
|
4
|
-
|
|
5
|
+
const DEFAULT_AUDIT_TIMEOUT_MS = 1000;
|
|
5
6
|
export const DEFAULT_PUBLIC_METHODS = new Set([
|
|
6
7
|
"initialize",
|
|
7
8
|
"ping",
|
|
@@ -17,21 +18,38 @@ export const isNotification = (body) => NOTIFICATION_PREDICATES.some((predicate)
|
|
|
17
18
|
export const resolveWww = (options, req) => typeof options.wwwAuthenticate === "function"
|
|
18
19
|
? options.wwwAuthenticate(req)
|
|
19
20
|
: options.wwwAuthenticate;
|
|
20
|
-
|
|
21
|
+
/**
|
|
22
|
+
* `id` defaults to `null` for the transport-level call sites in
|
|
23
|
+
* `dispatch.ts` — a body hasn't been parsed yet there, so there's no real
|
|
24
|
+
* id to echo. Call sites past that point (inside `dispatchRpc` and the
|
|
25
|
+
* method handlers) have one and must pass it, or a client correlating
|
|
26
|
+
* responses by id sees `null` on every 401 regardless of what it sent.
|
|
27
|
+
*/
|
|
28
|
+
export const unauthorized = (www, message = "Unauthorized", id = null) => jsonResponse(rpcError(id, JSONRPC_UNAUTHORIZED, message), 401, {
|
|
21
29
|
"WWW-Authenticate": wwwAuthenticateHeader(www),
|
|
22
30
|
});
|
|
23
31
|
/**
|
|
24
|
-
* Invoke the audit metrics hook, swallowing any failure
|
|
25
|
-
*
|
|
26
|
-
*
|
|
32
|
+
* Invoke the audit metrics hook, swallowing any failure and racing it
|
|
33
|
+
* against `auditTimeoutMs` so a hanging sink can't stall the response
|
|
34
|
+
* either — "can't fail a request" was already true; this adds "can't
|
|
35
|
+
* delay one" past a bound. The audit call keeps running in the background
|
|
36
|
+
* if the timer wins; its own `catch` below means it can never surface as
|
|
37
|
+
* an unhandled rejection whenever it does finish.
|
|
27
38
|
*/
|
|
28
39
|
export const safeAudit = async (options, entry) => {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
40
|
+
const audit = options.ports.audit;
|
|
41
|
+
if (!audit)
|
|
42
|
+
return;
|
|
43
|
+
const settled = (async () => {
|
|
44
|
+
try {
|
|
45
|
+
await audit(entry);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// Intentionally ignored.
|
|
49
|
+
}
|
|
50
|
+
})();
|
|
51
|
+
const timeoutMs = options.auditTimeoutMs ?? DEFAULT_AUDIT_TIMEOUT_MS;
|
|
52
|
+
await Promise.race([settled, new Promise((resolve) => setTimeout(resolve, timeoutMs))]);
|
|
35
53
|
};
|
|
36
54
|
export const hasScope = (principal, scope) => {
|
|
37
55
|
if (!scope)
|
|
@@ -47,13 +65,11 @@ const METHODS = {
|
|
|
47
65
|
})),
|
|
48
66
|
ping: async ({ id }) => jsonResponse(rpcResult(id, {})),
|
|
49
67
|
"tools/list": async ({ id, options }) => jsonResponse(rpcResult(id, { tools: options.registry.list() })),
|
|
50
|
-
"tools/call": async ({ req, body, id, principal, ctx, options }) => {
|
|
68
|
+
"tools/call": async ({ req, body, id, principal, ctx, options, startedAt }) => {
|
|
51
69
|
const params = body.params ?? {};
|
|
52
70
|
const name = String(params.name ?? "");
|
|
53
71
|
const rawArgs = params.arguments;
|
|
54
|
-
const args = rawArgs !== null &&
|
|
55
|
-
typeof rawArgs === "object" &&
|
|
56
|
-
!Array.isArray(rawArgs)
|
|
72
|
+
const args = rawArgs !== null && typeof rawArgs === "object" && !Array.isArray(rawArgs)
|
|
57
73
|
? rawArgs
|
|
58
74
|
: {};
|
|
59
75
|
const tool = options.registry.get(name);
|
|
@@ -64,9 +80,9 @@ const METHODS = {
|
|
|
64
80
|
principalId: principal?.id,
|
|
65
81
|
ok: false,
|
|
66
82
|
error: "missing_scope",
|
|
67
|
-
durationMs:
|
|
83
|
+
durationMs: Date.now() - startedAt,
|
|
68
84
|
});
|
|
69
|
-
return unauthorized(resolveWww(options, req), `Missing scope: ${tool.scope}
|
|
85
|
+
return unauthorized(resolveWww(options, req), `Missing scope: ${tool.scope}`, id);
|
|
70
86
|
}
|
|
71
87
|
const started = Date.now();
|
|
72
88
|
const result = await options.registry.call(name, ctx, args);
|
|
@@ -82,7 +98,7 @@ const METHODS = {
|
|
|
82
98
|
},
|
|
83
99
|
};
|
|
84
100
|
export const dispatchRpc = async (input) => {
|
|
85
|
-
const { req, body, options, publicMethods } = input;
|
|
101
|
+
const { req, body, options, publicMethods, startedAt } = input;
|
|
86
102
|
const method = body.method ?? "";
|
|
87
103
|
// Notifications omit `id`; explicit null is a valid request id (JSON-RPC).
|
|
88
104
|
const id = body.id === undefined ? null : body.id;
|
|
@@ -97,9 +113,9 @@ export const dispatchRpc = async (input) => {
|
|
|
97
113
|
tool: toolName,
|
|
98
114
|
ok: false,
|
|
99
115
|
error: "unauthorized",
|
|
100
|
-
durationMs:
|
|
116
|
+
durationMs: Date.now() - startedAt,
|
|
101
117
|
});
|
|
102
|
-
return unauthorized(resolveWww(options, req));
|
|
118
|
+
return unauthorized(resolveWww(options, req), undefined, id);
|
|
103
119
|
}
|
|
104
120
|
}
|
|
105
121
|
const handler = METHODS[method];
|
|
@@ -109,6 +125,6 @@ export const dispatchRpc = async (input) => {
|
|
|
109
125
|
return jsonResponse(rpcError(body.id, JSONRPC_METHOD_NOT_FOUND, `Method not found: ${method}`));
|
|
110
126
|
}
|
|
111
127
|
const ctx = await options.ports.context(req, principal);
|
|
112
|
-
return handler({ req, body, id, principal, ctx, options });
|
|
128
|
+
return handler({ req, body, id, principal, ctx, options, startedAt });
|
|
113
129
|
};
|
|
114
130
|
//# sourceMappingURL=methods.js.map
|
package/dist/methods.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"methods.js","sourceRoot":"","sources":["../src/methods.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"methods.js","sourceRoot":"","sources":["../src/methods.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authorize.d.ts","sourceRoot":"","sources":["../../src/oauth/authorize.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"authorize.d.ts","sourceRoot":"","sources":["../../src/oauth/authorize.ts"],"names":[],"mappings":"AAeA,OAAO,EAIL,KAAK,kBAAkB,EAKxB,MAAM,YAAY,CAAC;AA6CpB,eAAO,MAAM,eAAe,GAC1B,SAAS,OAAO,EAChB,SAAS,kBAAkB,KAC1B,OAAO,CAAC,QAAQ,CAqHlB,CAAC"}
|
package/dist/oauth/authorize.js
CHANGED
|
@@ -1,41 +1,42 @@
|
|
|
1
1
|
import { requireHttpMethod } from "../http.js";
|
|
2
2
|
import { issueAuthCode } from "./codes.js";
|
|
3
|
+
import { buildCodeRedirectUrl, buildErrorRedirectUrl, isPreApproved, issueConsentTicket, renderConsent, secureRedirect, } from "./consent.js";
|
|
3
4
|
import { OAUTH_ERRORS } from "./constants.js";
|
|
4
|
-
import { isAllowedRedirectUri
|
|
5
|
-
import { canonicalResource,
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
5
|
+
import { isAllowedRedirectUri } from "./redirect.js";
|
|
6
|
+
import { canonicalResource, resourceErrorInfo } from "./resource.js";
|
|
7
|
+
import { formatScope, requestedScopes, scopeErrorInfo } from "./scope.js";
|
|
8
|
+
import { unseal } from "./sealed.js";
|
|
9
|
+
import { advertisedScopes, defaultScopes, oauthError, registeredClientsRequired, resolveSecret, unregisteredClientError, } from "./types.js";
|
|
8
10
|
const GET_ONLY = new Set(["GET"]);
|
|
11
|
+
/** Reflected into `Location` on denial — an unbounded `state` is a header-size DoS knob. */
|
|
12
|
+
const MAX_STATE_LENGTH = 2048;
|
|
9
13
|
/**
|
|
10
|
-
* A
|
|
11
|
-
*
|
|
14
|
+
* A client bound to a known redirect-URI list — either pre-registered via
|
|
15
|
+
* `clientStore`, or a sealed assertion this server itself issued via
|
|
16
|
+
* `/register` (see `sealed.ts`). Unbound clients fall back to the global
|
|
17
|
+
* allowlist, unless `requireRegisteredClients` forbids that entirely.
|
|
12
18
|
*/
|
|
13
|
-
const redirectAllowed = (redirectUri,
|
|
19
|
+
const redirectAllowed = (redirectUri, boundRedirectUris, allowlist) => {
|
|
14
20
|
if (!redirectUri)
|
|
15
21
|
return false;
|
|
16
|
-
if (
|
|
17
|
-
return
|
|
22
|
+
if (boundRedirectUris)
|
|
23
|
+
return boundRedirectUris.includes(redirectUri);
|
|
18
24
|
return isAllowedRedirectUri(redirectUri, allowlist);
|
|
19
25
|
};
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
{
|
|
27
|
-
ok: redirectAllowed(input.redirectUri, input.registered, input.redirect),
|
|
28
|
-
description: "invalid redirect_uri",
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
ok: Boolean(input.codeChallenge) && input.challengeMethod === "S256",
|
|
32
|
-
description: "PKCE S256 code_challenge required",
|
|
33
|
-
},
|
|
34
|
-
].find((entry) => !entry.ok);
|
|
35
|
-
if (rule) {
|
|
36
|
-
return oauthError(OAUTH_ERRORS.invalidRequest, 400, rule.description);
|
|
26
|
+
const redirectToLogin = (request, url, options) => {
|
|
27
|
+
const next = `${url.pathname}${url.search}`;
|
|
28
|
+
const login = options.ports.loginUrl(request, next);
|
|
29
|
+
let loginTarget;
|
|
30
|
+
try {
|
|
31
|
+
loginTarget = new URL(login, url.origin);
|
|
37
32
|
}
|
|
38
|
-
|
|
33
|
+
catch {
|
|
34
|
+
return oauthError(OAUTH_ERRORS.invalidRequest, 500, "loginUrl must be absolute or same-origin relative");
|
|
35
|
+
}
|
|
36
|
+
if (loginTarget.protocol !== "http:" && loginTarget.protocol !== "https:") {
|
|
37
|
+
return oauthError(OAUTH_ERRORS.invalidRequest, 500, "loginUrl must be absolute or same-origin relative");
|
|
38
|
+
}
|
|
39
|
+
return secureRedirect(loginTarget.toString());
|
|
39
40
|
};
|
|
40
41
|
export const handleAuthorize = async (request, options) => {
|
|
41
42
|
const methodError = requireHttpMethod(request, GET_ONLY);
|
|
@@ -48,62 +49,93 @@ export const handleAuthorize = async (request, options) => {
|
|
|
48
49
|
return oauthError(OAUTH_ERRORS.invalidRequest, 400, "client_id required");
|
|
49
50
|
}
|
|
50
51
|
const registered = (await options.ports.clientStore?.get(clientId)) ?? null;
|
|
51
|
-
|
|
52
|
+
const secret = await resolveSecret(options.ports, request);
|
|
53
|
+
// Additive: a `client_id` this server itself sealed via `/register` is
|
|
54
|
+
// bound to its own redirect_uris exactly like a pre-registered client,
|
|
55
|
+
// without ever being written to storage. Non-sealed ids unseal to null
|
|
56
|
+
// and keep today's behavior.
|
|
57
|
+
const assertion = registered ? null : await unseal(secret, "client", clientId);
|
|
58
|
+
if (!registered && !assertion) {
|
|
52
59
|
const unregistered = unregisteredClientError(options, {
|
|
53
60
|
code: OAUTH_ERRORS.unauthorizedClient,
|
|
54
61
|
status: 400,
|
|
55
62
|
});
|
|
56
63
|
if (unregistered)
|
|
57
64
|
return unregistered;
|
|
65
|
+
if (registeredClientsRequired(options)) {
|
|
66
|
+
return oauthError(OAUTH_ERRORS.unauthorizedClient, 400, "client_id must come from clientStore or this server's own /register");
|
|
67
|
+
}
|
|
58
68
|
}
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
const boundRedirectUris = registered?.redirectUris ?? assertion?.redirectUris ?? null;
|
|
70
|
+
const redirectUri = url.searchParams.get("redirect_uri") ?? "";
|
|
71
|
+
const state = url.searchParams.get("state") ?? "";
|
|
72
|
+
// --- Pre-trust tier: redirect_uri is not yet verified as belonging to a
|
|
73
|
+
// legitimate party, so these two stay direct JSON responses. Redirecting
|
|
74
|
+
// here is exactly the open-redirect risk RFC 6749 §4.1.2.1 exists to
|
|
75
|
+
// avoid — the URI itself is what's in question. An oversized `state` is
|
|
76
|
+
// included in this tier too, for a narrower reason: reflecting it back
|
|
77
|
+
// into the error redirect it would otherwise trigger recreates the
|
|
78
|
+
// header-size DoS the cap exists to prevent, on the one input the cap
|
|
79
|
+
// can't safely echo.
|
|
80
|
+
if (!redirectAllowed(redirectUri, boundRedirectUris, options.redirect)) {
|
|
81
|
+
return oauthError(OAUTH_ERRORS.invalidRequest, 400, "invalid redirect_uri");
|
|
82
|
+
}
|
|
83
|
+
if (state.length > MAX_STATE_LENGTH) {
|
|
84
|
+
return oauthError(OAUTH_ERRORS.invalidRequest, 400, `state must be at most ${MAX_STATE_LENGTH} characters`);
|
|
85
|
+
}
|
|
86
|
+
// --- Post-trust tier: redirect_uri is now trusted, so every remaining
|
|
87
|
+
// error goes back to the client's own callback per RFC 6749 §4.1.2.1
|
|
88
|
+
// instead of a bare JSON body the connector never parses.
|
|
89
|
+
const redirectError = (code, description) => secureRedirect(buildErrorRedirectUrl(redirectUri, code, description, state));
|
|
90
|
+
const responseType = url.searchParams.get("response_type") ?? "";
|
|
91
|
+
if (responseType !== "code") {
|
|
92
|
+
return redirectError(OAUTH_ERRORS.unsupportedResponseType, "response_type must be code");
|
|
93
|
+
}
|
|
94
|
+
const codeChallenge = url.searchParams.get("code_challenge") ?? "";
|
|
95
|
+
const challengeMethod = url.searchParams.get("code_challenge_method") ?? "S256";
|
|
96
|
+
if (!codeChallenge || challengeMethod !== "S256") {
|
|
97
|
+
return redirectError(OAUTH_ERRORS.invalidRequest, "PKCE S256 code_challenge required");
|
|
98
|
+
}
|
|
99
|
+
const resourceIssue = resourceErrorInfo(url.searchParams.get("resource") ?? "", expectedResource);
|
|
100
|
+
if (resourceIssue)
|
|
101
|
+
return redirectError(resourceIssue.code, resourceIssue.description);
|
|
71
102
|
const advertised = advertisedScopes(options);
|
|
72
|
-
const granted = requestedScopes(url.searchParams.get("scope") ?? "",
|
|
73
|
-
const
|
|
74
|
-
if (
|
|
75
|
-
return
|
|
103
|
+
const granted = requestedScopes(url.searchParams.get("scope") ?? "", defaultScopes(options));
|
|
104
|
+
const scopeIssue = scopeErrorInfo(granted, advertised);
|
|
105
|
+
if (scopeIssue)
|
|
106
|
+
return redirectError(scopeIssue.code, scopeIssue.description);
|
|
76
107
|
const user = await options.ports.resolveUser(request);
|
|
77
|
-
if (!user)
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
return Response.redirect(loginTarget.toString(), 302);
|
|
108
|
+
if (!user)
|
|
109
|
+
return redirectToLogin(request, url, options);
|
|
110
|
+
if (isPreApproved(options, clientId, registered)) {
|
|
111
|
+
const code = await issueAuthCode(secret, {
|
|
112
|
+
clientId,
|
|
113
|
+
redirectUri,
|
|
114
|
+
codeChallenge,
|
|
115
|
+
userId: user.id,
|
|
116
|
+
resource: expectedResource,
|
|
117
|
+
scope: formatScope(granted),
|
|
118
|
+
});
|
|
119
|
+
return secureRedirect(buildCodeRedirectUrl(redirectUri, code, state));
|
|
91
120
|
}
|
|
92
|
-
const
|
|
93
|
-
const secret = await resolveSecret(options.ports, request);
|
|
94
|
-
const code = await issueAuthCode(secret, {
|
|
121
|
+
const ticket = await issueConsentTicket(secret, {
|
|
95
122
|
clientId,
|
|
96
123
|
redirectUri,
|
|
97
|
-
codeChallenge
|
|
124
|
+
codeChallenge,
|
|
98
125
|
userId: user.id,
|
|
99
126
|
resource: expectedResource,
|
|
100
127
|
scope: formatScope(granted),
|
|
128
|
+
state,
|
|
129
|
+
});
|
|
130
|
+
return renderConsent(options, {
|
|
131
|
+
clientId,
|
|
132
|
+
registeredClient: registered,
|
|
133
|
+
redirectUri,
|
|
134
|
+
scope: granted,
|
|
135
|
+
resource: expectedResource,
|
|
136
|
+
user,
|
|
137
|
+
ticket,
|
|
138
|
+
oauthPath: options.oauthPath ?? "/mcp/oauth",
|
|
101
139
|
});
|
|
102
|
-
const target = new URL(redirectUri);
|
|
103
|
-
target.searchParams.set("code", code);
|
|
104
|
-
const state = url.searchParams.get("state") ?? "";
|
|
105
|
-
if (state)
|
|
106
|
-
target.searchParams.set("state", state);
|
|
107
|
-
return Response.redirect(target.toString(), 302);
|
|
108
140
|
};
|
|
109
141
|
//# sourceMappingURL=authorize.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authorize.js","sourceRoot":"","sources":["../../src/oauth/authorize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,
|
|
1
|
+
{"version":3,"file":"authorize.js","sourceRoot":"","sources":["../../src/oauth/authorize.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Shared base64url helpers (WebCrypto / DOM — no Node Buffer). */
|
|
2
2
|
export declare const bytesToBase64Url: (bytes: ArrayBuffer | Uint8Array) => string;
|
|
3
3
|
export declare const textToBase64Url: (text: string) => string;
|
|
4
|
-
export declare const fromBase64Url: (text: string) => Uint8Array
|
|
4
|
+
export declare const fromBase64Url: (text: string) => Uint8Array<ArrayBuffer>;
|
|
5
5
|
//# sourceMappingURL=base64url.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base64url.d.ts","sourceRoot":"","sources":["../../src/oauth/base64url.ts"],"names":[],"mappings":"AAAA,mEAAmE;AAEnE,eAAO,MAAM,gBAAgB,GAAI,OAAO,WAAW,GAAG,UAAU,KAAG,
|
|
1
|
+
{"version":3,"file":"base64url.d.ts","sourceRoot":"","sources":["../../src/oauth/base64url.ts"],"names":[],"mappings":"AAAA,mEAAmE;AAEnE,eAAO,MAAM,gBAAgB,GAAI,OAAO,WAAW,GAAG,UAAU,KAAG,MAIlE,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,MAAM,MAAM,KAAG,MACG,CAAC;AAEnD,eAAO,MAAM,aAAa,GAAI,MAAM,MAAM,KAAG,UAAU,CAAC,WAAW,CAKlE,CAAC"}
|
package/dist/oauth/base64url.js
CHANGED
|
@@ -2,10 +2,7 @@
|
|
|
2
2
|
export const bytesToBase64Url = (bytes) => {
|
|
3
3
|
const u8 = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
|
|
4
4
|
const binary = Array.from(u8, (byte) => String.fromCharCode(byte)).join("");
|
|
5
|
-
return btoa(binary)
|
|
6
|
-
.replace(/\+/g, "-")
|
|
7
|
-
.replace(/\//g, "_")
|
|
8
|
-
.replace(/=+$/, "");
|
|
5
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
9
6
|
};
|
|
10
7
|
export const textToBase64Url = (text) => bytesToBase64Url(new TextEncoder().encode(text));
|
|
11
8
|
export const fromBase64Url = (text) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base64url.js","sourceRoot":"","sources":["../../src/oauth/base64url.ts"],"names":[],"mappings":"AAAA,mEAAmE;AAEnE,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,KAA+B,EAAU,EAAE;IAC1E,MAAM,EAAE,GAAG,KAAK,YAAY,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;IACvE,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC5E,OAAO,IAAI,CAAC,MAAM,CAAC
|
|
1
|
+
{"version":3,"file":"base64url.js","sourceRoot":"","sources":["../../src/oauth/base64url.ts"],"names":[],"mappings":"AAAA,mEAAmE;AAEnE,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,KAA+B,EAAU,EAAE;IAC1E,MAAM,EAAE,GAAG,KAAK,YAAY,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;IACvE,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC5E,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACjF,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,IAAY,EAAU,EAAE,CACtD,gBAAgB,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;AAEnD,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,IAAY,EAA2B,EAAE;IACrE,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAC1D,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;IAC/E,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC;IAC/B,OAAO,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;AACtD,CAAC,CAAC"}
|
|
@@ -13,7 +13,12 @@ export declare const readClientAuth: (request: Request, body: Record<string, str
|
|
|
13
13
|
*
|
|
14
14
|
* An unknown client_id is treated as public — PKCE is its only proof —
|
|
15
15
|
* unless `allowUnregisteredClients` is false, in which case it is rejected
|
|
16
|
-
* rather than silently falling back to "no auth required".
|
|
16
|
+
* rather than silently falling back to "no auth required". On that
|
|
17
|
+
* locked-down path, a dummy secret comparison runs even for a client_id
|
|
18
|
+
* this server has never heard of, so the crypto-verification cost can't be
|
|
19
|
+
* used to distinguish "unknown" from "known, wrong secret" by timing. This
|
|
20
|
+
* only runs on the non-default, already-more-expensive path — public
|
|
21
|
+
* clients (the common case) never pay it.
|
|
17
22
|
*/
|
|
18
|
-
export declare const firstClientAuthError: (auth: ClientAuth, options: OAuthRouterOptions) => Promise<Response | null>;
|
|
23
|
+
export declare const firstClientAuthError: (auth: ClientAuth, options: OAuthRouterOptions, request: Request) => Promise<Response | null>;
|
|
19
24
|
//# sourceMappingURL=clientauth.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clientauth.d.ts","sourceRoot":"","sources":["../../src/oauth/clientauth.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAE9D,OAAO,EAGL,KAAK,uBAAuB,EAC7B,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"clientauth.d.ts","sourceRoot":"","sources":["../../src/oauth/clientauth.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAE9D,OAAO,EAGL,KAAK,uBAAuB,EAC7B,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,KAAK,kBAAkB,EAKxB,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,uBAAuB,CAAC;CACjC,CAAC;AAqBF,qEAAqE;AACrE,eAAO,MAAM,cAAc,GACzB,SAAS,OAAO,EAChB,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAC3B,UAAU,GAAG,IAaf,CAAC;AAuCF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,oBAAoB,GAC/B,MAAM,UAAU,EAChB,SAAS,kBAAkB,EAC3B,SAAS,OAAO,KACf,OAAO,CAAC,QAAQ,GAAG,IAAI,CAkDzB,CAAC"}
|
package/dist/oauth/clientauth.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** Token-endpoint client authentication (RFC 6749 §2.3.1). */
|
|
2
2
|
import { OAUTH_ERRORS, TOKEN_ENDPOINT_AUTH_METHODS, } from "./constants.js";
|
|
3
|
-
import {
|
|
3
|
+
import { verifyClientSecret } from "./secrethash.js";
|
|
4
|
+
import { oauthError, resolveSecret, safeOAuthAudit, unregisteredClientsAllowed, } from "./types.js";
|
|
4
5
|
/** `Authorization: Basic base64(urlencode(id):urlencode(secret))`. */
|
|
5
6
|
const parseBasicAuth = (header) => {
|
|
6
7
|
if (!header?.startsWith("Basic "))
|
|
@@ -35,41 +36,83 @@ export const readClientAuth = (request, body) => {
|
|
|
35
36
|
return {
|
|
36
37
|
clientId,
|
|
37
38
|
secret: secret.length > 0 ? secret : null,
|
|
38
|
-
method: secret.length > 0
|
|
39
|
-
? TOKEN_ENDPOINT_AUTH_METHODS.post
|
|
40
|
-
: TOKEN_ENDPOINT_AUTH_METHODS.none,
|
|
39
|
+
method: secret.length > 0 ? TOKEN_ENDPOINT_AUTH_METHODS.post : TOKEN_ENDPOINT_AUTH_METHODS.none,
|
|
41
40
|
};
|
|
42
41
|
};
|
|
43
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Every failure below returns the same code, status, and description.
|
|
44
|
+
* Distinguishable messages here ("client requires client_secret_basic" vs.
|
|
45
|
+
* "unknown client_id" vs. "client_secret required" vs. …) let an
|
|
46
|
+
* unauthenticated caller enumerate which client_ids this server actually
|
|
47
|
+
* knows about and how each is configured, just by reading error text —
|
|
48
|
+
* a real information leak on the one endpoint whose entire job is to
|
|
49
|
+
* reject an untrusted caller. `/authorize`'s `unauthorized_client` stays
|
|
50
|
+
* descriptive on purpose: it's a human debugging an OAuth flow in a
|
|
51
|
+
* browser, not a bare API response, and a locked-down server already
|
|
52
|
+
* announces the same fact by omitting `registration_endpoint` from its
|
|
53
|
+
* metadata.
|
|
54
|
+
*/
|
|
55
|
+
const CLIENT_AUTH_FAILED = "client authentication failed";
|
|
56
|
+
/** The generic response every failure below returns to the caller. */
|
|
57
|
+
const invalidClient = () => oauthError(OAUTH_ERRORS.invalidClient, 401, CLIENT_AUTH_FAILED);
|
|
58
|
+
/** The response above, plus the real reason to `ports.audit` — never to the caller. */
|
|
59
|
+
const invalidClientAudited = async (options, clientId, reason) => {
|
|
60
|
+
await safeOAuthAudit(options, { event: "client_auth_failed", clientId, reason });
|
|
61
|
+
return invalidClient();
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Never a real credential — exists only so the locked-down branch below
|
|
65
|
+
* does the same amount of comparison work whether or not a secret was
|
|
66
|
+
* actually presented, and whether or not a stored hash actually exists.
|
|
67
|
+
*/
|
|
68
|
+
const DUMMY_SECRET = "dummy-secret-for-constant-time-padding";
|
|
69
|
+
const DUMMY_STORED_HASH = "hmac-sha256$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
|
44
70
|
/**
|
|
45
71
|
* Enforce the registered auth method for known clients.
|
|
46
72
|
*
|
|
47
73
|
* An unknown client_id is treated as public — PKCE is its only proof —
|
|
48
74
|
* unless `allowUnregisteredClients` is false, in which case it is rejected
|
|
49
|
-
* rather than silently falling back to "no auth required".
|
|
75
|
+
* rather than silently falling back to "no auth required". On that
|
|
76
|
+
* locked-down path, a dummy secret comparison runs even for a client_id
|
|
77
|
+
* this server has never heard of, so the crypto-verification cost can't be
|
|
78
|
+
* used to distinguish "unknown" from "known, wrong secret" by timing. This
|
|
79
|
+
* only runs on the non-default, already-more-expensive path — public
|
|
80
|
+
* clients (the common case) never pay it.
|
|
50
81
|
*/
|
|
51
|
-
export const firstClientAuthError = async (auth, options) => {
|
|
82
|
+
export const firstClientAuthError = async (auth, options, request) => {
|
|
52
83
|
const store = options.ports.clientStore;
|
|
53
84
|
const registered = (await store?.get(auth.clientId)) ?? null;
|
|
54
85
|
if (!registered) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
86
|
+
if (unregisteredClientsAllowed(options))
|
|
87
|
+
return null;
|
|
88
|
+
const codeSecretValue = await resolveSecret(options.ports, request);
|
|
89
|
+
await verifyClientSecret(auth.secret ?? DUMMY_SECRET, DUMMY_STORED_HASH, codeSecretValue);
|
|
90
|
+
return invalidClientAudited(options, auth.clientId, "unknown client_id");
|
|
59
91
|
}
|
|
60
92
|
const expected = registered.tokenEndpointAuthMethod;
|
|
61
93
|
if (expected === TOKEN_ENDPOINT_AUTH_METHODS.none)
|
|
62
94
|
return null;
|
|
63
95
|
if (auth.method !== expected) {
|
|
64
|
-
return
|
|
96
|
+
return invalidClientAudited(options, auth.clientId, `client requires ${expected}, got ${auth.method}`);
|
|
97
|
+
}
|
|
98
|
+
if (!auth.secret) {
|
|
99
|
+
return invalidClientAudited(options, auth.clientId, "client_secret required");
|
|
100
|
+
}
|
|
101
|
+
if (store?.secretHash) {
|
|
102
|
+
const codeSecretValue = await resolveSecret(options.ports, request);
|
|
103
|
+
const stored = await store.secretHash(auth.clientId);
|
|
104
|
+
// Constant path whether a hash is on file or not — same reasoning as
|
|
105
|
+
// the unregistered-client branch above.
|
|
106
|
+
const ok = await verifyClientSecret(auth.secret, stored ?? DUMMY_STORED_HASH, codeSecretValue);
|
|
107
|
+
if (ok && stored !== null)
|
|
108
|
+
return null;
|
|
109
|
+
return invalidClientAudited(options, auth.clientId, stored === null ? "no secretHash on file for this client" : "secretHash mismatch");
|
|
65
110
|
}
|
|
66
|
-
if (!auth.secret)
|
|
67
|
-
return invalidClient("client_secret required");
|
|
68
111
|
if (!store?.verifySecret) {
|
|
69
|
-
return
|
|
112
|
+
return invalidClientAudited(options, auth.clientId, "clientStore.verifySecret is not configured");
|
|
70
113
|
}
|
|
71
114
|
if (!(await store.verifySecret(auth.clientId, auth.secret))) {
|
|
72
|
-
return
|
|
115
|
+
return invalidClientAudited(options, auth.clientId, "verifySecret returned false");
|
|
73
116
|
}
|
|
74
117
|
return null;
|
|
75
118
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clientauth.js","sourceRoot":"","sources":["../../src/oauth/clientauth.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAE9D,OAAO,EACL,YAAY,EACZ,2BAA2B,GAE5B,MAAM,gBAAgB,CAAC;AACxB,OAAO,
|
|
1
|
+
{"version":3,"file":"clientauth.js","sourceRoot":"","sources":["../../src/oauth/clientauth.ts"],"names":[],"mappings":"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"}
|
package/dist/oauth/codes.d.ts
CHANGED
|
@@ -18,13 +18,13 @@ export type CodeStore = {
|
|
|
18
18
|
consume: (jti: string, expMs: number) => Promise<boolean> | boolean;
|
|
19
19
|
};
|
|
20
20
|
/**
|
|
21
|
-
* Issue an
|
|
22
|
-
*
|
|
21
|
+
* Issue an AES-GCM sealed auth code (v2). Single-use via codeStore, or
|
|
22
|
+
* pruning in-memory Map when omitted.
|
|
23
23
|
*/
|
|
24
24
|
export declare const issueAuthCode: (secret: string, record: Omit<AuthCodeRecord, "exp">, nowMs?: number) => Promise<string>;
|
|
25
25
|
export declare const consumeAuthCode: (secret: string, code: string, options?: {
|
|
26
|
-
codeStore?: CodeStore;
|
|
27
|
-
nowMs?: number;
|
|
26
|
+
codeStore?: CodeStore | undefined;
|
|
27
|
+
nowMs?: number | undefined;
|
|
28
28
|
}) => Promise<AuthCodeRecord | null>;
|
|
29
29
|
export declare const newClientId: () => string;
|
|
30
30
|
//# sourceMappingURL=codes.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codes.d.ts","sourceRoot":"","sources":["../../src/oauth/codes.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"codes.d.ts","sourceRoot":"","sources":["../../src/oauth/codes.ts"],"names":[],"mappings":"AAaA,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,gEAAgE;IAChE,MAAM,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,QAAQ,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB;;;OAGG;IACH,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;CACrE,CAAC;AAkBF;;;GAGG;AACH,eAAO,MAAM,aAAa,GACxB,QAAQ,MAAM,EACd,QAAQ,IAAI,CAAC,cAAc,EAAE,KAAK,CAAC,EACnC,cAAkB,KACjB,OAAO,CAAC,MAAM,CAOhB,CAAC;AAmCF,eAAO,MAAM,eAAe,GAC1B,QAAQ,MAAM,EACd,MAAM,MAAM,EACZ,UAAS;IAAE,SAAS,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAO,KAC9E,OAAO,CAAC,cAAc,GAAG,IAAI,CAkB/B,CAAC;AAEF,eAAO,MAAM,WAAW,QAAO,MAA6B,CAAC"}
|