mcp-trellis 0.2.1 → 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 +14 -5
- 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 -13
- 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 +6 -3
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Constant-time confidential-client secret hashing.
|
|
3
|
+
*
|
|
4
|
+
* `ClientStore.verifySecret` puts comparison in the host's hands by design
|
|
5
|
+
* — the library must never see or persist plaintext credentials. But that
|
|
6
|
+
* also means a host that writes `stored === presented` gets a timing oracle
|
|
7
|
+
* with no warning, while the library ships a perfectly good
|
|
8
|
+
* `timingSafeEqual` and uses it everywhere else it compares secrets
|
|
9
|
+
* (auth-code HMAC/seal, PKCE). `ClientStore.secretHash` is the alternative:
|
|
10
|
+
* the host still owns storage, but hands the library a stored hash instead
|
|
11
|
+
* of doing the comparison itself, and the library does the compare with
|
|
12
|
+
* the same primitive it already trusts.
|
|
13
|
+
*
|
|
14
|
+
* Keyed by `codeSecret`, HKDF-derived for this specific purpose — not a
|
|
15
|
+
* separate pepper the host has to generate, store, and keep in sync with
|
|
16
|
+
* hashing and verification. This project's whole pitch is "one secret";
|
|
17
|
+
* adding a second one just for client-secret hashing would undercut that
|
|
18
|
+
* for a feature only confidential clients (Gemini Enterprise, in practice)
|
|
19
|
+
* even use.
|
|
20
|
+
*
|
|
21
|
+
* HMAC-SHA256, not a slow password hash (PBKDF2/bcrypt/scrypt/argon2):
|
|
22
|
+
* client secrets are high-entropy, machine-generated values, not
|
|
23
|
+
* human-chosen passwords, so a fast keyed hash is cryptographically
|
|
24
|
+
* adequate here — and a slow hash would burn real CPU budget on every
|
|
25
|
+
* token exchange, on every runtime this library targets, including
|
|
26
|
+
* Cloudflare Workers' request-scoped CPU limits. If your client secrets
|
|
27
|
+
* are human-chosen, don't use this — do a real password hash inside your
|
|
28
|
+
* own `verifySecret` instead.
|
|
29
|
+
*/
|
|
30
|
+
/**
|
|
31
|
+
* Hash a client secret for storage in your `ClientStore.secretHash` lookup.
|
|
32
|
+
* Call this once, at registration time, with the same `codeSecret` value
|
|
33
|
+
* your `createOAuthRouter`/`createMcpApp` is configured with — store only
|
|
34
|
+
* the result, never the plaintext secret.
|
|
35
|
+
*/
|
|
36
|
+
export declare const hashClientSecret: (secret: string, codeSecretValue: string) => Promise<string>;
|
|
37
|
+
/**
|
|
38
|
+
* Verify a presented secret against a stored hash from `hashClientSecret`.
|
|
39
|
+
* Constant-time. Used internally by `firstClientAuthError` when
|
|
40
|
+
* `ClientStore.secretHash` is configured — exported for hosts that want
|
|
41
|
+
* the same check outside the token endpoint (e.g. a client-management API).
|
|
42
|
+
*/
|
|
43
|
+
export declare const verifyClientSecret: (presented: string, stored: string, codeSecretValue: string) => Promise<boolean>;
|
|
44
|
+
//# sourceMappingURL=secrethash.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrethash.d.ts","sourceRoot":"","sources":["../../src/oauth/secrethash.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AA8BH;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAC3B,QAAQ,MAAM,EACd,iBAAiB,MAAM,KACtB,OAAO,CAAC,MAAM,CAIhB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAC7B,WAAW,MAAM,EACjB,QAAQ,MAAM,EACd,iBAAiB,MAAM,KACtB,OAAO,CAAC,OAAO,CAIjB,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Constant-time confidential-client secret hashing.
|
|
3
|
+
*
|
|
4
|
+
* `ClientStore.verifySecret` puts comparison in the host's hands by design
|
|
5
|
+
* — the library must never see or persist plaintext credentials. But that
|
|
6
|
+
* also means a host that writes `stored === presented` gets a timing oracle
|
|
7
|
+
* with no warning, while the library ships a perfectly good
|
|
8
|
+
* `timingSafeEqual` and uses it everywhere else it compares secrets
|
|
9
|
+
* (auth-code HMAC/seal, PKCE). `ClientStore.secretHash` is the alternative:
|
|
10
|
+
* the host still owns storage, but hands the library a stored hash instead
|
|
11
|
+
* of doing the comparison itself, and the library does the compare with
|
|
12
|
+
* the same primitive it already trusts.
|
|
13
|
+
*
|
|
14
|
+
* Keyed by `codeSecret`, HKDF-derived for this specific purpose — not a
|
|
15
|
+
* separate pepper the host has to generate, store, and keep in sync with
|
|
16
|
+
* hashing and verification. This project's whole pitch is "one secret";
|
|
17
|
+
* adding a second one just for client-secret hashing would undercut that
|
|
18
|
+
* for a feature only confidential clients (Gemini Enterprise, in practice)
|
|
19
|
+
* even use.
|
|
20
|
+
*
|
|
21
|
+
* HMAC-SHA256, not a slow password hash (PBKDF2/bcrypt/scrypt/argon2):
|
|
22
|
+
* client secrets are high-entropy, machine-generated values, not
|
|
23
|
+
* human-chosen passwords, so a fast keyed hash is cryptographically
|
|
24
|
+
* adequate here — and a slow hash would burn real CPU budget on every
|
|
25
|
+
* token exchange, on every runtime this library targets, including
|
|
26
|
+
* Cloudflare Workers' request-scoped CPU limits. If your client secrets
|
|
27
|
+
* are human-chosen, don't use this — do a real password hash inside your
|
|
28
|
+
* own `verifySecret` instead.
|
|
29
|
+
*/
|
|
30
|
+
import { timingSafeEqual } from "../auth/bearer.js";
|
|
31
|
+
import { bytesToBase64Url } from "./base64url.js";
|
|
32
|
+
const HASH_PREFIX = "hmac-sha256$";
|
|
33
|
+
const HKDF_INFO = "mcp-trellis:client-secret-hash";
|
|
34
|
+
const deriveHmacKey = async (codeSecretValue) => {
|
|
35
|
+
const material = await crypto.subtle.importKey("raw", new TextEncoder().encode(codeSecretValue), "HKDF", false, ["deriveKey"]);
|
|
36
|
+
return crypto.subtle.deriveKey({
|
|
37
|
+
name: "HKDF",
|
|
38
|
+
hash: "SHA-256",
|
|
39
|
+
salt: new Uint8Array(0),
|
|
40
|
+
info: new TextEncoder().encode(HKDF_INFO),
|
|
41
|
+
}, material, { name: "HMAC", hash: "SHA-256", length: 256 }, false, ["sign"]);
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Hash a client secret for storage in your `ClientStore.secretHash` lookup.
|
|
45
|
+
* Call this once, at registration time, with the same `codeSecret` value
|
|
46
|
+
* your `createOAuthRouter`/`createMcpApp` is configured with — store only
|
|
47
|
+
* the result, never the plaintext secret.
|
|
48
|
+
*/
|
|
49
|
+
export const hashClientSecret = async (secret, codeSecretValue) => {
|
|
50
|
+
const key = await deriveHmacKey(codeSecretValue);
|
|
51
|
+
const sig = await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(secret));
|
|
52
|
+
return `${HASH_PREFIX}${bytesToBase64Url(sig)}`;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Verify a presented secret against a stored hash from `hashClientSecret`.
|
|
56
|
+
* Constant-time. Used internally by `firstClientAuthError` when
|
|
57
|
+
* `ClientStore.secretHash` is configured — exported for hosts that want
|
|
58
|
+
* the same check outside the token endpoint (e.g. a client-management API).
|
|
59
|
+
*/
|
|
60
|
+
export const verifyClientSecret = async (presented, stored, codeSecretValue) => {
|
|
61
|
+
if (!presented || !stored)
|
|
62
|
+
return false;
|
|
63
|
+
const recomputed = await hashClientSecret(presented, codeSecretValue);
|
|
64
|
+
return timingSafeEqual(recomputed, stored);
|
|
65
|
+
};
|
|
66
|
+
//# sourceMappingURL=secrethash.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrethash.js","sourceRoot":"","sources":["../../src/oauth/secrethash.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,MAAM,WAAW,GAAG,cAAc,CAAC;AACnC,MAAM,SAAS,GAAG,gCAAgC,CAAC;AAEnD,MAAM,aAAa,GAAG,KAAK,EAAE,eAAuB,EAAsB,EAAE;IAC1E,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CAC5C,KAAK,EACL,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,eAAe,CAAC,EACzC,MAAM,EACN,KAAK,EACL,CAAC,WAAW,CAAC,CACd,CAAC;IACF,OAAO,MAAM,CAAC,MAAM,CAAC,SAAS,CAC5B;QACE,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE,IAAI,UAAU,CAAC,CAAC,CAAC;QACvB,IAAI,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC;KAC1C,EACD,QAAQ,EACR,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,EAC9C,KAAK,EACL,CAAC,MAAM,CAAC,CACT,CAAC;AACJ,CAAC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,EACnC,MAAc,EACd,eAAuB,EACN,EAAE;IACnB,MAAM,GAAG,GAAG,MAAM,aAAa,CAAC,eAAe,CAAC,CAAC;IACjD,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IACpF,OAAO,GAAG,WAAW,GAAG,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC;AAClD,CAAC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,EACrC,SAAiB,EACjB,MAAc,EACd,eAAuB,EACL,EAAE;IACpB,IAAI,CAAC,SAAS,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IACxC,MAAM,UAAU,GAAG,MAAM,gBAAgB,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;IACtE,OAAO,eAAe,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;AAC7C,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token.d.ts","sourceRoot":"","sources":["../../src/oauth/token.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"token.d.ts","sourceRoot":"","sources":["../../src/oauth/token.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,kBAAkB,EAA4C,MAAM,YAAY,CAAC;AAuG/F,eAAO,MAAM,WAAW,GACtB,SAAS,OAAO,EAChB,SAAS,kBAAkB,KAC1B,OAAO,CAAC,QAAQ,CAuClB,CAAC"}
|
package/dist/oauth/token.js
CHANGED
|
@@ -1,35 +1,15 @@
|
|
|
1
|
+
import { BodyTooLargeError } from "../body.js";
|
|
1
2
|
import { requireHttpMethod } from "../http.js";
|
|
2
3
|
import { firstClientAuthError, readClientAuth } from "./clientauth.js";
|
|
3
4
|
import { consumeAuthCode } from "./codes.js";
|
|
4
5
|
import { GRANT_TYPES, OAUTH_ERRORS } from "./constants.js";
|
|
5
6
|
import { verifyPkceS256 } from "./pkce.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
7
|
+
import { readOAuthBody } from "./reqbody.js";
|
|
8
|
+
import { canonicalResource, firstResourceError, resourcesEqual } from "./resource.js";
|
|
9
|
+
import { oauthError, resolveSecret, tokenResponse } from "./types.js";
|
|
8
10
|
const POST_ONLY = new Set(["POST"]);
|
|
9
11
|
const NO_CORS = { cors: false };
|
|
10
|
-
const
|
|
11
|
-
const body = (await request.json());
|
|
12
|
-
if (!body || typeof body !== "object" || Array.isArray(body)) {
|
|
13
|
-
throw new Error("malformed request body");
|
|
14
|
-
}
|
|
15
|
-
return Object.fromEntries(Object.entries(body)
|
|
16
|
-
.filter(([, value]) => value != null)
|
|
17
|
-
.map(([key, value]) => [key, String(value)]));
|
|
18
|
-
};
|
|
19
|
-
const readFormBody = async (request) => {
|
|
20
|
-
const params = new URLSearchParams(await request.text());
|
|
21
|
-
return Object.fromEntries([...params.entries()]);
|
|
22
|
-
};
|
|
23
|
-
const BODY_READERS = [
|
|
24
|
-
{ match: (ctype) => ctype.includes("application/json"), read: readJsonBody },
|
|
25
|
-
{ match: () => true, read: readFormBody },
|
|
26
|
-
];
|
|
27
|
-
const readTokenBody = async (request) => {
|
|
28
|
-
const ctype = request.headers.get("Content-Type") ?? "";
|
|
29
|
-
const reader = BODY_READERS.find((entry) => entry.match(ctype))?.read ?? readFormBody;
|
|
30
|
-
return reader(request);
|
|
31
|
-
};
|
|
32
|
-
const handleRefresh = async ({ body, options, expectedResource, clientId, }) => {
|
|
12
|
+
const handleRefresh = async ({ body, options, expectedResource, clientId }) => {
|
|
33
13
|
if (!options.ports.refreshAccessToken) {
|
|
34
14
|
return oauthError(OAUTH_ERRORS.unsupportedGrantType, 400, "refresh_token not enabled");
|
|
35
15
|
}
|
|
@@ -62,18 +42,14 @@ const firstAuthCodeMismatch = (record, body, clientId) => {
|
|
|
62
42
|
description: "client_id mismatch",
|
|
63
43
|
},
|
|
64
44
|
].find((entry) => !entry.ok);
|
|
65
|
-
return rule
|
|
66
|
-
? oauthError(OAUTH_ERRORS.invalidGrant, 400, rule.description)
|
|
67
|
-
: null;
|
|
45
|
+
return rule ? oauthError(OAUTH_ERRORS.invalidGrant, 400, rule.description) : null;
|
|
68
46
|
};
|
|
69
47
|
const handleAuthCode = async ({ request, body, options, expectedResource, clientId, }) => {
|
|
70
48
|
const resourceError = firstResourceError(body.resource ?? "", expectedResource);
|
|
71
49
|
if (resourceError)
|
|
72
50
|
return resourceError;
|
|
73
51
|
const secret = await resolveSecret(options.ports, request);
|
|
74
|
-
const record = await consumeAuthCode(secret, body.code ?? "", {
|
|
75
|
-
codeStore: options.ports.codeStore,
|
|
76
|
-
});
|
|
52
|
+
const record = await consumeAuthCode(secret, body.code ?? "", options.ports.codeStore !== undefined ? { codeStore: options.ports.codeStore } : {});
|
|
77
53
|
if (!record) {
|
|
78
54
|
return oauthError(OAUTH_ERRORS.invalidGrant, 400, "invalid or expired code");
|
|
79
55
|
}
|
|
@@ -104,16 +80,19 @@ export const handleToken = async (request, options) => {
|
|
|
104
80
|
return methodError;
|
|
105
81
|
let body;
|
|
106
82
|
try {
|
|
107
|
-
body = await
|
|
83
|
+
body = await readOAuthBody(request);
|
|
108
84
|
}
|
|
109
|
-
catch {
|
|
85
|
+
catch (exc) {
|
|
86
|
+
if (exc instanceof BodyTooLargeError) {
|
|
87
|
+
return oauthError(OAUTH_ERRORS.invalidRequest, 413, "request body too large");
|
|
88
|
+
}
|
|
110
89
|
return oauthError(OAUTH_ERRORS.invalidRequest, 400, "malformed request body");
|
|
111
90
|
}
|
|
112
91
|
const auth = readClientAuth(request, body);
|
|
113
92
|
if (!auth) {
|
|
114
93
|
return oauthError(OAUTH_ERRORS.invalidRequest, 400, "client_id required");
|
|
115
94
|
}
|
|
116
|
-
const clientAuthError = await firstClientAuthError(auth, options);
|
|
95
|
+
const clientAuthError = await firstClientAuthError(auth, options, request);
|
|
117
96
|
if (clientAuthError)
|
|
118
97
|
return clientAuthError;
|
|
119
98
|
const url = new URL(request.url);
|
package/dist/oauth/token.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token.js","sourceRoot":"","sources":["../../src/oauth/token.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACvE,OAAO,
|
|
1
|
+
{"version":3,"file":"token.js","sourceRoot":"","sources":["../../src/oauth/token.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACvE,OAAO,EAAuB,eAAe,EAAE,MAAM,YAAY,CAAC;AAClE,OAAO,EAAE,WAAW,EAAkB,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC3E,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACtF,OAAO,EAA2B,UAAU,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE/F,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;AACpC,MAAM,OAAO,GAAG,EAAE,IAAI,EAAE,KAAK,EAAW,CAAC;AAazC,MAAM,aAAa,GAAiB,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,EAAE,EAAE;IAC1F,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,kBAAkB,EAAE,CAAC;QACtC,OAAO,UAAU,CAAC,YAAY,CAAC,oBAAoB,EAAE,GAAG,EAAE,2BAA2B,CAAC,CAAC;IACzF,CAAC;IACD,MAAM,YAAY,GAAG,IAAI,CAAC,aAAa,IAAI,EAAE,CAAC;IAC9C,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,OAAO,UAAU,CAAC,YAAY,CAAC,cAAc,EAAE,GAAG,EAAE,wBAAwB,CAAC,CAAC;IAChF,CAAC;IACD,MAAM,aAAa,GAAG,kBAAkB,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE,EAAE,gBAAgB,CAAC,CAAC;IAChF,IAAI,aAAa;QAAE,OAAO,aAAa,CAAC;IAExC,8FAA8F;IAC9F,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,kBAAkB,CAAC;QACpD,YAAY;QACZ,QAAQ;QACR,QAAQ,EAAE,gBAAgB;KAC3B,CAAC,CAAC;IACH,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,UAAU,CAAC,YAAY,CAAC,YAAY,EAAE,GAAG,EAAE,uBAAuB,CAAC,CAAC;IAC7E,CAAC;IACD,OAAO,aAAa,CAAC,MAAM,CAAC,CAAC;AAC/B,CAAC,CAAC;AAEF,MAAM,qBAAqB,GAAG,CAC5B,MAAsB,EACtB,IAA4B,EAC5B,QAAgB,EACC,EAAE;IACnB,MAAM,IAAI,GAAG;QACX;YACE,EAAE,EAAE,MAAM,CAAC,WAAW,KAAK,CAAC,IAAI,CAAC,YAAY,IAAI,EAAE,CAAC;YACpD,WAAW,EAAE,uBAAuB;SACrC;QACD;YACE,EAAE,EAAE,MAAM,CAAC,QAAQ,KAAK,QAAQ;YAChC,WAAW,EAAE,oBAAoB;SAClC;KACF,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAE7B,OAAO,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,YAAY,CAAC,YAAY,EAAE,GAAG,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACpF,CAAC,CAAC;AAEF,MAAM,cAAc,GAAiB,KAAK,EAAE,EAC1C,OAAO,EACP,IAAI,EACJ,OAAO,EACP,gBAAgB,EAChB,QAAQ,GACT,EAAE,EAAE;IACH,MAAM,aAAa,GAAG,kBAAkB,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE,EAAE,gBAAgB,CAAC,CAAC;IAChF,IAAI,aAAa;QAAE,OAAO,aAAa,CAAC;IAExC,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,MAAM,eAAe,CAClC,MAAM,EACN,IAAI,CAAC,IAAI,IAAI,EAAE,EACf,OAAO,CAAC,KAAK,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CACpF,CAAC;IACF,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,UAAU,CAAC,YAAY,CAAC,YAAY,EAAE,GAAG,EAAE,yBAAyB,CAAC,CAAC;IAC/E,CAAC;IAED,MAAM,QAAQ,GAAG,qBAAqB,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;IAC/D,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAE9B,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,QAAQ,EAAE,gBAAgB,CAAC,EAAE,CAAC;QACvD,OAAO,UAAU,CAAC,YAAY,CAAC,aAAa,EAAE,GAAG,EAAE,yCAAyC,CAAC,CAAC;IAChG,CAAC;IAED,IAAI,CAAC,CAAC,MAAM,cAAc,CAAC,IAAI,CAAC,aAAa,IAAI,EAAE,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC;QAC5E,OAAO,UAAU,CAAC,YAAY,CAAC,YAAY,EAAE,GAAG,EAAE,0BAA0B,CAAC,CAAC;IAChF,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC;QACjD,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,QAAQ;QACR,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,QAAQ,EAAE,MAAM,CAAC,QAAQ;KAC1B,CAAC,CAAC;IACH,OAAO,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;AAC7C,CAAC,CAAC;AAEF,MAAM,cAAc,GAAiC;IACnD,CAAC,WAAW,CAAC,iBAAiB,CAAC,EAAE,cAAc;IAC/C,CAAC,WAAW,CAAC,YAAY,CAAC,EAAE,aAAa;CAC1C,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAG,KAAK,EAC9B,OAAgB,EAChB,OAA2B,EACR,EAAE;IACrB,MAAM,WAAW,GAAG,iBAAiB,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IACnE,IAAI,WAAW;QAAE,OAAO,WAAW,CAAC;IAEpC,IAAI,IAA4B,CAAC;IACjC,IAAI,CAAC;QACH,IAAI,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,CAAC;IACtC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,iBAAiB,EAAE,CAAC;YACrC,OAAO,UAAU,CAAC,YAAY,CAAC,cAAc,EAAE,GAAG,EAAE,wBAAwB,CAAC,CAAC;QAChF,CAAC;QACD,OAAO,UAAU,CAAC,YAAY,CAAC,cAAc,EAAE,GAAG,EAAE,wBAAwB,CAAC,CAAC;IAChF,CAAC;IAED,MAAM,IAAI,GAAG,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC3C,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,UAAU,CAAC,YAAY,CAAC,cAAc,EAAE,GAAG,EAAE,oBAAoB,CAAC,CAAC;IAC5E,CAAC;IACD,MAAM,eAAe,GAAG,MAAM,oBAAoB,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAC3E,IAAI,eAAe;QAAE,OAAO,eAAe,CAAC;IAE5C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACjC,MAAM,gBAAgB,GAAG,iBAAiB,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;IAE7E,MAAM,OAAO,GAAG,cAAc,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC;IACtD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,SAAS,GAAgB,CAAC,WAAW,CAAC,iBAAiB,CAAC,CAAC;QAC/D,IAAI,OAAO,CAAC,KAAK,CAAC,kBAAkB,EAAE,CAAC;YACrC,SAAS,CAAC,IAAI,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC;QAC3C,CAAC;QACD,OAAO,UAAU,CAAC,YAAY,CAAC,oBAAoB,EAAE,GAAG,EAAE,cAAc,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAClG,CAAC;IACD,OAAO,OAAO,CAAC;QACb,OAAO;QACP,IAAI;QACJ,OAAO;QACP,gBAAgB;QAChB,QAAQ,EAAE,IAAI,CAAC,QAAQ;KACxB,CAAC,CAAC;AACL,CAAC,CAAC"}
|
package/dist/oauth/types.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { type TokenEndpointAuthMethod } from "./constants.js";
|
|
2
1
|
import type { CodeStore } from "./codes.js";
|
|
2
|
+
import type { ConsentOptions } from "./consent.js";
|
|
3
|
+
import { type TokenEndpointAuthMethod } from "./constants.js";
|
|
3
4
|
import type { RedirectAllowlistOptions } from "./redirect.js";
|
|
4
5
|
export type OAuthUser = {
|
|
5
6
|
id: string;
|
|
@@ -15,15 +16,33 @@ export type RegisteredClient = {
|
|
|
15
16
|
redirectUris: string[];
|
|
16
17
|
tokenEndpointAuthMethod: TokenEndpointAuthMethod;
|
|
17
18
|
};
|
|
19
|
+
/**
|
|
20
|
+
* The payload sealed into a `client_id` this server itself issued via
|
|
21
|
+
* `/register` (see `sealed.ts`, type `"client"`). Zero storage: the id
|
|
22
|
+
* *is* the registration record, self-verifying against `codeSecret`.
|
|
23
|
+
*/
|
|
24
|
+
export type ClientAssertion = {
|
|
25
|
+
redirectUris: string[];
|
|
26
|
+
};
|
|
18
27
|
export type ClientStore = {
|
|
19
28
|
/** Resolve a pre-registered client, or null when unknown. */
|
|
20
29
|
get: (clientId: string) => Promise<RegisteredClient | null>;
|
|
21
30
|
/**
|
|
22
|
-
* Verify a presented client secret.
|
|
23
|
-
*
|
|
24
|
-
*
|
|
31
|
+
* Verify a presented client secret yourself. Secret storage and
|
|
32
|
+
* comparison stay entirely in your implementation — the library never
|
|
33
|
+
* sees or persists credentials. Prefer `secretHash` below when you can:
|
|
34
|
+
* a hand-rolled `stored === presented` here is a timing oracle with no
|
|
35
|
+
* warning from the library.
|
|
25
36
|
*/
|
|
26
37
|
verifySecret?: (clientId: string, presented: string) => Promise<boolean>;
|
|
38
|
+
/**
|
|
39
|
+
* Preferred over `verifySecret`: return the stored hash (from
|
|
40
|
+
* `hashClientSecret`) for `clientId`, or `null` when unknown, and the
|
|
41
|
+
* library does the comparison with the same constant-time primitive it
|
|
42
|
+
* uses everywhere else. Checked first when both are present. See
|
|
43
|
+
* `secrethash.ts`.
|
|
44
|
+
*/
|
|
45
|
+
secretHash?: (clientId: string) => Promise<string | null>;
|
|
27
46
|
};
|
|
28
47
|
export type MintedToken = {
|
|
29
48
|
accessToken: string;
|
|
@@ -51,7 +70,22 @@ export type RefreshAccessTokenInput = {
|
|
|
51
70
|
export type RevokeTokenInput = {
|
|
52
71
|
token: string;
|
|
53
72
|
clientId: string;
|
|
54
|
-
tokenTypeHint?: "access_token" | "refresh_token";
|
|
73
|
+
tokenTypeHint?: "access_token" | "refresh_token" | undefined;
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* OAuth-side metrics hook, mirroring `McpPorts.audit` on the MCP side.
|
|
77
|
+
* `reason` is intentionally more specific than what the caller ever sees —
|
|
78
|
+
* `/token` and `/revoke` collapse every client-auth failure to one generic
|
|
79
|
+
* `invalid_client` (closing an enumeration oracle; see `clientauth.ts`),
|
|
80
|
+
* and a rejected `codeSecret` surfaces to the caller only as `server_error`
|
|
81
|
+
* — this port is where an operator gets the real reason back, without
|
|
82
|
+
* handing it to an unauthenticated caller.
|
|
83
|
+
*/
|
|
84
|
+
export type OAuthAuditEntry = {
|
|
85
|
+
/** Machine-stable event name, e.g. "client_auth_failed", "server_error". */
|
|
86
|
+
event: string;
|
|
87
|
+
clientId?: string | undefined;
|
|
88
|
+
reason: string;
|
|
55
89
|
};
|
|
56
90
|
export type OAuthPorts = {
|
|
57
91
|
/** HMAC secret for signing auth codes. */
|
|
@@ -63,7 +97,7 @@ export type OAuthPorts = {
|
|
|
63
97
|
/** Mint an access token bound to the consenting user and resource audience. */
|
|
64
98
|
mintAccessToken: (input: MintAccessTokenInput) => Promise<MintedToken>;
|
|
65
99
|
/** Optional refresh_token grant. Presence advertises the grant. */
|
|
66
|
-
refreshAccessToken?: (input: RefreshAccessTokenInput) => Promise<MintedToken | null
|
|
100
|
+
refreshAccessToken?: ((input: RefreshAccessTokenInput) => Promise<MintedToken | null>) | undefined;
|
|
67
101
|
/**
|
|
68
102
|
* RFC 7009. Presence mounts `/revoke` and advertises `revocation_endpoint`.
|
|
69
103
|
* Well-formed authenticated revoke is HTTP 200 even if the token is unknown
|
|
@@ -71,18 +105,40 @@ export type OAuthPorts = {
|
|
|
71
105
|
* `refreshAccessToken` MUST consult the same store; unknown tokens and
|
|
72
106
|
* tokens not issued to `clientId` MUST no-op (not throw).
|
|
73
107
|
*/
|
|
74
|
-
revokeToken?: (input: RevokeTokenInput) => Promise<void
|
|
108
|
+
revokeToken?: ((input: RevokeTokenInput) => Promise<void>) | undefined;
|
|
75
109
|
/** Shared single-use jti store for multi-instance; in-memory default otherwise. */
|
|
76
|
-
codeStore?: CodeStore;
|
|
110
|
+
codeStore?: CodeStore | undefined;
|
|
77
111
|
/** Pre-registered clients. Required to serve confidential clients. */
|
|
78
|
-
clientStore?: ClientStore;
|
|
112
|
+
clientStore?: ClientStore | undefined;
|
|
113
|
+
/**
|
|
114
|
+
* Opt-in metrics hook. Pass any function to see the real reason behind a
|
|
115
|
+
* collapsed `invalid_client` or a rejected `codeSecret`. Omit it and the
|
|
116
|
+
* library stays silent. Throwing never fails the request — same
|
|
117
|
+
* guarantee as `McpPorts.audit`.
|
|
118
|
+
*/
|
|
119
|
+
audit?: ((entry: OAuthAuditEntry) => void | Promise<void>) | undefined;
|
|
79
120
|
};
|
|
121
|
+
/**
|
|
122
|
+
* Invoke `ports.audit`, swallowing any failure — the OAuth-side twin of
|
|
123
|
+
* `methods.ts`'s `safeAudit`. No timeout wrapper here: unlike the MCP
|
|
124
|
+
* side, nothing here is expected to be a slow, request-scoped metrics
|
|
125
|
+
* call in the same way, and adding one would be complexity without a
|
|
126
|
+
* demonstrated need — revisit if that changes.
|
|
127
|
+
*/
|
|
128
|
+
export declare const safeOAuthAudit: (options: OAuthRouterOptions, entry: OAuthAuditEntry) => Promise<void>;
|
|
80
129
|
export type OAuthRouterOptions = {
|
|
81
130
|
ports: OAuthPorts;
|
|
82
131
|
resourcePath?: string;
|
|
83
132
|
oauthPath?: string;
|
|
84
133
|
realm?: string;
|
|
85
134
|
scopes?: string[];
|
|
135
|
+
/**
|
|
136
|
+
* Scopes granted when a client omits `scope` entirely. Required when
|
|
137
|
+
* `scopes` advertises more than one — with only one advertised scope
|
|
138
|
+
* there's no escalation to prevent, so it defaults to that scope.
|
|
139
|
+
* Must be a subset of `scopes`.
|
|
140
|
+
*/
|
|
141
|
+
defaultScopes?: string[];
|
|
86
142
|
redirect?: RedirectAllowlistOptions;
|
|
87
143
|
/** Advertised in AS metadata. Defaults to `["none"]` (public clients only). */
|
|
88
144
|
tokenEndpointAuthMethods?: TokenEndpointAuthMethod[];
|
|
@@ -98,12 +154,53 @@ export type OAuthRouterOptions = {
|
|
|
98
154
|
* `hasDynamicClient`.
|
|
99
155
|
*/
|
|
100
156
|
allowUnregisteredClients?: boolean;
|
|
157
|
+
/**
|
|
158
|
+
* Require every `client_id` to have gone through `clientStore` or this
|
|
159
|
+
* server's own `/register` (whose issued ids are self-verifying sealed
|
|
160
|
+
* assertions — see `sealed.ts`). Unlike `allowUnregisteredClients: false`,
|
|
161
|
+
* DCR stays mounted; a client just can't invent an id out of thin air.
|
|
162
|
+
*
|
|
163
|
+
* Default **true** since 1.0. Set **false** only when you intentionally
|
|
164
|
+
* accept arbitrary public `client_id`s (pre-CIMD connectors that invent ids).
|
|
165
|
+
*/
|
|
166
|
+
requireRegisteredClients?: boolean;
|
|
167
|
+
/**
|
|
168
|
+
* Consent policy. Policy, not a credential — sits alongside `redirect`,
|
|
169
|
+
* not under `ports`. Omit for the built-in hardened interstitial.
|
|
170
|
+
*/
|
|
171
|
+
consent?: ConsentOptions;
|
|
101
172
|
};
|
|
102
173
|
/** Default true — see `OAuthRouterOptions.allowUnregisteredClients`. */
|
|
103
174
|
export declare const unregisteredClientsAllowed: (options: OAuthRouterOptions) => boolean;
|
|
175
|
+
/** Default true since 1.0 — see `OAuthRouterOptions.requireRegisteredClients`. */
|
|
176
|
+
export declare const registeredClientsRequired: (options: OAuthRouterOptions) => boolean;
|
|
104
177
|
/** Scopes this AS advertises and is willing to grant. */
|
|
105
178
|
export declare const advertisedScopes: (options: OAuthRouterOptions) => string[];
|
|
179
|
+
/**
|
|
180
|
+
* Scopes granted when a client omits `scope`. Falls back to the full
|
|
181
|
+
* advertised set only when that set has at most one entry — see
|
|
182
|
+
* `assertScopeConfig`, which forbids the ambiguous multi-scope case at
|
|
183
|
+
* construction instead of silently over-granting here.
|
|
184
|
+
*/
|
|
185
|
+
export declare const defaultScopes: (options: OAuthRouterOptions) => string[];
|
|
186
|
+
/**
|
|
187
|
+
* A multi-scope server MUST say what an omitted `scope` grants — the
|
|
188
|
+
* alternative (silently granting everything advertised) is exactly the
|
|
189
|
+
* escalation least-privilege scoping exists to prevent.
|
|
190
|
+
*/
|
|
191
|
+
export declare const assertScopeConfig: (options: OAuthRouterOptions) => void;
|
|
192
|
+
export declare const assertCodeSecret: (secret: string) => void;
|
|
106
193
|
export declare const resolveSecret: (ports: OAuthPorts, req: Request) => Promise<string>;
|
|
194
|
+
/**
|
|
195
|
+
* An OAuth error's code and description without a `Response` built yet —
|
|
196
|
+
* lets a caller choose delivery: `oauthError` below for a direct JSON
|
|
197
|
+
* body, or a redirect to the client's own callback (RFC 6749 §4.1.2.1,
|
|
198
|
+
* see `authorize.ts` and `consent.ts`).
|
|
199
|
+
*/
|
|
200
|
+
export type OAuthErrorInfo = {
|
|
201
|
+
code: string;
|
|
202
|
+
description: string;
|
|
203
|
+
};
|
|
107
204
|
export declare const oauthError: (error: string, status: number, description?: string) => Response;
|
|
108
205
|
/** Direct JSON error when this AS does not serve unknown `client_id`s. */
|
|
109
206
|
export declare const unregisteredClientError: (options: OAuthRouterOptions, reject: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/oauth/types.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/oauth/types.ts"],"names":[],"mappings":"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"}
|
package/dist/oauth/types.js
CHANGED
|
@@ -1,13 +1,76 @@
|
|
|
1
|
-
import { DEFAULT_SCOPE } from "./constants.js";
|
|
2
1
|
import { jsonResponse } from "../http.js";
|
|
2
|
+
import { DEFAULT_SCOPE } from "./constants.js";
|
|
3
|
+
/**
|
|
4
|
+
* Invoke `ports.audit`, swallowing any failure — the OAuth-side twin of
|
|
5
|
+
* `methods.ts`'s `safeAudit`. No timeout wrapper here: unlike the MCP
|
|
6
|
+
* side, nothing here is expected to be a slow, request-scoped metrics
|
|
7
|
+
* call in the same way, and adding one would be complexity without a
|
|
8
|
+
* demonstrated need — revisit if that changes.
|
|
9
|
+
*/
|
|
10
|
+
export const safeOAuthAudit = async (options, entry) => {
|
|
11
|
+
try {
|
|
12
|
+
await options.ports.audit?.(entry);
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
// Intentionally ignored.
|
|
16
|
+
}
|
|
17
|
+
};
|
|
3
18
|
/** Default true — see `OAuthRouterOptions.allowUnregisteredClients`. */
|
|
4
19
|
export const unregisteredClientsAllowed = (options) => options.allowUnregisteredClients !== false;
|
|
20
|
+
/** Default true since 1.0 — see `OAuthRouterOptions.requireRegisteredClients`. */
|
|
21
|
+
export const registeredClientsRequired = (options) => options.requireRegisteredClients !== false;
|
|
5
22
|
/** Scopes this AS advertises and is willing to grant. */
|
|
6
23
|
export const advertisedScopes = (options) => options.scopes ?? [DEFAULT_SCOPE];
|
|
24
|
+
/**
|
|
25
|
+
* Scopes granted when a client omits `scope`. Falls back to the full
|
|
26
|
+
* advertised set only when that set has at most one entry — see
|
|
27
|
+
* `assertScopeConfig`, which forbids the ambiguous multi-scope case at
|
|
28
|
+
* construction instead of silently over-granting here.
|
|
29
|
+
*/
|
|
30
|
+
export const defaultScopes = (options) => options.defaultScopes ?? advertisedScopes(options);
|
|
31
|
+
/**
|
|
32
|
+
* A multi-scope server MUST say what an omitted `scope` grants — the
|
|
33
|
+
* alternative (silently granting everything advertised) is exactly the
|
|
34
|
+
* escalation least-privilege scoping exists to prevent.
|
|
35
|
+
*/
|
|
36
|
+
export const assertScopeConfig = (options) => {
|
|
37
|
+
const advertised = advertisedScopes(options);
|
|
38
|
+
if (advertised.length > 1 && !options.defaultScopes) {
|
|
39
|
+
throw new Error("scopes has more than one entry — defaultScopes must say what an omitted " +
|
|
40
|
+
"scope request grants (e.g. defaultScopes: [] for least privilege)");
|
|
41
|
+
}
|
|
42
|
+
const unknown = (options.defaultScopes ?? []).find((scope) => !advertised.includes(scope));
|
|
43
|
+
if (unknown !== undefined) {
|
|
44
|
+
throw new Error(`defaultScopes contains "${unknown}", which is not in scopes`);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* `codeSecret` can forge an auth code for any userId/scope/resource, so a
|
|
49
|
+
* weak or copy-pasted one is a full authorization bypass, not a footgun.
|
|
50
|
+
* The literals here are the exact strings this package's own docs/examples
|
|
51
|
+
* publish — copy-paste is the realistic failure mode.
|
|
52
|
+
*/
|
|
53
|
+
const MIN_CODE_SECRET_LENGTH = 32;
|
|
54
|
+
const DENYLISTED_CODE_SECRETS = new Set([
|
|
55
|
+
"e2e-code-secret-value",
|
|
56
|
+
"change-me",
|
|
57
|
+
"test-secret-value",
|
|
58
|
+
]);
|
|
59
|
+
const GENERATE_HINT = "generate one with `openssl rand -base64 32`";
|
|
60
|
+
export const assertCodeSecret = (secret) => {
|
|
61
|
+
// Checked before the length rule so a denylisted literal is always named
|
|
62
|
+
// for what it is, even if a future literal happens to be 32+ characters.
|
|
63
|
+
if (DENYLISTED_CODE_SECRETS.has(secret)) {
|
|
64
|
+
throw new Error(`codeSecret must not be a literal published in this package's own docs or examples — ${GENERATE_HINT}`);
|
|
65
|
+
}
|
|
66
|
+
if (secret.length < MIN_CODE_SECRET_LENGTH) {
|
|
67
|
+
throw new Error(`codeSecret must be at least ${MIN_CODE_SECRET_LENGTH} characters (got ${secret.length}) — ${GENERATE_HINT}`);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
7
70
|
export const resolveSecret = async (ports, req) => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
return
|
|
71
|
+
const secret = typeof ports.codeSecret === "string" ? ports.codeSecret : await ports.codeSecret(req);
|
|
72
|
+
assertCodeSecret(secret);
|
|
73
|
+
return secret;
|
|
11
74
|
};
|
|
12
75
|
export const oauthError = (error, status, description) => jsonResponse(description ? { error, error_description: description } : { error }, status, undefined, { cors: false });
|
|
13
76
|
const UNREGISTERED_CLIENT_DESCRIPTION = "unknown client_id — this server only serves pre-registered clients";
|
package/dist/oauth/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/oauth/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAgC,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/oauth/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wellknown.d.ts","sourceRoot":"","sources":["../../src/oauth/wellknown.ts"],"names":[],"mappings":"AAOA,OAAO,
|
|
1
|
+
{"version":3,"file":"wellknown.d.ts","sourceRoot":"","sources":["../../src/oauth/wellknown.ts"],"names":[],"mappings":"AAOA,OAAO,EAAmB,KAAK,kBAAkB,EAA8B,MAAM,YAAY,CAAC;AAUlG,eAAO,MAAM,eAAe,GAC1B,SAAS,OAAO,EAChB,SAAS,kBAAkB,EAC3B,OAAO;IACL,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACtB,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB,KACA,OAAO,CAAC,QAAQ,GAAG,IAAI,CAyBzB,CAAC"}
|
package/dist/oauth/wellknown.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsonResponse, requireHttpMethod } from "../http.js";
|
|
2
|
-
import { authorizationServerMetadata, protectedResourceMetadata, } from "./metadata.js";
|
|
3
2
|
import { GRANT_TYPES } from "./constants.js";
|
|
4
|
-
import {
|
|
3
|
+
import { authorizationServerMetadata, protectedResourceMetadata, } from "./metadata.js";
|
|
4
|
+
import { unregisteredClientsAllowed } from "./types.js";
|
|
5
5
|
const GET_ONLY = new Set(["GET"]);
|
|
6
6
|
const grantTypesOf = (ports) => {
|
|
7
7
|
const grants = [GRANT_TYPES.authorizationCode];
|
|
@@ -22,8 +22,10 @@ export const handleWellKnown = async (request, options, paths) => {
|
|
|
22
22
|
resourcePath: paths.resourcePath,
|
|
23
23
|
oauthPath: paths.oauthPath,
|
|
24
24
|
grantTypes: grantTypesOf(options.ports),
|
|
25
|
-
scopes: options.scopes,
|
|
26
|
-
|
|
25
|
+
...(options.scopes !== undefined ? { scopes: options.scopes } : {}),
|
|
26
|
+
...(options.tokenEndpointAuthMethods !== undefined
|
|
27
|
+
? { tokenEndpointAuthMethods: options.tokenEndpointAuthMethods }
|
|
28
|
+
: {}),
|
|
27
29
|
dcrEnabled: unregisteredClientsAllowed(options),
|
|
28
30
|
revocationEnabled: Boolean(options.ports.revokeToken),
|
|
29
31
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wellknown.js","sourceRoot":"","sources":["../../src/oauth/wellknown.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC7D,OAAO,
|
|
1
|
+
{"version":3,"file":"wellknown.js","sourceRoot":"","sources":["../../src/oauth/wellknown.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EACL,2BAA2B,EAE3B,yBAAyB,GAC1B,MAAM,eAAe,CAAC;AACvB,OAAO,EAA4C,0BAA0B,EAAE,MAAM,YAAY,CAAC;AAElG,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;AAElC,MAAM,YAAY,GAAG,CAAC,KAAiB,EAAe,EAAE;IACtD,MAAM,MAAM,GAAgB,CAAC,WAAW,CAAC,iBAAiB,CAAC,CAAC;IAC5D,IAAI,KAAK,CAAC,kBAAkB;QAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC;IACpE,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,EAClC,OAAgB,EAChB,OAA2B,EAC3B,KAKC,EACyB,EAAE;IAC5B,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACjC,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC;IAC1B,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAEvE,MAAM,WAAW,GAAG,iBAAiB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACzD,IAAI,WAAW;QAAE,OAAO,WAAW,CAAC;IAEpC,MAAM,QAAQ,GAAG;QACf,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,UAAU,EAAE,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC;QACvC,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnE,GAAG,CAAC,OAAO,CAAC,wBAAwB,KAAK,SAAS;YAChD,CAAC,CAAC,EAAE,wBAAwB,EAAE,OAAO,CAAC,wBAAwB,EAAE;YAChE,CAAC,CAAC,EAAE,CAAC;QACP,UAAU,EAAE,0BAA0B,CAAC,OAAO,CAAC;QAC/C,iBAAiB,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC;KACtD,CAAC;IAEF,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;QACnC,CAAC,CAAC,yBAAyB,CAAC,QAAQ,CAAC;QACrC,CAAC,CAAC,2BAA2B,CAAC,QAAQ,CAAC,CAAC;IAC1C,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC,CAAC"}
|
package/dist/protocol.d.ts
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
export declare const PROTOCOL_VERSIONS: Set<string>;
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Answered on `initialize` when the client's requested version is missing
|
|
4
|
+
* or unrecognized. The spec's guidance is to respond with a version the
|
|
5
|
+
* server supports, preferring the latest — not the oldest one, which is
|
|
6
|
+
* what this constant did before.
|
|
7
|
+
*/
|
|
8
|
+
export declare const DEFAULT_PROTOCOL_VERSION = "2025-06-18";
|
|
9
|
+
/**
|
|
10
|
+
* The assumed protocol version for a request that omits the
|
|
11
|
+
* `MCP-Protocol-Version` header — distinct from `DEFAULT_PROTOCOL_VERSION`
|
|
12
|
+
* above. This is the 2025-06-18 spec's own backwards-compatibility rule
|
|
13
|
+
* for the header specifically (introduced in that revision, so a header-less
|
|
14
|
+
* request predates it), not a "pick the newest" preference.
|
|
15
|
+
*/
|
|
16
|
+
export declare const ASSUMED_HEADER_PROTOCOL_VERSION = "2025-03-26";
|
|
3
17
|
export declare const pickProtocolVersion: (params?: Record<string, unknown>) => string;
|
|
4
18
|
//# sourceMappingURL=protocol.d.ts.map
|
package/dist/protocol.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB,
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB,aAAsD,CAAC;AAErF;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,eAAe,CAAC;AAErD;;;;;;GAMG;AACH,eAAO,MAAM,+BAA+B,eAAe,CAAC;AAE5D,eAAO,MAAM,mBAAmB,GAAI,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,MAMtE,CAAC"}
|
package/dist/protocol.js
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
|
-
export const PROTOCOL_VERSIONS = new Set([
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
export const PROTOCOL_VERSIONS = new Set(["2024-11-05", "2025-03-26", "2025-06-18"]);
|
|
2
|
+
/**
|
|
3
|
+
* Answered on `initialize` when the client's requested version is missing
|
|
4
|
+
* or unrecognized. The spec's guidance is to respond with a version the
|
|
5
|
+
* server supports, preferring the latest — not the oldest one, which is
|
|
6
|
+
* what this constant did before.
|
|
7
|
+
*/
|
|
8
|
+
export const DEFAULT_PROTOCOL_VERSION = "2025-06-18";
|
|
9
|
+
/**
|
|
10
|
+
* The assumed protocol version for a request that omits the
|
|
11
|
+
* `MCP-Protocol-Version` header — distinct from `DEFAULT_PROTOCOL_VERSION`
|
|
12
|
+
* above. This is the 2025-06-18 spec's own backwards-compatibility rule
|
|
13
|
+
* for the header specifically (introduced in that revision, so a header-less
|
|
14
|
+
* request predates it), not a "pick the newest" preference.
|
|
15
|
+
*/
|
|
16
|
+
export const ASSUMED_HEADER_PROTOCOL_VERSION = "2025-03-26";
|
|
7
17
|
export const pickProtocolVersion = (params) => {
|
|
8
18
|
const requested = params?.protocolVersion;
|
|
9
19
|
if (typeof requested === "string" && PROTOCOL_VERSIONS.has(requested)) {
|
package/dist/protocol.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC,CAAC,YAAY,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC,CAAC;AAErF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,YAAY,CAAC;AAErD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,YAAY,CAAC;AAE5D,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,MAAgC,EAAU,EAAE;IAC9E,MAAM,SAAS,GAAG,MAAM,EAAE,eAAe,CAAC;IAC1C,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,iBAAiB,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;QACtE,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,wBAAwB,CAAC;AAClC,CAAC,CAAC"}
|