mcp-trellis 1.0.0 → 2.2.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 +65 -93
- package/dist/adapters/node-request.d.ts +53 -0
- package/dist/adapters/node-request.d.ts.map +1 -0
- package/dist/adapters/node-request.js +165 -0
- package/dist/adapters/node-request.js.map +1 -0
- package/dist/adapters/node.d.ts +13 -30
- package/dist/adapters/node.d.ts.map +1 -1
- package/dist/adapters/node.js +55 -101
- package/dist/adapters/node.js.map +1 -1
- package/dist/advanced.d.ts +18 -0
- package/dist/advanced.d.ts.map +1 -0
- package/dist/advanced.js +18 -0
- package/dist/advanced.js.map +1 -0
- package/dist/app-options.d.ts +132 -0
- package/dist/app-options.d.ts.map +1 -0
- package/dist/app-options.js +2 -0
- package/dist/app-options.js.map +1 -0
- package/dist/app.d.ts +2 -89
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +143 -63
- package/dist/app.js.map +1 -1
- package/dist/audit.d.ts +5 -5
- package/dist/audit.d.ts.map +1 -1
- package/dist/audit.js +19 -8
- package/dist/audit.js.map +1 -1
- package/dist/auth/bearer.d.ts +4 -1
- package/dist/auth/bearer.d.ts.map +1 -1
- package/dist/auth/bearer.js +19 -4
- package/dist/auth/bearer.js.map +1 -1
- package/dist/auth/signed-token.d.ts +26 -0
- package/dist/auth/signed-token.d.ts.map +1 -0
- package/dist/auth/signed-token.js +96 -0
- package/dist/auth/signed-token.js.map +1 -0
- package/dist/clients.js +1 -1
- package/dist/clients.js.map +1 -1
- package/dist/deprecated.d.ts +42 -0
- package/dist/deprecated.d.ts.map +1 -0
- package/dist/deprecated.js +35 -0
- package/dist/deprecated.js.map +1 -0
- package/dist/{body.d.ts → http/body.d.ts} +27 -6
- package/dist/http/body.d.ts.map +1 -0
- package/dist/{body.js → http/body.js} +33 -13
- package/dist/http/body.js.map +1 -0
- package/dist/http/http.d.ts +42 -0
- package/dist/http/http.d.ts.map +1 -0
- package/dist/http/http.js +100 -0
- package/dist/http/http.js.map +1 -0
- package/dist/http/origins.d.ts +37 -0
- package/dist/http/origins.d.ts.map +1 -0
- package/dist/http/origins.js +87 -0
- package/dist/http/origins.js.map +1 -0
- package/dist/{dispatch.d.ts → mcp/dispatch.d.ts} +1 -1
- package/dist/mcp/dispatch.d.ts.map +1 -0
- package/dist/mcp/dispatch.js +99 -0
- package/dist/mcp/dispatch.js.map +1 -0
- package/dist/{jsonrpc.d.ts → mcp/jsonrpc.d.ts} +9 -0
- package/dist/mcp/jsonrpc.d.ts.map +1 -0
- package/dist/mcp/jsonrpc.js +88 -0
- package/dist/mcp/jsonrpc.js.map +1 -0
- package/dist/{methods.d.ts → mcp/methods.d.ts} +60 -11
- package/dist/mcp/methods.d.ts.map +1 -0
- package/dist/mcp/methods.js +84 -0
- package/dist/mcp/methods.js.map +1 -0
- package/dist/{protocol.d.ts → mcp/protocol.d.ts} +1 -3
- package/dist/mcp/protocol.d.ts.map +1 -0
- package/dist/{protocol.js → mcp/protocol.js} +1 -3
- package/dist/mcp/protocol.js.map +1 -0
- package/dist/mcp/registry.d.ts +58 -0
- package/dist/mcp/registry.d.ts.map +1 -0
- package/dist/mcp/registry.js +81 -0
- package/dist/mcp/registry.js.map +1 -0
- package/dist/mcp/rpc-handlers.d.ts +11 -0
- package/dist/mcp/rpc-handlers.d.ts.map +1 -0
- package/dist/mcp/rpc-handlers.js +133 -0
- package/dist/mcp/rpc-handlers.js.map +1 -0
- package/dist/{tools.d.ts → mcp/tools.d.ts} +20 -1
- package/dist/mcp/tools.d.ts.map +1 -0
- package/dist/mcp/tools.js +166 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/{validate.d.ts → mcp/validate.d.ts} +18 -21
- package/dist/mcp/validate.d.ts.map +1 -0
- package/dist/{validate.js → mcp/validate.js} +59 -41
- package/dist/mcp/validate.js.map +1 -0
- package/dist/oauth/audit.d.ts +20 -0
- package/dist/oauth/audit.d.ts.map +1 -0
- package/dist/oauth/audit.js +29 -0
- package/dist/oauth/audit.js.map +1 -0
- package/dist/oauth/cimd/cimd-parse.d.ts +4 -0
- package/dist/oauth/cimd/cimd-parse.d.ts.map +1 -0
- package/dist/oauth/cimd/cimd-parse.js +70 -0
- package/dist/oauth/cimd/cimd-parse.js.map +1 -0
- package/dist/oauth/cimd/cimd-ssrf.d.ts +8 -0
- package/dist/oauth/cimd/cimd-ssrf.d.ts.map +1 -0
- package/dist/oauth/cimd/cimd-ssrf.js +193 -0
- package/dist/oauth/cimd/cimd-ssrf.js.map +1 -0
- package/dist/oauth/cimd/cimd.d.ts +36 -0
- package/dist/oauth/cimd/cimd.d.ts.map +1 -0
- package/dist/oauth/cimd/cimd.js +132 -0
- package/dist/oauth/cimd/cimd.js.map +1 -0
- package/dist/oauth/cimd/types.d.ts +12 -0
- package/dist/oauth/cimd/types.d.ts.map +1 -0
- package/dist/oauth/cimd/types.js +3 -0
- package/dist/oauth/cimd/types.js.map +1 -0
- package/dist/oauth/config.d.ts +51 -0
- package/dist/oauth/config.d.ts.map +1 -0
- package/dist/oauth/config.js +108 -0
- package/dist/oauth/config.js.map +1 -0
- package/dist/oauth/constants.d.ts +11 -0
- package/dist/oauth/constants.d.ts.map +1 -1
- package/dist/oauth/constants.js +11 -0
- package/dist/oauth/constants.js.map +1 -1
- package/dist/oauth/{base64url.d.ts → crypto/base64url.d.ts} +0 -1
- package/dist/oauth/crypto/base64url.d.ts.map +1 -0
- package/dist/oauth/{base64url.js → crypto/base64url.js} +0 -1
- package/dist/oauth/crypto/base64url.js.map +1 -0
- package/dist/oauth/crypto/clientauth.d.ts +13 -0
- package/dist/oauth/crypto/clientauth.d.ts.map +1 -0
- package/dist/oauth/crypto/clientauth.js +195 -0
- package/dist/oauth/crypto/clientauth.js.map +1 -0
- package/dist/oauth/crypto/codes.d.ts +65 -0
- package/dist/oauth/crypto/codes.d.ts.map +1 -0
- package/dist/oauth/crypto/codes.js +121 -0
- package/dist/oauth/crypto/codes.js.map +1 -0
- package/dist/oauth/crypto/hkdf.d.ts +13 -0
- package/dist/oauth/crypto/hkdf.d.ts.map +1 -0
- package/dist/oauth/crypto/hkdf.js +30 -0
- package/dist/oauth/crypto/hkdf.js.map +1 -0
- package/dist/oauth/crypto/keycache.d.ts +19 -0
- package/dist/oauth/crypto/keycache.d.ts.map +1 -0
- package/dist/oauth/crypto/keycache.js +44 -0
- package/dist/oauth/crypto/keycache.js.map +1 -0
- package/dist/oauth/crypto/pkce.d.ts.map +1 -0
- package/dist/oauth/{pkce.js → crypto/pkce.js} +1 -1
- package/dist/oauth/crypto/pkce.js.map +1 -0
- package/dist/oauth/{sealed.d.ts → crypto/sealed.d.ts} +12 -3
- package/dist/oauth/crypto/sealed.d.ts.map +1 -0
- package/dist/oauth/{sealed.js → crypto/sealed.js} +22 -14
- package/dist/oauth/crypto/sealed.js.map +1 -0
- package/dist/oauth/{secrethash.d.ts → crypto/secrethash.d.ts} +14 -0
- package/dist/oauth/crypto/secrethash.d.ts.map +1 -0
- package/dist/oauth/{secrethash.js → crypto/secrethash.js} +27 -11
- package/dist/oauth/crypto/secrethash.js.map +1 -0
- package/dist/oauth/deprecated.d.ts +52 -0
- package/dist/oauth/deprecated.d.ts.map +1 -0
- package/dist/oauth/deprecated.js +49 -0
- package/dist/oauth/deprecated.js.map +1 -0
- package/dist/oauth/endpoints/authorize.d.ts +3 -0
- package/dist/oauth/endpoints/authorize.d.ts.map +1 -0
- package/dist/oauth/endpoints/authorize.js +209 -0
- package/dist/oauth/endpoints/authorize.js.map +1 -0
- package/dist/oauth/endpoints/callback.d.ts +20 -0
- package/dist/oauth/endpoints/callback.d.ts.map +1 -0
- package/dist/oauth/endpoints/callback.js +31 -0
- package/dist/oauth/endpoints/callback.js.map +1 -0
- package/dist/oauth/endpoints/consent-page.d.ts +5 -0
- package/dist/oauth/endpoints/consent-page.d.ts.map +1 -0
- package/dist/oauth/endpoints/consent-page.js +63 -0
- package/dist/oauth/endpoints/consent-page.js.map +1 -0
- package/dist/oauth/endpoints/consent.d.ts +29 -0
- package/dist/oauth/endpoints/consent.d.ts.map +1 -0
- package/dist/oauth/endpoints/consent.js +98 -0
- package/dist/oauth/endpoints/consent.js.map +1 -0
- package/dist/oauth/{metadata.d.ts → endpoints/metadata.d.ts} +8 -2
- package/dist/oauth/endpoints/metadata.d.ts.map +1 -0
- package/dist/oauth/{metadata.js → endpoints/metadata.js} +10 -4
- package/dist/oauth/endpoints/metadata.js.map +1 -0
- package/dist/oauth/endpoints/register.d.ts +10 -0
- package/dist/oauth/endpoints/register.d.ts.map +1 -0
- package/dist/oauth/endpoints/register.js +96 -0
- package/dist/oauth/endpoints/register.js.map +1 -0
- package/dist/oauth/{revoke.d.ts → endpoints/revoke.d.ts} +1 -1
- package/dist/oauth/endpoints/revoke.d.ts.map +1 -0
- package/dist/oauth/{revoke.js → endpoints/revoke.js} +8 -18
- package/dist/oauth/endpoints/revoke.js.map +1 -0
- package/dist/oauth/endpoints/token.d.ts +3 -0
- package/dist/oauth/endpoints/token.d.ts.map +1 -0
- package/dist/oauth/{token.js → endpoints/token.js} +32 -25
- package/dist/oauth/endpoints/token.js.map +1 -0
- package/dist/oauth/{wellknown.d.ts → endpoints/wellknown.d.ts} +1 -1
- package/dist/oauth/endpoints/wellknown.d.ts.map +1 -0
- package/dist/oauth/{wellknown.js → endpoints/wellknown.js} +7 -9
- package/dist/oauth/endpoints/wellknown.js.map +1 -0
- package/dist/oauth/policy/redirect.d.ts.map +1 -0
- package/dist/oauth/policy/redirect.js.map +1 -0
- package/dist/oauth/policy/reqbody.d.ts +18 -0
- package/dist/oauth/policy/reqbody.d.ts.map +1 -0
- package/dist/oauth/policy/reqbody.js +68 -0
- package/dist/oauth/policy/reqbody.js.map +1 -0
- package/dist/oauth/{resource.d.ts → policy/resource.d.ts} +1 -1
- package/dist/oauth/policy/resource.d.ts.map +1 -0
- package/dist/oauth/{resource.js → policy/resource.js} +2 -2
- package/dist/oauth/policy/resource.js.map +1 -0
- package/dist/oauth/{scope.d.ts → policy/scope.d.ts} +2 -2
- package/dist/oauth/policy/scope.d.ts.map +1 -0
- package/dist/oauth/{scope.js → policy/scope.js} +3 -3
- package/dist/oauth/policy/scope.js.map +1 -0
- package/dist/oauth/router.d.ts +10 -13
- package/dist/oauth/router.d.ts.map +1 -1
- package/dist/oauth/router.js +93 -115
- package/dist/oauth/router.js.map +1 -1
- package/dist/oauth/types.d.ts +99 -45
- package/dist/oauth/types.d.ts.map +1 -1
- package/dist/oauth/types.js +1 -90
- package/dist/oauth/types.js.map +1 -1
- package/dist/server.d.ts +13 -12
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +10 -8
- package/dist/server.js.map +1 -1
- package/dist/util/defined.d.ts +11 -0
- package/dist/util/defined.d.ts.map +1 -0
- package/dist/util/defined.js +15 -0
- package/dist/util/defined.js.map +1 -0
- package/dist/util/json.d.ts +3 -0
- package/dist/util/json.d.ts.map +1 -0
- package/dist/util/json.js +3 -0
- package/dist/util/json.js.map +1 -0
- package/docs/ROADMAP.md +37 -47
- package/docs/compatibility.md +86 -0
- package/docs/diagrams/demo-tool-result.png +0 -0
- package/docs/diagrams/mcp-trellis-architecture.html +305 -0
- package/docs/guide.md +46 -16
- package/docs/reference.md +55 -22
- package/docs/security.md +58 -9
- package/docs/troubleshooting.md +19 -0
- package/package.json +27 -10
- package/dist/adapters/origins.d.ts +0 -19
- package/dist/adapters/origins.d.ts.map +0 -1
- package/dist/adapters/origins.js +0 -52
- package/dist/adapters/origins.js.map +0 -1
- package/dist/body.d.ts.map +0 -1
- package/dist/body.js.map +0 -1
- package/dist/dispatch.d.ts.map +0 -1
- package/dist/dispatch.js +0 -119
- package/dist/dispatch.js.map +0 -1
- package/dist/http.d.ts +0 -17
- package/dist/http.d.ts.map +0 -1
- package/dist/http.js +0 -38
- package/dist/http.js.map +0 -1
- package/dist/jsonrpc.d.ts.map +0 -1
- package/dist/jsonrpc.js +0 -18
- package/dist/jsonrpc.js.map +0 -1
- package/dist/methods.d.ts.map +0 -1
- package/dist/methods.js +0 -130
- package/dist/methods.js.map +0 -1
- package/dist/oauth/authorize.d.ts +0 -3
- package/dist/oauth/authorize.d.ts.map +0 -1
- package/dist/oauth/authorize.js +0 -141
- package/dist/oauth/authorize.js.map +0 -1
- package/dist/oauth/base64url.d.ts.map +0 -1
- package/dist/oauth/base64url.js.map +0 -1
- package/dist/oauth/clientauth.d.ts +0 -24
- package/dist/oauth/clientauth.d.ts.map +0 -1
- package/dist/oauth/clientauth.js +0 -119
- package/dist/oauth/clientauth.js.map +0 -1
- package/dist/oauth/codes.d.ts +0 -30
- package/dist/oauth/codes.d.ts.map +0 -1
- package/dist/oauth/codes.js +0 -85
- package/dist/oauth/codes.js.map +0 -1
- package/dist/oauth/consent.d.ts +0 -65
- package/dist/oauth/consent.d.ts.map +0 -1
- package/dist/oauth/consent.js +0 -173
- package/dist/oauth/consent.js.map +0 -1
- package/dist/oauth/metadata.d.ts.map +0 -1
- package/dist/oauth/metadata.js.map +0 -1
- package/dist/oauth/pkce.d.ts.map +0 -1
- package/dist/oauth/pkce.js.map +0 -1
- package/dist/oauth/redirect.d.ts.map +0 -1
- package/dist/oauth/redirect.js.map +0 -1
- package/dist/oauth/reqbody.d.ts +0 -10
- package/dist/oauth/reqbody.d.ts.map +0 -1
- package/dist/oauth/reqbody.js +0 -29
- package/dist/oauth/reqbody.js.map +0 -1
- package/dist/oauth/resource.d.ts.map +0 -1
- package/dist/oauth/resource.js.map +0 -1
- package/dist/oauth/revoke.d.ts.map +0 -1
- package/dist/oauth/revoke.js.map +0 -1
- package/dist/oauth/scope.d.ts.map +0 -1
- package/dist/oauth/scope.js.map +0 -1
- package/dist/oauth/sealed.d.ts.map +0 -1
- package/dist/oauth/sealed.js.map +0 -1
- package/dist/oauth/secrethash.d.ts.map +0 -1
- package/dist/oauth/secrethash.js.map +0 -1
- package/dist/oauth/token.d.ts +0 -3
- package/dist/oauth/token.d.ts.map +0 -1
- package/dist/oauth/token.js.map +0 -1
- package/dist/oauth/wellknown.d.ts.map +0 -1
- package/dist/oauth/wellknown.js.map +0 -1
- package/dist/protocol.d.ts.map +0 -1
- package/dist/protocol.js.map +0 -1
- package/dist/registry.d.ts +0 -37
- package/dist/registry.d.ts.map +0 -1
- package/dist/registry.js +0 -77
- package/dist/registry.js.map +0 -1
- package/dist/tools.d.ts.map +0 -1
- package/dist/tools.js +0 -98
- package/dist/tools.js.map +0 -1
- package/dist/validate.d.ts.map +0 -1
- package/dist/validate.js.map +0 -1
- /package/dist/oauth/{pkce.d.ts → crypto/pkce.d.ts} +0 -0
- /package/dist/oauth/{redirect.d.ts → policy/redirect.d.ts} +0 -0
- /package/dist/oauth/{redirect.js → policy/redirect.js} +0 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { GET_ONLY, requireHttpMethod } from "../../http/http.js";
|
|
2
|
+
import { auditLegacyKeyUsage } from "../audit.js";
|
|
3
|
+
import { cimdFetchOpts, isCimdClientId, resolveCimdClient } from "../cimd/cimd.js";
|
|
4
|
+
import { advertisedScopes, defaultScopes, oauthError, registeredClientsRequired, resolveSecrets, unregisteredClientError, } from "../config.js";
|
|
5
|
+
import { OAUTH_ERRORS } from "../constants.js";
|
|
6
|
+
import { issueAuthCode } from "../crypto/codes.js";
|
|
7
|
+
import { unsealAny } from "../crypto/sealed.js";
|
|
8
|
+
import { isAllowedRedirectUri } from "../policy/redirect.js";
|
|
9
|
+
import { canonicalResource, resourceErrorInfo } from "../policy/resource.js";
|
|
10
|
+
import { formatScope, requestedScopes, scopeErrorInfo } from "../policy/scope.js";
|
|
11
|
+
import { codeRedirect, errorRedirect, secureRedirect } from "./callback.js";
|
|
12
|
+
import { isPreApproved, issueConsentTicket, renderConsent } from "./consent.js";
|
|
13
|
+
/** BASE64URL(SHA-256(verifier)) without padding is always exactly 43 characters. */
|
|
14
|
+
const S256_CHALLENGE = /^[A-Za-z0-9_-]{43}$/;
|
|
15
|
+
/** Reflected into `Location` on denial — an unbounded `state` is a header-size DoS knob. */
|
|
16
|
+
const MAX_STATE_LENGTH = 2048;
|
|
17
|
+
/**
|
|
18
|
+
* A client bound to a known redirect-URI list — either pre-registered via
|
|
19
|
+
* `clientStore`, or a sealed assertion this server itself issued via
|
|
20
|
+
* `/register` (see `sealed.ts`). Unbound clients fall back to the global
|
|
21
|
+
* allowlist, unless `requireRegisteredClients` forbids that entirely.
|
|
22
|
+
*/
|
|
23
|
+
const redirectAllowed = (redirectUri, boundRedirectUris, allowlist) => {
|
|
24
|
+
if (!redirectUri)
|
|
25
|
+
return false;
|
|
26
|
+
if (boundRedirectUris)
|
|
27
|
+
return boundRedirectUris.includes(redirectUri);
|
|
28
|
+
return isAllowedRedirectUri(redirectUri, allowlist);
|
|
29
|
+
};
|
|
30
|
+
const redirectToLogin = (request, url, options) => {
|
|
31
|
+
const next = `${url.pathname}${url.search}`;
|
|
32
|
+
const login = options.ports.loginUrl(request, next);
|
|
33
|
+
let loginTarget;
|
|
34
|
+
try {
|
|
35
|
+
loginTarget = new URL(login, url.origin);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return oauthError(OAUTH_ERRORS.invalidRequest, 500, "loginUrl must be absolute or same-origin relative");
|
|
39
|
+
}
|
|
40
|
+
if (loginTarget.protocol !== "http:" && loginTarget.protocol !== "https:") {
|
|
41
|
+
return oauthError(OAUTH_ERRORS.invalidRequest, 500, "loginUrl must be absolute or same-origin relative");
|
|
42
|
+
}
|
|
43
|
+
return secureRedirect(loginTarget.toString());
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Resolves `client_id` against `clientStore`, this server's own sealed
|
|
47
|
+
* `/register` assertions, and (when enabled) CIMD — in that order — and
|
|
48
|
+
* rejects when none recognize it and the config requires one to. A sealed
|
|
49
|
+
* assertion binds its own `redirectUris` like a pre-registered client,
|
|
50
|
+
* without ever touching storage.
|
|
51
|
+
*/
|
|
52
|
+
const resolveAuthorizeClient = async (options, clientId, secrets) => {
|
|
53
|
+
const registered = (await options.ports.clientStore?.get(clientId)) ?? null;
|
|
54
|
+
const assertion = registered ? null : await unsealClientAssertion(options, clientId, secrets);
|
|
55
|
+
const isCimdId = options.cimd === true && isCimdClientId(clientId);
|
|
56
|
+
const cimdDoc = !registered && !assertion && isCimdId
|
|
57
|
+
? await resolveCimdClient(clientId, cimdFetchOpts(options))
|
|
58
|
+
: null;
|
|
59
|
+
if (!registered && !assertion && !cimdDoc) {
|
|
60
|
+
const unknown = unknownClientError(options, isCimdId);
|
|
61
|
+
if (unknown)
|
|
62
|
+
return unknown;
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
registered,
|
|
66
|
+
boundRedirectUris: registered?.redirectUris ?? assertion?.redirectUris ?? cimdDoc?.redirect_uris ?? null,
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
/** This server's own `/register` assertion for `clientId`, auditing a legacy-key unseal. */
|
|
70
|
+
const unsealClientAssertion = async (options, clientId, secrets) => {
|
|
71
|
+
const unsealed = await unsealAny(secrets, "client", clientId);
|
|
72
|
+
await auditLegacyKeyUsage(options, {
|
|
73
|
+
clientId,
|
|
74
|
+
keyIndex: unsealed?.keyIndex,
|
|
75
|
+
source: "client assertion unsealed",
|
|
76
|
+
});
|
|
77
|
+
return unsealed?.value ?? null;
|
|
78
|
+
};
|
|
79
|
+
/** The rejection for a `client_id` nothing resolved, or null when the config admits it. */
|
|
80
|
+
const unknownClientError = (options, isCimdId) => {
|
|
81
|
+
const unregistered = unregisteredClientError(options, {
|
|
82
|
+
code: OAUTH_ERRORS.unauthorizedClient,
|
|
83
|
+
status: 400,
|
|
84
|
+
});
|
|
85
|
+
if (unregistered)
|
|
86
|
+
return unregistered;
|
|
87
|
+
if (isCimdId) {
|
|
88
|
+
return oauthError(OAUTH_ERRORS.unauthorizedClient, 400, "CIMD client_id could not be resolved");
|
|
89
|
+
}
|
|
90
|
+
if (!registeredClientsRequired(options))
|
|
91
|
+
return null;
|
|
92
|
+
return oauthError(OAUTH_ERRORS.unauthorizedClient, 400, "client_id must come from clientStore or this server's own /register");
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Pre-trust tier: `redirect_uri` is not yet verified as belonging to a
|
|
96
|
+
* legitimate party, so these two stay direct JSON responses. Redirecting
|
|
97
|
+
* here is exactly the open-redirect risk RFC 6749 §4.1.2.1 exists to
|
|
98
|
+
* avoid — the URI itself is what's in question. An oversized `state` is
|
|
99
|
+
* included in this tier too, for a narrower reason: reflecting it back
|
|
100
|
+
* into the error redirect it would otherwise trigger recreates the
|
|
101
|
+
* header-size DoS the cap exists to prevent, on the one input the cap
|
|
102
|
+
* can't safely echo.
|
|
103
|
+
*/
|
|
104
|
+
const firstPreTrustError = (callback, boundRedirectUris, options) => {
|
|
105
|
+
if (!redirectAllowed(callback.redirectUri, boundRedirectUris, options.redirect)) {
|
|
106
|
+
return oauthError(OAUTH_ERRORS.invalidRequest, 400, "invalid redirect_uri");
|
|
107
|
+
}
|
|
108
|
+
if (callback.state.length > MAX_STATE_LENGTH) {
|
|
109
|
+
return oauthError(OAUTH_ERRORS.invalidRequest, 400, `state must be at most ${MAX_STATE_LENGTH} characters`);
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
};
|
|
113
|
+
/**
|
|
114
|
+
* Post-trust tier: `redirect_uri` is now trusted, so every remaining error
|
|
115
|
+
* goes back to the client's own callback per RFC 6749 §4.1.2.1 instead of a
|
|
116
|
+
* bare JSON body the connector never parses.
|
|
117
|
+
*/
|
|
118
|
+
const firstPostTrustError = ({ url, callback, expectedResource }, options) => {
|
|
119
|
+
const redirectError = (code, description) => errorRedirect(callback, code, description);
|
|
120
|
+
const responseType = url.searchParams.get("response_type") ?? "";
|
|
121
|
+
if (responseType !== "code") {
|
|
122
|
+
return redirectError(OAUTH_ERRORS.unsupportedResponseType, "response_type must be code");
|
|
123
|
+
}
|
|
124
|
+
const codeChallenge = url.searchParams.get("code_challenge") ?? "";
|
|
125
|
+
// RFC 7636 defaults an absent method to `plain`; this AS only supports
|
|
126
|
+
// S256, so an absent method is read as S256 for client compatibility —
|
|
127
|
+
// a plain-style challenge then fails the shape check below anyway.
|
|
128
|
+
const challengeMethod = url.searchParams.get("code_challenge_method") ?? "S256";
|
|
129
|
+
if (!codeChallenge || challengeMethod !== "S256") {
|
|
130
|
+
return redirectError(OAUTH_ERRORS.invalidRequest, "PKCE S256 code_challenge required");
|
|
131
|
+
}
|
|
132
|
+
if (!S256_CHALLENGE.test(codeChallenge)) {
|
|
133
|
+
return redirectError(OAUTH_ERRORS.invalidRequest, "code_challenge must be a base64url SHA-256 digest (43 characters)");
|
|
134
|
+
}
|
|
135
|
+
const resourceIssue = resourceErrorInfo(url.searchParams.get("resource") ?? "", expectedResource);
|
|
136
|
+
if (resourceIssue)
|
|
137
|
+
return redirectError(resourceIssue.code, resourceIssue.description);
|
|
138
|
+
const advertised = advertisedScopes(options);
|
|
139
|
+
const granted = requestedScopes(url.searchParams.get("scope") ?? "", defaultScopes(options));
|
|
140
|
+
const scopeIssue = scopeErrorInfo(granted, advertised);
|
|
141
|
+
if (scopeIssue)
|
|
142
|
+
return redirectError(scopeIssue.code, scopeIssue.description);
|
|
143
|
+
return { codeChallenge, granted };
|
|
144
|
+
};
|
|
145
|
+
/** Pre-approved clients skip straight to a code; everyone else gets a consent ticket. */
|
|
146
|
+
const issueAuthorizeOutcome = async (options, secrets, { context, clientId, registered, user, codeChallenge, granted }) => {
|
|
147
|
+
const { callback, expectedResource } = context;
|
|
148
|
+
const grant = {
|
|
149
|
+
clientId,
|
|
150
|
+
redirectUri: callback.redirectUri,
|
|
151
|
+
codeChallenge,
|
|
152
|
+
userId: user.id,
|
|
153
|
+
resource: expectedResource,
|
|
154
|
+
scope: formatScope(granted),
|
|
155
|
+
};
|
|
156
|
+
if (isPreApproved(options, clientId, registered)) {
|
|
157
|
+
return codeRedirect(callback, await issueAuthCode(secrets[0], grant));
|
|
158
|
+
}
|
|
159
|
+
const ticket = await issueConsentTicket(secrets[0], { ...grant, state: callback.state });
|
|
160
|
+
return renderConsent(options, {
|
|
161
|
+
clientId,
|
|
162
|
+
registeredClient: registered,
|
|
163
|
+
redirectUri: callback.redirectUri,
|
|
164
|
+
scope: granted,
|
|
165
|
+
resource: expectedResource,
|
|
166
|
+
user,
|
|
167
|
+
ticket,
|
|
168
|
+
oauthPath: options.oauthPath,
|
|
169
|
+
});
|
|
170
|
+
};
|
|
171
|
+
export const handleAuthorize = async (request, options) => {
|
|
172
|
+
const methodError = requireHttpMethod(request, GET_ONLY);
|
|
173
|
+
if (methodError)
|
|
174
|
+
return methodError;
|
|
175
|
+
const url = new URL(request.url);
|
|
176
|
+
const expectedResource = canonicalResource(url.origin, options.resourcePath);
|
|
177
|
+
const clientId = url.searchParams.get("client_id") ?? "";
|
|
178
|
+
if (!clientId) {
|
|
179
|
+
return oauthError(OAUTH_ERRORS.invalidRequest, 400, "client_id required");
|
|
180
|
+
}
|
|
181
|
+
const secrets = await resolveSecrets(options.ports, request);
|
|
182
|
+
const clientResolution = await resolveAuthorizeClient(options, clientId, secrets);
|
|
183
|
+
if (clientResolution instanceof Response)
|
|
184
|
+
return clientResolution;
|
|
185
|
+
const { registered, boundRedirectUris } = clientResolution;
|
|
186
|
+
const callback = {
|
|
187
|
+
redirectUri: url.searchParams.get("redirect_uri") ?? "",
|
|
188
|
+
state: url.searchParams.get("state") ?? "",
|
|
189
|
+
issuer: url.origin,
|
|
190
|
+
};
|
|
191
|
+
const preTrustError = firstPreTrustError(callback, boundRedirectUris, options);
|
|
192
|
+
if (preTrustError)
|
|
193
|
+
return preTrustError;
|
|
194
|
+
const context = { url, callback, expectedResource };
|
|
195
|
+
const validated = firstPostTrustError(context, options);
|
|
196
|
+
if (validated instanceof Response)
|
|
197
|
+
return validated;
|
|
198
|
+
const user = await options.ports.resolveUser(request);
|
|
199
|
+
if (!user)
|
|
200
|
+
return redirectToLogin(request, url, options);
|
|
201
|
+
return issueAuthorizeOutcome(options, secrets, {
|
|
202
|
+
...validated,
|
|
203
|
+
context,
|
|
204
|
+
clientId,
|
|
205
|
+
registered,
|
|
206
|
+
user,
|
|
207
|
+
});
|
|
208
|
+
};
|
|
209
|
+
//# sourceMappingURL=authorize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authorize.js","sourceRoot":"","sources":["../../../src/oauth/endpoints/authorize.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Responses that send the user agent back to the client's own, already
|
|
3
|
+
* validated `redirect_uri` — the code on success, the error otherwise
|
|
4
|
+
* (RFC 6749 §4.1.2 / §4.1.2.1), with `iss` per RFC 9207.
|
|
5
|
+
*/
|
|
6
|
+
/** The validated client callback one authorize/consent round trip returns to. */
|
|
7
|
+
export type Callback = {
|
|
8
|
+
redirectUri: string;
|
|
9
|
+
state: string;
|
|
10
|
+
issuer: string;
|
|
11
|
+
};
|
|
12
|
+
export declare const secureRedirect: (location: string) => Response;
|
|
13
|
+
export declare const codeRedirect: (callback: Callback, code: string) => Response;
|
|
14
|
+
export declare const errorRedirect: (callback: Callback, error: string, description?: string) => Response;
|
|
15
|
+
/**
|
|
16
|
+
* URL form of `errorRedirect`, for hosts composing their own authorize flow
|
|
17
|
+
* (`mcp-trellis/advanced`). `issuer`, when set, adds `iss` (RFC 9207).
|
|
18
|
+
*/
|
|
19
|
+
export declare const buildErrorRedirectUrl: (redirectUri: string, error: string, description: string, state: string, issuer?: string) => string;
|
|
20
|
+
//# sourceMappingURL=callback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"callback.d.ts","sourceRoot":"","sources":["../../../src/oauth/endpoints/callback.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,iFAAiF;AACjF,MAAM,MAAM,QAAQ,GAAG;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,UAAU,MAAM,KAAG,QAQ9C,CAAC;AAeL,eAAO,MAAM,YAAY,GAAI,UAAU,QAAQ,EAAE,MAAM,MAAM,KAAG,QACf,CAAC;AAElD,eAAO,MAAM,aAAa,GAAI,UAAU,QAAQ,EAAE,OAAO,MAAM,EAAE,oBAAgB,KAAG,QACF,CAAC;AAEnF;;;GAGG;AACH,eAAO,MAAM,qBAAqB,GAChC,aAAa,MAAM,EACnB,OAAO,MAAM,EACb,aAAa,MAAM,EACnB,OAAO,MAAM,EACb,SAAS,MAAM,KACd,MAAgG,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Responses that send the user agent back to the client's own, already
|
|
3
|
+
* validated `redirect_uri` — the code on success, the error otherwise
|
|
4
|
+
* (RFC 6749 §4.1.2 / §4.1.2.1), with `iss` per RFC 9207.
|
|
5
|
+
*/
|
|
6
|
+
export const secureRedirect = (location) => new Response(null, {
|
|
7
|
+
status: 302,
|
|
8
|
+
headers: {
|
|
9
|
+
Location: location,
|
|
10
|
+
"Referrer-Policy": "no-referrer",
|
|
11
|
+
"Cache-Control": "no-store",
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
/** Empty values are omitted, and `state` / `iss` always come last. */
|
|
15
|
+
const callbackUrl = (callback, params) => {
|
|
16
|
+
const target = new URL(callback.redirectUri);
|
|
17
|
+
const entries = [...Object.entries(params), ["state", callback.state], ["iss", callback.issuer]];
|
|
18
|
+
for (const [key, value] of entries) {
|
|
19
|
+
if (key && value)
|
|
20
|
+
target.searchParams.set(key, value);
|
|
21
|
+
}
|
|
22
|
+
return target.toString();
|
|
23
|
+
};
|
|
24
|
+
export const codeRedirect = (callback, code) => secureRedirect(callbackUrl(callback, { code }));
|
|
25
|
+
export const errorRedirect = (callback, error, description = "") => secureRedirect(callbackUrl(callback, { error, error_description: description }));
|
|
26
|
+
/**
|
|
27
|
+
* URL form of `errorRedirect`, for hosts composing their own authorize flow
|
|
28
|
+
* (`mcp-trellis/advanced`). `issuer`, when set, adds `iss` (RFC 9207).
|
|
29
|
+
*/
|
|
30
|
+
export const buildErrorRedirectUrl = (redirectUri, error, description, state, issuer) => callbackUrl({ redirectUri, state, issuer }, { error, error_description: description });
|
|
31
|
+
//# sourceMappingURL=callback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"callback.js","sourceRoot":"","sources":["../../../src/oauth/endpoints/callback.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AASH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,QAAgB,EAAY,EAAE,CAC3D,IAAI,QAAQ,CAAC,IAAI,EAAE;IACjB,MAAM,EAAE,GAAG;IACX,OAAO,EAAE;QACP,QAAQ,EAAE,QAAQ;QAClB,iBAAiB,EAAE,aAAa;QAChC,eAAe,EAAE,UAAU;KAC5B;CACF,CAAC,CAAC;AAEL,sEAAsE;AACtE,MAAM,WAAW,GAAG,CAClB,QAA6E,EAC7E,MAA8B,EACtB,EAAE;IACV,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IACjG,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,OAAO,EAAE,CAAC;QACnC,IAAI,GAAG,IAAI,KAAK;YAAE,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,MAAM,CAAC,QAAQ,EAAE,CAAC;AAC3B,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,QAAkB,EAAE,IAAY,EAAY,EAAE,CACzE,cAAc,CAAC,WAAW,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AAElD,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,QAAkB,EAAE,KAAa,EAAE,WAAW,GAAG,EAAE,EAAY,EAAE,CAC7F,cAAc,CAAC,WAAW,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC;AAEnF;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACnC,WAAmB,EACnB,KAAa,EACb,WAAmB,EACnB,KAAa,EACb,MAAe,EACP,EAAE,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,CAAC,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** The built-in consent interstitial, used when the host supplies no `consent.render`. */
|
|
2
|
+
import type { ConsentRequest } from "../types.js";
|
|
3
|
+
/** Built-in hardened interstitial — used whenever the host doesn't supply `consent.render`. */
|
|
4
|
+
export declare const renderBuiltInConsent: (input: ConsentRequest) => Response;
|
|
5
|
+
//# sourceMappingURL=consent-page.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consent-page.d.ts","sourceRoot":"","sources":["../../../src/oauth/endpoints/consent-page.ts"],"names":[],"mappings":"AAAA,0FAA0F;AAE1F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AA+BlD,+FAA+F;AAC/F,eAAO,MAAM,oBAAoB,GAAI,OAAO,cAAc,KAAG,QAmC5D,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/** The built-in consent interstitial, used when the host supplies no `consent.render`. */
|
|
2
|
+
const ESCAPE_HTML = {
|
|
3
|
+
"&": "&",
|
|
4
|
+
"<": "<",
|
|
5
|
+
">": ">",
|
|
6
|
+
'"': """,
|
|
7
|
+
"'": "'",
|
|
8
|
+
};
|
|
9
|
+
const escapeHtml = (value) => value.replace(/[&<>"']/g, (ch) => ESCAPE_HTML[ch] ?? ch);
|
|
10
|
+
/**
|
|
11
|
+
* `form-action` governs where a form (and the redirect chain it triggers)
|
|
12
|
+
* may end up — not just the immediate POST target. Chromium enforces it on
|
|
13
|
+
* the 302 this page's own submission produces, so `'self'` alone blocks the
|
|
14
|
+
* approve/deny POST to `${oauthPath}/consent` from ever reaching a
|
|
15
|
+
* `redirectUri` on another origin (the common case — Claude, Gemini,
|
|
16
|
+
* loopback clients). `redirectUri` is already validated by `authorize.ts`
|
|
17
|
+
* before this renders, so its origin is safe to add here.
|
|
18
|
+
*/
|
|
19
|
+
const consentSecurityHeaders = (redirectUri) => ({
|
|
20
|
+
"Content-Security-Policy": `default-src 'none'; style-src 'unsafe-inline'; ` +
|
|
21
|
+
`form-action 'self' ${new URL(redirectUri).origin}; frame-ancestors 'none'`,
|
|
22
|
+
"X-Frame-Options": "DENY",
|
|
23
|
+
"Referrer-Policy": "no-referrer",
|
|
24
|
+
"Cache-Control": "no-store",
|
|
25
|
+
});
|
|
26
|
+
/** Built-in hardened interstitial — used whenever the host doesn't supply `consent.render`. */
|
|
27
|
+
export const renderBuiltInConsent = (input) => {
|
|
28
|
+
const html = `<!doctype html>
|
|
29
|
+
<html lang="en">
|
|
30
|
+
<head>
|
|
31
|
+
<meta charset="utf-8">
|
|
32
|
+
<title>Authorize access</title>
|
|
33
|
+
<style>
|
|
34
|
+
body { font: 16px system-ui, sans-serif; max-width: 32rem; margin: 3rem auto; padding: 0 1rem; }
|
|
35
|
+
dl { display: grid; grid-template-columns: auto 1fr; gap: 0.25rem 1rem; }
|
|
36
|
+
dt { font-weight: 600; }
|
|
37
|
+
button { font-size: 1rem; padding: 0.5rem 1.25rem; margin-right: 0.5rem; }
|
|
38
|
+
</style>
|
|
39
|
+
</head>
|
|
40
|
+
<body>
|
|
41
|
+
<h1>Authorize access</h1>
|
|
42
|
+
<p><strong>${escapeHtml(input.clientId)}</strong> is requesting access to your account.</p>
|
|
43
|
+
<dl>
|
|
44
|
+
<dt>Redirect URI</dt><dd>${escapeHtml(input.redirectUri)}</dd>
|
|
45
|
+
<dt>Scope</dt><dd>${escapeHtml(input.scope.join(" ") || "(none)")}</dd>
|
|
46
|
+
<dt>Resource</dt><dd>${escapeHtml(input.resource)}</dd>
|
|
47
|
+
</dl>
|
|
48
|
+
<form method="POST" action="${escapeHtml(input.oauthPath)}/consent">
|
|
49
|
+
<input type="hidden" name="consent_ticket" value="${escapeHtml(input.ticket)}">
|
|
50
|
+
<button type="submit" name="approved" value="true">Allow</button>
|
|
51
|
+
<button type="submit" name="approved" value="false">Deny</button>
|
|
52
|
+
</form>
|
|
53
|
+
</body>
|
|
54
|
+
</html>`;
|
|
55
|
+
return new Response(html, {
|
|
56
|
+
status: 200,
|
|
57
|
+
headers: {
|
|
58
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
59
|
+
...consentSecurityHeaders(input.redirectUri),
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
//# sourceMappingURL=consent-page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consent-page.js","sourceRoot":"","sources":["../../../src/oauth/endpoints/consent-page.ts"],"names":[],"mappings":"AAAA,0FAA0F;AAI1F,MAAM,WAAW,GAA2B;IAC1C,GAAG,EAAE,OAAO;IACZ,GAAG,EAAE,MAAM;IACX,GAAG,EAAE,MAAM;IACX,GAAG,EAAE,QAAQ;IACb,GAAG,EAAE,OAAO;CACb,CAAC;AAEF,MAAM,UAAU,GAAG,CAAC,KAAa,EAAU,EAAE,CAC3C,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;AAE3D;;;;;;;;GAQG;AACH,MAAM,sBAAsB,GAAG,CAAC,WAAmB,EAA0B,EAAE,CAAC,CAAC;IAC/E,yBAAyB,EACvB,iDAAiD;QACjD,sBAAsB,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC,MAAM,0BAA0B;IAC7E,iBAAiB,EAAE,MAAM;IACzB,iBAAiB,EAAE,aAAa;IAChC,eAAe,EAAE,UAAU;CAC5B,CAAC,CAAC;AAEH,+FAA+F;AAC/F,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,KAAqB,EAAY,EAAE;IACtE,MAAM,IAAI,GAAG;;;;;;;;;;;;;;aAcF,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC;;6BAEV,UAAU,CAAC,KAAK,CAAC,WAAW,CAAC;sBACpC,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC;yBAC1C,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC;;8BAErB,UAAU,CAAC,KAAK,CAAC,SAAS,CAAC;sDACH,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC;;;;;QAKtE,CAAC;IACP,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE;QACxB,MAAM,EAAE,GAAG;QACX,OAAO,EAAE;YACP,cAAc,EAAE,0BAA0B;YAC1C,GAAG,sBAAsB,CAAC,KAAK,CAAC,WAAW,CAAC;SAC7C;KACF,CAAC,CAAC;AACL,CAAC,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The consent step between a resolved user and code issuance (RFC 6749
|
|
3
|
+
* §4.1.1 "the resource owner grants or denies the ... request").
|
|
4
|
+
*
|
|
5
|
+
* `/authorize` seals everything the eventual `/consent` POST needs into an
|
|
6
|
+
* opaque, single-use ticket — the ticket carries `clientId`, `redirectUri`,
|
|
7
|
+
* `codeChallenge`, `resource`, and `scope` from the *validated* request, so
|
|
8
|
+
* `handleConsent` trusts nothing from the POST body except which ticket and
|
|
9
|
+
* whether the user approved it. Nothing else in that body is read.
|
|
10
|
+
*/
|
|
11
|
+
import type { ConsentRequest, OAuthRouterOptions, RegisteredClient, ResolvedOAuthRouterOptions } from "../types.js";
|
|
12
|
+
type ConsentTicketPayload = {
|
|
13
|
+
clientId: string;
|
|
14
|
+
redirectUri: string;
|
|
15
|
+
codeChallenge: string;
|
|
16
|
+
userId: string;
|
|
17
|
+
resource: string;
|
|
18
|
+
scope: string;
|
|
19
|
+
state: string;
|
|
20
|
+
exp: number;
|
|
21
|
+
jti: string;
|
|
22
|
+
};
|
|
23
|
+
export declare const issueConsentTicket: (secret: string, payload: Omit<ConsentTicketPayload, "exp" | "jti">) => Promise<string>;
|
|
24
|
+
export declare const renderConsent: (options: OAuthRouterOptions, input: ConsentRequest) => Promise<Response>;
|
|
25
|
+
/** Only honored when `clientStore` actually resolves the id — never a self-invented one. */
|
|
26
|
+
export declare const isPreApproved: (options: OAuthRouterOptions, clientId: string, registered: RegisteredClient | null) => boolean;
|
|
27
|
+
export declare const handleConsent: (request: Request, options: ResolvedOAuthRouterOptions) => Promise<Response>;
|
|
28
|
+
export {};
|
|
29
|
+
//# sourceMappingURL=consent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consent.d.ts","sourceRoot":"","sources":["../../../src/oauth/endpoints/consent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AASH,OAAO,KAAK,EACV,cAAc,EACd,kBAAkB,EAElB,gBAAgB,EAChB,0BAA0B,EAC3B,MAAM,aAAa,CAAC;AAMrB,KAAK,oBAAoB,GAAG;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,MAAM,EACd,SAAS,IAAI,CAAC,oBAAoB,EAAE,KAAK,GAAG,KAAK,CAAC,KACjD,OAAO,CAAC,MAAM,CAKkB,CAAC;AAEpC,eAAO,MAAM,aAAa,GACxB,SAAS,kBAAkB,EAC3B,OAAO,cAAc,KACpB,OAAO,CAAC,QAAQ,CAA6D,CAAC;AAEjF,4FAA4F;AAC5F,eAAO,MAAM,aAAa,GACxB,SAAS,kBAAkB,EAC3B,UAAU,MAAM,EAChB,YAAY,gBAAgB,GAAG,IAAI,KAClC,OAC0F,CAAC;AAyD9F,eAAO,MAAM,aAAa,GACxB,SAAS,OAAO,EAChB,SAAS,0BAA0B,KAClC,OAAO,CAAC,QAAQ,CAmClB,CAAC"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The consent step between a resolved user and code issuance (RFC 6749
|
|
3
|
+
* §4.1.1 "the resource owner grants or denies the ... request").
|
|
4
|
+
*
|
|
5
|
+
* `/authorize` seals everything the eventual `/consent` POST needs into an
|
|
6
|
+
* opaque, single-use ticket — the ticket carries `clientId`, `redirectUri`,
|
|
7
|
+
* `codeChallenge`, `resource`, and `scope` from the *validated* request, so
|
|
8
|
+
* `handleConsent` trusts nothing from the POST body except which ticket and
|
|
9
|
+
* whether the user approved it. Nothing else in that body is read.
|
|
10
|
+
*/
|
|
11
|
+
import { NO_CORS, POST_ONLY, requireHttpMethod } from "../../http/http.js";
|
|
12
|
+
import { auditLegacyKeyUsage } from "../audit.js";
|
|
13
|
+
import { oauthError, resolveSecrets } from "../config.js";
|
|
14
|
+
import { OAUTH_ERRORS } from "../constants.js";
|
|
15
|
+
import { issueAuthCode } from "../crypto/codes.js";
|
|
16
|
+
import { seal, unsealAny } from "../crypto/sealed.js";
|
|
17
|
+
import { readBodyOrError, readOAuthBody } from "../policy/reqbody.js";
|
|
18
|
+
import { codeRedirect, errorRedirect } from "./callback.js";
|
|
19
|
+
import { renderBuiltInConsent } from "./consent-page.js";
|
|
20
|
+
const CONSENT_TICKET_TTL_MS = 300_000;
|
|
21
|
+
export const issueConsentTicket = (secret, payload) => seal(secret, "consent", {
|
|
22
|
+
...payload,
|
|
23
|
+
exp: Date.now() + CONSENT_TICKET_TTL_MS,
|
|
24
|
+
jti: crypto.randomUUID(),
|
|
25
|
+
});
|
|
26
|
+
export const renderConsent = async (options, input) => (options.consent?.render ?? renderBuiltInConsent)(input);
|
|
27
|
+
/** Only honored when `clientStore` actually resolves the id — never a self-invented one. */
|
|
28
|
+
export const isPreApproved = (options, clientId, registered) => Boolean(registered) && (options.consent?.preApprovedClientIds?.includes(clientId) ?? false);
|
|
29
|
+
/** Unseals `ticket`, checks its expiry/jti shape, and audits a legacy-key unseal. */
|
|
30
|
+
const readValidTicket = async (request, options, ticket) => {
|
|
31
|
+
const secrets = await resolveSecrets(options.ports, request);
|
|
32
|
+
const unsealed = await unsealAny(secrets, "consent", ticket);
|
|
33
|
+
const payload = unsealed?.value ?? null;
|
|
34
|
+
if (!payload ||
|
|
35
|
+
typeof payload.exp !== "number" ||
|
|
36
|
+
payload.exp < Date.now() ||
|
|
37
|
+
typeof payload.jti !== "string" ||
|
|
38
|
+
!payload.jti) {
|
|
39
|
+
return oauthError(OAUTH_ERRORS.invalidGrant, 400, "invalid or expired consent_ticket");
|
|
40
|
+
}
|
|
41
|
+
await auditLegacyKeyUsage(options, {
|
|
42
|
+
clientId: payload.clientId,
|
|
43
|
+
keyIndex: unsealed?.keyIndex,
|
|
44
|
+
source: "consent ticket unsealed",
|
|
45
|
+
});
|
|
46
|
+
return { payload, secrets };
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* The resolved user, once the ticket is confirmed as theirs and burned.
|
|
50
|
+
* Consumed only after the user check — a POST from the wrong session must
|
|
51
|
+
* not burn the rightful user's ticket — but before either outcome (code or
|
|
52
|
+
* denial), so single-use holds. Auth codes share this store; the `ct:`
|
|
53
|
+
* prefix keeps a ticket's jti apart from a code's.
|
|
54
|
+
*/
|
|
55
|
+
const consumeTicketForUser = async (request, options, payload) => {
|
|
56
|
+
const user = await options.ports.resolveUser(request);
|
|
57
|
+
if (!user || user.id !== payload.userId) {
|
|
58
|
+
return oauthError(OAUTH_ERRORS.invalidGrant, 400, "consent_ticket does not match the current user");
|
|
59
|
+
}
|
|
60
|
+
const fresh = await options.ports.codeStore.consume(`ct:${payload.jti}`, payload.exp);
|
|
61
|
+
return fresh ? user : oauthError(OAUTH_ERRORS.invalidGrant, 400, "consent_ticket already used");
|
|
62
|
+
};
|
|
63
|
+
export const handleConsent = async (request, options) => {
|
|
64
|
+
const methodError = requireHttpMethod(request, POST_ONLY, NO_CORS);
|
|
65
|
+
if (methodError)
|
|
66
|
+
return methodError;
|
|
67
|
+
const body = await readBodyOrError(() => readOAuthBody(request));
|
|
68
|
+
if (body instanceof Response)
|
|
69
|
+
return body;
|
|
70
|
+
const ticket = body.consent_ticket ?? "";
|
|
71
|
+
if (!ticket) {
|
|
72
|
+
return oauthError(OAUTH_ERRORS.invalidRequest, 400, "consent_ticket required");
|
|
73
|
+
}
|
|
74
|
+
const resolved = await readValidTicket(request, options, ticket);
|
|
75
|
+
if (resolved instanceof Response)
|
|
76
|
+
return resolved;
|
|
77
|
+
const { payload, secrets } = resolved;
|
|
78
|
+
const user = await consumeTicketForUser(request, options, payload);
|
|
79
|
+
if (user instanceof Response)
|
|
80
|
+
return user;
|
|
81
|
+
const callback = {
|
|
82
|
+
redirectUri: payload.redirectUri,
|
|
83
|
+
state: payload.state,
|
|
84
|
+
issuer: new URL(request.url).origin,
|
|
85
|
+
};
|
|
86
|
+
if (body.approved !== "true")
|
|
87
|
+
return errorRedirect(callback, OAUTH_ERRORS.accessDenied);
|
|
88
|
+
const code = await issueAuthCode(secrets[0], {
|
|
89
|
+
clientId: payload.clientId,
|
|
90
|
+
redirectUri: payload.redirectUri,
|
|
91
|
+
codeChallenge: payload.codeChallenge,
|
|
92
|
+
userId: user.id,
|
|
93
|
+
resource: payload.resource,
|
|
94
|
+
scope: payload.scope,
|
|
95
|
+
});
|
|
96
|
+
return codeRedirect(callback, code);
|
|
97
|
+
};
|
|
98
|
+
//# sourceMappingURL=consent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consent.js","sourceRoot":"","sources":["../../../src/oauth/endpoints/consent.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type GrantType, type TokenEndpointAuthMethod } from "
|
|
2
|
-
export type { GrantType } from "
|
|
1
|
+
import { type GrantType, type TokenEndpointAuthMethod } from "../constants.js";
|
|
2
|
+
export type { GrantType } from "../constants.js";
|
|
3
3
|
export type ProtectedResourceMetadata = {
|
|
4
4
|
resource: string;
|
|
5
5
|
authorization_servers: string[];
|
|
@@ -22,6 +22,10 @@ export type AuthorizationServerMetadata = {
|
|
|
22
22
|
/** Omitted when `revokeToken` is not configured. */
|
|
23
23
|
revocation_endpoint?: string;
|
|
24
24
|
revocation_endpoint_auth_methods_supported?: TokenEndpointAuthMethod[];
|
|
25
|
+
/** RFC 9207 — authorize redirect includes `iss`. */
|
|
26
|
+
authorization_response_iss_parameter_supported?: boolean;
|
|
27
|
+
/** Client ID Metadata Documents (MCP 2026-07-28). */
|
|
28
|
+
client_id_metadata_document_supported?: boolean;
|
|
25
29
|
};
|
|
26
30
|
export type MetadataOptions = {
|
|
27
31
|
origin: string;
|
|
@@ -38,6 +42,8 @@ export type MetadataOptions = {
|
|
|
38
42
|
dcrEnabled?: boolean | undefined;
|
|
39
43
|
/** Advertise `revocation_endpoint`. Default false. */
|
|
40
44
|
revocationEnabled?: boolean | undefined;
|
|
45
|
+
/** Advertise CIMD support. Default false. */
|
|
46
|
+
cimdSupported?: boolean | undefined;
|
|
41
47
|
};
|
|
42
48
|
export declare const protectedResourceMetadata: (options: MetadataOptions) => ProtectedResourceMetadata;
|
|
43
49
|
export declare const authorizationServerMetadata: (options: MetadataOptions) => AuthorizationServerMetadata;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../../../src/oauth/endpoints/metadata.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,SAAS,EAEd,KAAK,uBAAuB,EAC7B,MAAM,iBAAiB,CAAC;AAGzB,YAAY,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,MAAM,MAAM,yBAAyB,GAAG;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,wBAAwB,EAAE,MAAM,EAAE,CAAC;IACnC,gBAAgB,EAAE,MAAM,EAAE,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,sBAAsB,EAAE,MAAM,CAAC;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,+EAA+E;IAC/E,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,wBAAwB,EAAE,MAAM,EAAE,CAAC;IACnC,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,gCAAgC,EAAE,MAAM,EAAE,CAAC;IAC3C,qCAAqC,EAAE,uBAAuB,EAAE,CAAC;IACjE,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,kEAAkE;IAClE,4BAA4B,EAAE,OAAO,CAAC;IACtC,oDAAoD;IACpD,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,0CAA0C,CAAC,EAAE,uBAAuB,EAAE,CAAC;IACvE,oDAAoD;IACpD,8CAA8C,CAAC,EAAE,OAAO,CAAC;IACzD,qDAAqD;IACrD,qCAAqC,CAAC,EAAE,OAAO,CAAC;CACjD,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,4CAA4C;IAC5C,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,uDAAuD;IACvD,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC9B,uEAAuE;IACvE,wBAAwB,CAAC,EAAE,uBAAuB,EAAE,GAAG,SAAS,CAAC;IACjE,uDAAuD;IACvD,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,sDAAsD;IACtD,iBAAiB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACxC,6CAA6C;IAC7C,aAAa,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACrC,CAAC;AAEF,eAAO,MAAM,yBAAyB,GAAI,SAAS,eAAe,KAAG,yBAKnE,CAAC;AAEH,eAAO,MAAM,2BAA2B,GACtC,SAAS,eAAe,KACvB,2BA2BF,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,MAAM,EACd,qBAAoC,EACpC,cAAa,KACZ,MAIC,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { wwwAuthenticateHeader } from "../../auth/bearer.js";
|
|
2
|
+
import { DEFAULT_OAUTH_PATH, DEFAULT_SCOPE, TOKEN_ENDPOINT_AUTH_METHODS, } from "../constants.js";
|
|
3
|
+
import { canonicalResource, DEFAULT_RESOURCE_PATH } from "../policy/resource.js";
|
|
3
4
|
export const protectedResourceMetadata = (options) => ({
|
|
4
5
|
resource: canonicalResource(options.origin, options.resourcePath),
|
|
5
6
|
authorization_servers: [options.origin],
|
|
@@ -7,7 +8,7 @@ export const protectedResourceMetadata = (options) => ({
|
|
|
7
8
|
scopes_supported: options.scopes ?? [DEFAULT_SCOPE],
|
|
8
9
|
});
|
|
9
10
|
export const authorizationServerMetadata = (options) => {
|
|
10
|
-
const oauthPath = options.oauthPath ??
|
|
11
|
+
const oauthPath = options.oauthPath ?? DEFAULT_OAUTH_PATH;
|
|
11
12
|
const tokenEndpointAuthMethods = options.tokenEndpointAuthMethods ?? [
|
|
12
13
|
TOKEN_ENDPOINT_AUTH_METHODS.none,
|
|
13
14
|
];
|
|
@@ -30,7 +31,12 @@ export const authorizationServerMetadata = (options) => {
|
|
|
30
31
|
token_endpoint_auth_methods_supported: tokenEndpointAuthMethods,
|
|
31
32
|
scopes_supported: options.scopes ?? [DEFAULT_SCOPE],
|
|
32
33
|
resource_parameter_supported: true,
|
|
34
|
+
authorization_response_iss_parameter_supported: true,
|
|
35
|
+
...(options.cimdSupported ? { client_id_metadata_document_supported: true } : {}),
|
|
33
36
|
};
|
|
34
37
|
};
|
|
35
|
-
export const mcpWwwAuthenticate = (origin, resourcePath = DEFAULT_RESOURCE_PATH, realm = "mcp") =>
|
|
38
|
+
export const mcpWwwAuthenticate = (origin, resourcePath = DEFAULT_RESOURCE_PATH, realm = "mcp") => wwwAuthenticateHeader({
|
|
39
|
+
realm,
|
|
40
|
+
resourceMetadataUrl: `${origin}/.well-known/oauth-protected-resource${resourcePath}`,
|
|
41
|
+
});
|
|
36
42
|
//# sourceMappingURL=metadata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../../src/oauth/endpoints/metadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EACL,kBAAkB,EAClB,aAAa,EAEb,2BAA2B,GAE5B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAoDjF,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,OAAwB,EAA6B,EAAE,CAAC,CAAC;IACjG,QAAQ,EAAE,iBAAiB,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,YAAY,CAAC;IACjE,qBAAqB,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC;IACvC,wBAAwB,EAAE,CAAC,QAAQ,CAAC;IACpC,gBAAgB,EAAE,OAAO,CAAC,MAAM,IAAI,CAAC,aAAa,CAAC;CACpD,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,2BAA2B,GAAG,CACzC,OAAwB,EACK,EAAE;IAC/B,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,kBAAkB,CAAC;IAC1D,MAAM,wBAAwB,GAAG,OAAO,CAAC,wBAAwB,IAAI;QACnE,2BAA2B,CAAC,IAAI;KACjC,CAAC;IACF,OAAO;QACL,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,sBAAsB,EAAE,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,YAAY;QACjE,cAAc,EAAE,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,QAAQ;QACrD,GAAG,CAAC,OAAO,CAAC,UAAU,KAAK,KAAK;YAC9B,CAAC,CAAC,EAAE,qBAAqB,EAAE,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,WAAW,EAAE;YACrE,CAAC,CAAC,EAAE,CAAC;QACP,GAAG,CAAC,OAAO,CAAC,iBAAiB;YAC3B,CAAC,CAAC;gBACE,mBAAmB,EAAE,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,SAAS;gBAC3D,0CAA0C,EAAE,wBAAwB;aACrE;YACH,CAAC,CAAC,EAAE,CAAC;QACP,wBAAwB,EAAE,CAAC,MAAM,CAAC;QAClC,qBAAqB,EAAE,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC;QAC9C,gCAAgC,EAAE,CAAC,MAAM,CAAC;QAC1C,qCAAqC,EAAE,wBAAwB;QAC/D,gBAAgB,EAAE,OAAO,CAAC,MAAM,IAAI,CAAC,aAAa,CAAC;QACnD,4BAA4B,EAAE,IAAI;QAClC,8CAA8C,EAAE,IAAI;QACpD,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,qCAAqC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAClF,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAChC,MAAc,EACd,YAAY,GAAG,qBAAqB,EACpC,KAAK,GAAG,KAAK,EACL,EAAE,CACV,qBAAqB,CAAC;IACpB,KAAK;IACL,mBAAmB,EAAE,GAAG,MAAM,wCAAwC,YAAY,EAAE;CACrF,CAAC,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RFC 7591 Dynamic Client Registration — `POST ${oauthPath}/register`.
|
|
3
|
+
*
|
|
4
|
+
* Zero storage: the issued `client_id` is a sealed assertion of the validated
|
|
5
|
+
* `redirect_uris` (see `sealed.ts`). Unauthenticated callers can still mint
|
|
6
|
+
* their own id; consent + `requireRegisteredClients` address impersonation.
|
|
7
|
+
*/
|
|
8
|
+
import type { OAuthRouterOptions } from "../types.js";
|
|
9
|
+
export declare const handleRegister: (request: Request, options: OAuthRouterOptions) => Promise<Response>;
|
|
10
|
+
//# sourceMappingURL=register.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../../src/oauth/endpoints/register.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAUH,OAAO,KAAK,EAAmB,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAsFvE,eAAO,MAAM,cAAc,GACzB,SAAS,OAAO,EAChB,SAAS,kBAAkB,KAC1B,OAAO,CAAC,QAAQ,CAgBlB,CAAC"}
|