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.
Files changed (301) hide show
  1. package/README.md +65 -93
  2. package/dist/adapters/node-request.d.ts +53 -0
  3. package/dist/adapters/node-request.d.ts.map +1 -0
  4. package/dist/adapters/node-request.js +165 -0
  5. package/dist/adapters/node-request.js.map +1 -0
  6. package/dist/adapters/node.d.ts +13 -30
  7. package/dist/adapters/node.d.ts.map +1 -1
  8. package/dist/adapters/node.js +55 -101
  9. package/dist/adapters/node.js.map +1 -1
  10. package/dist/advanced.d.ts +18 -0
  11. package/dist/advanced.d.ts.map +1 -0
  12. package/dist/advanced.js +18 -0
  13. package/dist/advanced.js.map +1 -0
  14. package/dist/app-options.d.ts +132 -0
  15. package/dist/app-options.d.ts.map +1 -0
  16. package/dist/app-options.js +2 -0
  17. package/dist/app-options.js.map +1 -0
  18. package/dist/app.d.ts +2 -89
  19. package/dist/app.d.ts.map +1 -1
  20. package/dist/app.js +143 -63
  21. package/dist/app.js.map +1 -1
  22. package/dist/audit.d.ts +5 -5
  23. package/dist/audit.d.ts.map +1 -1
  24. package/dist/audit.js +19 -8
  25. package/dist/audit.js.map +1 -1
  26. package/dist/auth/bearer.d.ts +4 -1
  27. package/dist/auth/bearer.d.ts.map +1 -1
  28. package/dist/auth/bearer.js +19 -4
  29. package/dist/auth/bearer.js.map +1 -1
  30. package/dist/auth/signed-token.d.ts +26 -0
  31. package/dist/auth/signed-token.d.ts.map +1 -0
  32. package/dist/auth/signed-token.js +96 -0
  33. package/dist/auth/signed-token.js.map +1 -0
  34. package/dist/clients.js +1 -1
  35. package/dist/clients.js.map +1 -1
  36. package/dist/deprecated.d.ts +42 -0
  37. package/dist/deprecated.d.ts.map +1 -0
  38. package/dist/deprecated.js +35 -0
  39. package/dist/deprecated.js.map +1 -0
  40. package/dist/{body.d.ts → http/body.d.ts} +27 -6
  41. package/dist/http/body.d.ts.map +1 -0
  42. package/dist/{body.js → http/body.js} +33 -13
  43. package/dist/http/body.js.map +1 -0
  44. package/dist/http/http.d.ts +42 -0
  45. package/dist/http/http.d.ts.map +1 -0
  46. package/dist/http/http.js +100 -0
  47. package/dist/http/http.js.map +1 -0
  48. package/dist/http/origins.d.ts +37 -0
  49. package/dist/http/origins.d.ts.map +1 -0
  50. package/dist/http/origins.js +87 -0
  51. package/dist/http/origins.js.map +1 -0
  52. package/dist/{dispatch.d.ts → mcp/dispatch.d.ts} +1 -1
  53. package/dist/mcp/dispatch.d.ts.map +1 -0
  54. package/dist/mcp/dispatch.js +99 -0
  55. package/dist/mcp/dispatch.js.map +1 -0
  56. package/dist/{jsonrpc.d.ts → mcp/jsonrpc.d.ts} +9 -0
  57. package/dist/mcp/jsonrpc.d.ts.map +1 -0
  58. package/dist/mcp/jsonrpc.js +88 -0
  59. package/dist/mcp/jsonrpc.js.map +1 -0
  60. package/dist/{methods.d.ts → mcp/methods.d.ts} +60 -11
  61. package/dist/mcp/methods.d.ts.map +1 -0
  62. package/dist/mcp/methods.js +84 -0
  63. package/dist/mcp/methods.js.map +1 -0
  64. package/dist/{protocol.d.ts → mcp/protocol.d.ts} +1 -3
  65. package/dist/mcp/protocol.d.ts.map +1 -0
  66. package/dist/{protocol.js → mcp/protocol.js} +1 -3
  67. package/dist/mcp/protocol.js.map +1 -0
  68. package/dist/mcp/registry.d.ts +58 -0
  69. package/dist/mcp/registry.d.ts.map +1 -0
  70. package/dist/mcp/registry.js +81 -0
  71. package/dist/mcp/registry.js.map +1 -0
  72. package/dist/mcp/rpc-handlers.d.ts +11 -0
  73. package/dist/mcp/rpc-handlers.d.ts.map +1 -0
  74. package/dist/mcp/rpc-handlers.js +133 -0
  75. package/dist/mcp/rpc-handlers.js.map +1 -0
  76. package/dist/{tools.d.ts → mcp/tools.d.ts} +20 -1
  77. package/dist/mcp/tools.d.ts.map +1 -0
  78. package/dist/mcp/tools.js +166 -0
  79. package/dist/mcp/tools.js.map +1 -0
  80. package/dist/{validate.d.ts → mcp/validate.d.ts} +18 -21
  81. package/dist/mcp/validate.d.ts.map +1 -0
  82. package/dist/{validate.js → mcp/validate.js} +59 -41
  83. package/dist/mcp/validate.js.map +1 -0
  84. package/dist/oauth/audit.d.ts +20 -0
  85. package/dist/oauth/audit.d.ts.map +1 -0
  86. package/dist/oauth/audit.js +29 -0
  87. package/dist/oauth/audit.js.map +1 -0
  88. package/dist/oauth/cimd/cimd-parse.d.ts +4 -0
  89. package/dist/oauth/cimd/cimd-parse.d.ts.map +1 -0
  90. package/dist/oauth/cimd/cimd-parse.js +70 -0
  91. package/dist/oauth/cimd/cimd-parse.js.map +1 -0
  92. package/dist/oauth/cimd/cimd-ssrf.d.ts +8 -0
  93. package/dist/oauth/cimd/cimd-ssrf.d.ts.map +1 -0
  94. package/dist/oauth/cimd/cimd-ssrf.js +193 -0
  95. package/dist/oauth/cimd/cimd-ssrf.js.map +1 -0
  96. package/dist/oauth/cimd/cimd.d.ts +36 -0
  97. package/dist/oauth/cimd/cimd.d.ts.map +1 -0
  98. package/dist/oauth/cimd/cimd.js +132 -0
  99. package/dist/oauth/cimd/cimd.js.map +1 -0
  100. package/dist/oauth/cimd/types.d.ts +12 -0
  101. package/dist/oauth/cimd/types.d.ts.map +1 -0
  102. package/dist/oauth/cimd/types.js +3 -0
  103. package/dist/oauth/cimd/types.js.map +1 -0
  104. package/dist/oauth/config.d.ts +51 -0
  105. package/dist/oauth/config.d.ts.map +1 -0
  106. package/dist/oauth/config.js +108 -0
  107. package/dist/oauth/config.js.map +1 -0
  108. package/dist/oauth/constants.d.ts +11 -0
  109. package/dist/oauth/constants.d.ts.map +1 -1
  110. package/dist/oauth/constants.js +11 -0
  111. package/dist/oauth/constants.js.map +1 -1
  112. package/dist/oauth/{base64url.d.ts → crypto/base64url.d.ts} +0 -1
  113. package/dist/oauth/crypto/base64url.d.ts.map +1 -0
  114. package/dist/oauth/{base64url.js → crypto/base64url.js} +0 -1
  115. package/dist/oauth/crypto/base64url.js.map +1 -0
  116. package/dist/oauth/crypto/clientauth.d.ts +13 -0
  117. package/dist/oauth/crypto/clientauth.d.ts.map +1 -0
  118. package/dist/oauth/crypto/clientauth.js +195 -0
  119. package/dist/oauth/crypto/clientauth.js.map +1 -0
  120. package/dist/oauth/crypto/codes.d.ts +65 -0
  121. package/dist/oauth/crypto/codes.d.ts.map +1 -0
  122. package/dist/oauth/crypto/codes.js +121 -0
  123. package/dist/oauth/crypto/codes.js.map +1 -0
  124. package/dist/oauth/crypto/hkdf.d.ts +13 -0
  125. package/dist/oauth/crypto/hkdf.d.ts.map +1 -0
  126. package/dist/oauth/crypto/hkdf.js +30 -0
  127. package/dist/oauth/crypto/hkdf.js.map +1 -0
  128. package/dist/oauth/crypto/keycache.d.ts +19 -0
  129. package/dist/oauth/crypto/keycache.d.ts.map +1 -0
  130. package/dist/oauth/crypto/keycache.js +44 -0
  131. package/dist/oauth/crypto/keycache.js.map +1 -0
  132. package/dist/oauth/crypto/pkce.d.ts.map +1 -0
  133. package/dist/oauth/{pkce.js → crypto/pkce.js} +1 -1
  134. package/dist/oauth/crypto/pkce.js.map +1 -0
  135. package/dist/oauth/{sealed.d.ts → crypto/sealed.d.ts} +12 -3
  136. package/dist/oauth/crypto/sealed.d.ts.map +1 -0
  137. package/dist/oauth/{sealed.js → crypto/sealed.js} +22 -14
  138. package/dist/oauth/crypto/sealed.js.map +1 -0
  139. package/dist/oauth/{secrethash.d.ts → crypto/secrethash.d.ts} +14 -0
  140. package/dist/oauth/crypto/secrethash.d.ts.map +1 -0
  141. package/dist/oauth/{secrethash.js → crypto/secrethash.js} +27 -11
  142. package/dist/oauth/crypto/secrethash.js.map +1 -0
  143. package/dist/oauth/deprecated.d.ts +52 -0
  144. package/dist/oauth/deprecated.d.ts.map +1 -0
  145. package/dist/oauth/deprecated.js +49 -0
  146. package/dist/oauth/deprecated.js.map +1 -0
  147. package/dist/oauth/endpoints/authorize.d.ts +3 -0
  148. package/dist/oauth/endpoints/authorize.d.ts.map +1 -0
  149. package/dist/oauth/endpoints/authorize.js +209 -0
  150. package/dist/oauth/endpoints/authorize.js.map +1 -0
  151. package/dist/oauth/endpoints/callback.d.ts +20 -0
  152. package/dist/oauth/endpoints/callback.d.ts.map +1 -0
  153. package/dist/oauth/endpoints/callback.js +31 -0
  154. package/dist/oauth/endpoints/callback.js.map +1 -0
  155. package/dist/oauth/endpoints/consent-page.d.ts +5 -0
  156. package/dist/oauth/endpoints/consent-page.d.ts.map +1 -0
  157. package/dist/oauth/endpoints/consent-page.js +63 -0
  158. package/dist/oauth/endpoints/consent-page.js.map +1 -0
  159. package/dist/oauth/endpoints/consent.d.ts +29 -0
  160. package/dist/oauth/endpoints/consent.d.ts.map +1 -0
  161. package/dist/oauth/endpoints/consent.js +98 -0
  162. package/dist/oauth/endpoints/consent.js.map +1 -0
  163. package/dist/oauth/{metadata.d.ts → endpoints/metadata.d.ts} +8 -2
  164. package/dist/oauth/endpoints/metadata.d.ts.map +1 -0
  165. package/dist/oauth/{metadata.js → endpoints/metadata.js} +10 -4
  166. package/dist/oauth/endpoints/metadata.js.map +1 -0
  167. package/dist/oauth/endpoints/register.d.ts +10 -0
  168. package/dist/oauth/endpoints/register.d.ts.map +1 -0
  169. package/dist/oauth/endpoints/register.js +96 -0
  170. package/dist/oauth/endpoints/register.js.map +1 -0
  171. package/dist/oauth/{revoke.d.ts → endpoints/revoke.d.ts} +1 -1
  172. package/dist/oauth/endpoints/revoke.d.ts.map +1 -0
  173. package/dist/oauth/{revoke.js → endpoints/revoke.js} +8 -18
  174. package/dist/oauth/endpoints/revoke.js.map +1 -0
  175. package/dist/oauth/endpoints/token.d.ts +3 -0
  176. package/dist/oauth/endpoints/token.d.ts.map +1 -0
  177. package/dist/oauth/{token.js → endpoints/token.js} +32 -25
  178. package/dist/oauth/endpoints/token.js.map +1 -0
  179. package/dist/oauth/{wellknown.d.ts → endpoints/wellknown.d.ts} +1 -1
  180. package/dist/oauth/endpoints/wellknown.d.ts.map +1 -0
  181. package/dist/oauth/{wellknown.js → endpoints/wellknown.js} +7 -9
  182. package/dist/oauth/endpoints/wellknown.js.map +1 -0
  183. package/dist/oauth/policy/redirect.d.ts.map +1 -0
  184. package/dist/oauth/policy/redirect.js.map +1 -0
  185. package/dist/oauth/policy/reqbody.d.ts +18 -0
  186. package/dist/oauth/policy/reqbody.d.ts.map +1 -0
  187. package/dist/oauth/policy/reqbody.js +68 -0
  188. package/dist/oauth/policy/reqbody.js.map +1 -0
  189. package/dist/oauth/{resource.d.ts → policy/resource.d.ts} +1 -1
  190. package/dist/oauth/policy/resource.d.ts.map +1 -0
  191. package/dist/oauth/{resource.js → policy/resource.js} +2 -2
  192. package/dist/oauth/policy/resource.js.map +1 -0
  193. package/dist/oauth/{scope.d.ts → policy/scope.d.ts} +2 -2
  194. package/dist/oauth/policy/scope.d.ts.map +1 -0
  195. package/dist/oauth/{scope.js → policy/scope.js} +3 -3
  196. package/dist/oauth/policy/scope.js.map +1 -0
  197. package/dist/oauth/router.d.ts +10 -13
  198. package/dist/oauth/router.d.ts.map +1 -1
  199. package/dist/oauth/router.js +93 -115
  200. package/dist/oauth/router.js.map +1 -1
  201. package/dist/oauth/types.d.ts +99 -45
  202. package/dist/oauth/types.d.ts.map +1 -1
  203. package/dist/oauth/types.js +1 -90
  204. package/dist/oauth/types.js.map +1 -1
  205. package/dist/server.d.ts +13 -12
  206. package/dist/server.d.ts.map +1 -1
  207. package/dist/server.js +10 -8
  208. package/dist/server.js.map +1 -1
  209. package/dist/util/defined.d.ts +11 -0
  210. package/dist/util/defined.d.ts.map +1 -0
  211. package/dist/util/defined.js +15 -0
  212. package/dist/util/defined.js.map +1 -0
  213. package/dist/util/json.d.ts +3 -0
  214. package/dist/util/json.d.ts.map +1 -0
  215. package/dist/util/json.js +3 -0
  216. package/dist/util/json.js.map +1 -0
  217. package/docs/ROADMAP.md +37 -47
  218. package/docs/compatibility.md +86 -0
  219. package/docs/diagrams/demo-tool-result.png +0 -0
  220. package/docs/diagrams/mcp-trellis-architecture.html +305 -0
  221. package/docs/guide.md +46 -16
  222. package/docs/reference.md +55 -22
  223. package/docs/security.md +58 -9
  224. package/docs/troubleshooting.md +19 -0
  225. package/package.json +27 -10
  226. package/dist/adapters/origins.d.ts +0 -19
  227. package/dist/adapters/origins.d.ts.map +0 -1
  228. package/dist/adapters/origins.js +0 -52
  229. package/dist/adapters/origins.js.map +0 -1
  230. package/dist/body.d.ts.map +0 -1
  231. package/dist/body.js.map +0 -1
  232. package/dist/dispatch.d.ts.map +0 -1
  233. package/dist/dispatch.js +0 -119
  234. package/dist/dispatch.js.map +0 -1
  235. package/dist/http.d.ts +0 -17
  236. package/dist/http.d.ts.map +0 -1
  237. package/dist/http.js +0 -38
  238. package/dist/http.js.map +0 -1
  239. package/dist/jsonrpc.d.ts.map +0 -1
  240. package/dist/jsonrpc.js +0 -18
  241. package/dist/jsonrpc.js.map +0 -1
  242. package/dist/methods.d.ts.map +0 -1
  243. package/dist/methods.js +0 -130
  244. package/dist/methods.js.map +0 -1
  245. package/dist/oauth/authorize.d.ts +0 -3
  246. package/dist/oauth/authorize.d.ts.map +0 -1
  247. package/dist/oauth/authorize.js +0 -141
  248. package/dist/oauth/authorize.js.map +0 -1
  249. package/dist/oauth/base64url.d.ts.map +0 -1
  250. package/dist/oauth/base64url.js.map +0 -1
  251. package/dist/oauth/clientauth.d.ts +0 -24
  252. package/dist/oauth/clientauth.d.ts.map +0 -1
  253. package/dist/oauth/clientauth.js +0 -119
  254. package/dist/oauth/clientauth.js.map +0 -1
  255. package/dist/oauth/codes.d.ts +0 -30
  256. package/dist/oauth/codes.d.ts.map +0 -1
  257. package/dist/oauth/codes.js +0 -85
  258. package/dist/oauth/codes.js.map +0 -1
  259. package/dist/oauth/consent.d.ts +0 -65
  260. package/dist/oauth/consent.d.ts.map +0 -1
  261. package/dist/oauth/consent.js +0 -173
  262. package/dist/oauth/consent.js.map +0 -1
  263. package/dist/oauth/metadata.d.ts.map +0 -1
  264. package/dist/oauth/metadata.js.map +0 -1
  265. package/dist/oauth/pkce.d.ts.map +0 -1
  266. package/dist/oauth/pkce.js.map +0 -1
  267. package/dist/oauth/redirect.d.ts.map +0 -1
  268. package/dist/oauth/redirect.js.map +0 -1
  269. package/dist/oauth/reqbody.d.ts +0 -10
  270. package/dist/oauth/reqbody.d.ts.map +0 -1
  271. package/dist/oauth/reqbody.js +0 -29
  272. package/dist/oauth/reqbody.js.map +0 -1
  273. package/dist/oauth/resource.d.ts.map +0 -1
  274. package/dist/oauth/resource.js.map +0 -1
  275. package/dist/oauth/revoke.d.ts.map +0 -1
  276. package/dist/oauth/revoke.js.map +0 -1
  277. package/dist/oauth/scope.d.ts.map +0 -1
  278. package/dist/oauth/scope.js.map +0 -1
  279. package/dist/oauth/sealed.d.ts.map +0 -1
  280. package/dist/oauth/sealed.js.map +0 -1
  281. package/dist/oauth/secrethash.d.ts.map +0 -1
  282. package/dist/oauth/secrethash.js.map +0 -1
  283. package/dist/oauth/token.d.ts +0 -3
  284. package/dist/oauth/token.d.ts.map +0 -1
  285. package/dist/oauth/token.js.map +0 -1
  286. package/dist/oauth/wellknown.d.ts.map +0 -1
  287. package/dist/oauth/wellknown.js.map +0 -1
  288. package/dist/protocol.d.ts.map +0 -1
  289. package/dist/protocol.js.map +0 -1
  290. package/dist/registry.d.ts +0 -37
  291. package/dist/registry.d.ts.map +0 -1
  292. package/dist/registry.js +0 -77
  293. package/dist/registry.js.map +0 -1
  294. package/dist/tools.d.ts.map +0 -1
  295. package/dist/tools.js +0 -98
  296. package/dist/tools.js.map +0 -1
  297. package/dist/validate.d.ts.map +0 -1
  298. package/dist/validate.js.map +0 -1
  299. /package/dist/oauth/{pkce.d.ts → crypto/pkce.d.ts} +0 -0
  300. /package/dist/oauth/{redirect.d.ts → policy/redirect.d.ts} +0 -0
  301. /package/dist/oauth/{redirect.js → policy/redirect.js} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hkdf.d.ts","sourceRoot":"","sources":["../../../src/oauth/crypto/hkdf.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,wFAAwF;AACxF,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,eAAe,GAAG,gBAAgB,CAAC;IAC9C,KAAK,EAAE,QAAQ,CAAC;CACjB,CAAC;AAgCF,eAAO,MAAM,OAAO,GAAU,QAAQ,MAAM,EAAE,SAAS,cAAc,KAAG,OAAO,CAAC,SAAS,CAGxF,CAAC"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Purpose-bound keys derived from `codeSecret` with HKDF-SHA256, cached so
3
+ * rotation (`unsealAny`, `verifyClientSecretAny`) doesn't re-run
4
+ * `importKey` + `deriveKey` for every entry on every request.
5
+ */
6
+ import { bytesToBase64Url } from "./base64url.js";
7
+ import { createBoundedCache, settleCached } from "./keycache.js";
8
+ const deriveKey = async (secret, purpose) => {
9
+ const encoder = new TextEncoder();
10
+ const material = await crypto.subtle.importKey("raw", encoder.encode(secret), "HKDF", false, [
11
+ "deriveKey",
12
+ ]);
13
+ return crypto.subtle.deriveKey({ name: "HKDF", hash: "SHA-256", salt: new Uint8Array(0), info: encoder.encode(purpose.info) }, material, purpose.algorithm, false, [purpose.usage]);
14
+ };
15
+ /**
16
+ * SHA-256 fingerprint of `secret` for the cache key — not a KDF. Keeps the
17
+ * plaintext secret out of Map key space (material still lives inside the
18
+ * derived CryptoKey).
19
+ */
20
+ const secretFingerprint = async (secret) => {
21
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(`mcp-trellis:cache-key:${secret}`));
22
+ return bytesToBase64Url(digest);
23
+ };
24
+ const KEY_CACHE_LIMIT = 64;
25
+ const keyCache = createBoundedCache(KEY_CACHE_LIMIT);
26
+ export const hkdfKey = async (secret, purpose) => {
27
+ const cacheKey = `${purpose.info}:${purpose.usage}:${await secretFingerprint(secret)}`;
28
+ return settleCached(keyCache, cacheKey, () => deriveKey(secret, purpose));
29
+ };
30
+ //# sourceMappingURL=hkdf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hkdf.js","sourceRoot":"","sources":["../../../src/oauth/crypto/hkdf.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AASjE,MAAM,SAAS,GAAG,KAAK,EAAE,MAAc,EAAE,OAAuB,EAAsB,EAAE;IACtF,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;IAClC,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE;QAC3F,WAAW;KACZ,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,MAAM,CAAC,SAAS,CAC5B,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,UAAU,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAC9F,QAAQ,EACR,OAAO,CAAC,SAAS,EACjB,KAAK,EACL,CAAC,OAAO,CAAC,KAAK,CAAC,CAChB,CAAC;AACJ,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,iBAAiB,GAAG,KAAK,EAAE,MAAc,EAAmB,EAAE;IAClE,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CACvC,SAAS,EACT,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,yBAAyB,MAAM,EAAE,CAAC,CAC5D,CAAC;IACF,OAAO,gBAAgB,CAAC,MAAM,CAAC,CAAC;AAClC,CAAC,CAAC;AAEF,MAAM,eAAe,GAAG,EAAE,CAAC;AAC3B,MAAM,QAAQ,GAAG,kBAAkB,CAAqB,eAAe,CAAC,CAAC;AAEzE,MAAM,CAAC,MAAM,OAAO,GAAG,KAAK,EAAE,MAAc,EAAE,OAAuB,EAAsB,EAAE;IAC3F,MAAM,QAAQ,GAAG,GAAG,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,KAAK,IAAI,MAAM,iBAAiB,CAAC,MAAM,CAAC,EAAE,CAAC;IACvF,OAAO,YAAY,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAC5E,CAAC,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * A small bounded cache, used by `hkdf.ts` for derived `CryptoKey`s.
3
+ *
4
+ * Insertion-order eviction, not true LRU — simpler, and the expected key
5
+ * count (a handful of rotation entries, rarely changing at runtime) stays
6
+ * well under the cap in ordinary use, so eviction order rarely matters in
7
+ * practice. The cap itself exists for the discouraged-but-possible case of
8
+ * a function-form `codeSecret` returning a different value on every call,
9
+ * which would otherwise grow this cache without bound.
10
+ */
11
+ export type BoundedCache<V> = {
12
+ get: (key: string) => V | undefined;
13
+ set: (key: string, value: V) => void;
14
+ delete: (key: string) => void;
15
+ };
16
+ export declare const createBoundedCache: <V>(limit: number) => BoundedCache<V>;
17
+ /** Cache a promise; drop the entry if it rejects so the slot is not poisoned. */
18
+ export declare const settleCached: <T>(cache: BoundedCache<Promise<T>>, key: string, create: () => Promise<T>) => Promise<T>;
19
+ //# sourceMappingURL=keycache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keycache.d.ts","sourceRoot":"","sources":["../../../src/oauth/crypto/keycache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI;IAC5B,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,CAAC,GAAG,SAAS,CAAC;IACpC,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IACrC,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/B,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,CAAC,EAAE,OAAO,MAAM,KAAG,YAAY,CAAC,CAAC,CAenE,CAAC;AAEF,iFAAiF;AACjF,eAAO,MAAM,YAAY,GAAU,CAAC,EAClC,OAAO,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAC/B,KAAK,MAAM,EACX,QAAQ,MAAM,OAAO,CAAC,CAAC,CAAC,KACvB,OAAO,CAAC,CAAC,CAWX,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * A small bounded cache, used by `hkdf.ts` for derived `CryptoKey`s.
3
+ *
4
+ * Insertion-order eviction, not true LRU — simpler, and the expected key
5
+ * count (a handful of rotation entries, rarely changing at runtime) stays
6
+ * well under the cap in ordinary use, so eviction order rarely matters in
7
+ * practice. The cap itself exists for the discouraged-but-possible case of
8
+ * a function-form `codeSecret` returning a different value on every call,
9
+ * which would otherwise grow this cache without bound.
10
+ */
11
+ export const createBoundedCache = (limit) => {
12
+ const map = new Map();
13
+ return {
14
+ get: (key) => map.get(key),
15
+ set: (key, value) => {
16
+ if (!map.has(key) && map.size >= limit) {
17
+ const oldest = map.keys().next().value;
18
+ if (oldest !== undefined)
19
+ map.delete(oldest);
20
+ }
21
+ map.set(key, value);
22
+ },
23
+ delete: (key) => {
24
+ map.delete(key);
25
+ },
26
+ };
27
+ };
28
+ /** Cache a promise; drop the entry if it rejects so the slot is not poisoned. */
29
+ export const settleCached = async (cache, key, create) => {
30
+ const cached = cache.get(key);
31
+ if (cached)
32
+ return cached;
33
+ const pending = create();
34
+ cache.set(key, pending);
35
+ try {
36
+ return await pending;
37
+ }
38
+ catch (caught) {
39
+ if (cache.get(key) === pending)
40
+ cache.delete(key);
41
+ throw caught;
42
+ }
43
+ };
44
+ //# sourceMappingURL=keycache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keycache.js","sourceRoot":"","sources":["../../../src/oauth/crypto/keycache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAQH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAI,KAAa,EAAmB,EAAE;IACtE,MAAM,GAAG,GAAG,IAAI,GAAG,EAAa,CAAC;IACjC,OAAO;QACL,GAAG,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC;QAC1B,GAAG,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE;YAClB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,IAAI,IAAI,KAAK,EAAE,CAAC;gBACvC,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;gBACvC,IAAI,MAAM,KAAK,SAAS;oBAAE,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC/C,CAAC;YACD,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACtB,CAAC;QACD,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE;YACd,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAClB,CAAC;KACF,CAAC;AACJ,CAAC,CAAC;AAEF,iFAAiF;AACjF,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAC/B,KAA+B,EAC/B,GAAW,EACX,MAAwB,EACZ,EAAE;IACd,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC9B,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAC1B,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC;IACzB,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACxB,IAAI,CAAC;QACH,OAAO,MAAM,OAAO,CAAC;IACvB,CAAC;IAAC,OAAO,MAAM,EAAE,CAAC;QAChB,IAAI,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,OAAO;YAAE,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAClD,MAAM,MAAM,CAAC;IACf,CAAC;AACH,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pkce.d.ts","sourceRoot":"","sources":["../../../src/oauth/crypto/pkce.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,eAAe,GAAU,OAAO,MAAM,KAAG,OAAO,CAAC,MAAM,CAGnE,CAAC;AAEF,eAAO,MAAM,cAAc,GAAU,UAAU,MAAM,EAAE,WAAW,MAAM,KAAG,OAAO,CAAC,OAAO,CAIzF,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,SAAS,MAAM,KAAG,MAGjD,CAAC"}
@@ -1,4 +1,4 @@
1
- import { timingSafeEqual } from "../auth/bearer.js";
1
+ import { timingSafeEqual } from "../../auth/bearer.js";
2
2
  import { bytesToBase64Url } from "./base64url.js";
3
3
  export const sha256Base64Url = async (input) => {
4
4
  const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(input));
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pkce.js","sourceRoot":"","sources":["../../../src/oauth/crypto/pkce.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,EAAE,KAAa,EAAmB,EAAE;IACtE,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACtF,OAAO,gBAAgB,CAAC,MAAM,CAAC,CAAC;AAClC,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,EAAE,QAAgB,EAAE,SAAiB,EAAoB,EAAE;IAC5F,IAAI,CAAC,QAAQ,IAAI,CAAC,SAAS;QAAE,OAAO,KAAK,CAAC;IAC1C,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,QAAQ,CAAC,CAAC;IACjD,OAAO,eAAe,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;AAC9C,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,OAAe,EAAU,EAAE;IACzD,MAAM,KAAK,GAAG,MAAM,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;IAC9D,OAAO,gBAAgB,CAAC,KAAK,CAAC,CAAC;AACjC,CAAC,CAAC"}
@@ -1,7 +1,6 @@
1
1
  /**
2
- * AES-GCM sealing for short-lived, self-describing tokens derived from the
3
- * same `codeSecret` as auth codes (HMAC today, AES-GCM from Phase 2 —
4
- * see `codes.ts`).
2
+ * AES-GCM sealing for short-lived, self-describing tokens (auth codes,
3
+ * consent tickets, DCR client assertions), all keyed from `codeSecret`.
5
4
  *
6
5
  * Domain separation is mandatory, not a nice-to-have: a consent ticket must
7
6
  * never unseal as a client-id assertion, and neither must ever unseal as an
@@ -14,4 +13,14 @@ export type SealedType = "consent" | "client" | "code";
14
13
  export declare const seal: <T>(secret: string, type: SealedType, payload: T) => Promise<string>;
15
14
  /** Unseals a string sealed with the same `secret` and `type`; `null` on any mismatch or tamper. */
16
15
  export declare const unseal: <T>(secret: string, type: SealedType, sealed: string) => Promise<T | null>;
16
+ /**
17
+ * Rotation-aware unseal: tries each `secrets` entry in order, returning the
18
+ * index of the one that actually worked so a caller can audit "sealed under
19
+ * a non-primary key" without a second decrypt pass. `secrets` is expected
20
+ * non-empty (callers resolve it via `resolveSecrets`, which enforces that).
21
+ */
22
+ export declare const unsealAny: <T>(secrets: readonly string[], type: SealedType, sealed: string) => Promise<{
23
+ value: T;
24
+ keyIndex: number;
25
+ } | null>;
17
26
  //# sourceMappingURL=sealed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sealed.d.ts","sourceRoot":"","sources":["../../../src/oauth/crypto/sealed.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAC;AAevD,uDAAuD;AACvD,eAAO,MAAM,IAAI,GAAU,CAAC,EAAE,QAAQ,MAAM,EAAE,MAAM,UAAU,EAAE,SAAS,CAAC,KAAG,OAAO,CAAC,MAAM,CAW1F,CAAC;AAEF,mGAAmG;AACnG,eAAO,MAAM,MAAM,GAAU,CAAC,EAC5B,QAAQ,MAAM,EACd,MAAM,UAAU,EAChB,QAAQ,MAAM,KACb,OAAO,CAAC,CAAC,GAAG,IAAI,CAsBlB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,SAAS,GAAU,CAAC,EAC/B,SAAS,SAAS,MAAM,EAAE,EAC1B,MAAM,UAAU,EAChB,QAAQ,MAAM,KACb,OAAO,CAAC;IAAE,KAAK,EAAE,CAAC,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAM/C,CAAC"}
@@ -1,7 +1,6 @@
1
1
  /**
2
- * AES-GCM sealing for short-lived, self-describing tokens derived from the
3
- * same `codeSecret` as auth codes (HMAC today, AES-GCM from Phase 2 —
4
- * see `codes.ts`).
2
+ * AES-GCM sealing for short-lived, self-describing tokens (auth codes,
3
+ * consent tickets, DCR client assertions), all keyed from `codeSecret`.
5
4
  *
6
5
  * Domain separation is mandatory, not a nice-to-have: a consent ticket must
7
6
  * never unseal as a client-id assertion, and neither must ever unseal as an
@@ -10,20 +9,15 @@
10
9
  * two independent reasons decryption fails across types, not one.
11
10
  */
12
11
  import { bytesToBase64Url, fromBase64Url } from "./base64url.js";
12
+ import { hkdfKey } from "./hkdf.js";
13
13
  const HKDF_INFO_PREFIX = "mcp-trellis:sealed:";
14
14
  const IV_BYTES = 12;
15
- const deriveKey = async (secret, type, usage) => {
16
- const material = await crypto.subtle.importKey("raw", new TextEncoder().encode(secret), "HKDF", false, ["deriveKey"]);
17
- return crypto.subtle.deriveKey({
18
- name: "HKDF",
19
- hash: "SHA-256",
20
- salt: new Uint8Array(0),
21
- info: new TextEncoder().encode(`${HKDF_INFO_PREFIX}${type}`),
22
- }, material, { name: "AES-GCM", length: 256 }, false, [usage]);
23
- };
15
+ const AES_GCM_256 = { name: "AES-GCM", length: 256 };
16
+ /** Same key material for "encrypt" and "decrypt" — WebCrypto just restricts the derived key's own `usages`. */
17
+ const sealingKey = (secret, type, usage) => hkdfKey(secret, { info: `${HKDF_INFO_PREFIX}${type}`, algorithm: AES_GCM_256, usage });
24
18
  /** Seals `payload` as an opaque, type-bound string. */
25
19
  export const seal = async (secret, type, payload) => {
26
- const key = await deriveKey(secret, type, "encrypt");
20
+ const key = await sealingKey(secret, type, "encrypt");
27
21
  const iv = crypto.getRandomValues(new Uint8Array(IV_BYTES));
28
22
  const additionalData = new TextEncoder().encode(type);
29
23
  const plaintext = new TextEncoder().encode(JSON.stringify(payload));
@@ -41,7 +35,7 @@ export const unseal = async (secret, type, sealed) => {
41
35
  if (!ivPart || !ciphertextPart)
42
36
  return null;
43
37
  try {
44
- const key = await deriveKey(secret, type, "decrypt");
38
+ const key = await sealingKey(secret, type, "decrypt");
45
39
  const iv = fromBase64Url(ivPart);
46
40
  if (iv.byteLength !== IV_BYTES)
47
41
  return null;
@@ -54,4 +48,18 @@ export const unseal = async (secret, type, sealed) => {
54
48
  return null;
55
49
  }
56
50
  };
51
+ /**
52
+ * Rotation-aware unseal: tries each `secrets` entry in order, returning the
53
+ * index of the one that actually worked so a caller can audit "sealed under
54
+ * a non-primary key" without a second decrypt pass. `secrets` is expected
55
+ * non-empty (callers resolve it via `resolveSecrets`, which enforces that).
56
+ */
57
+ export const unsealAny = async (secrets, type, sealed) => {
58
+ for (const [keyIndex, secret] of secrets.entries()) {
59
+ const value = await unseal(secret, type, sealed);
60
+ if (value !== null)
61
+ return { value, keyIndex };
62
+ }
63
+ return null;
64
+ };
57
65
  //# sourceMappingURL=sealed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sealed.js","sourceRoot":"","sources":["../../../src/oauth/crypto/sealed.ts"],"names":[],"mappings":"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"}
@@ -41,4 +41,18 @@ export declare const hashClientSecret: (secret: string, codeSecretValue: string)
41
41
  * the same check outside the token endpoint (e.g. a client-management API).
42
42
  */
43
43
  export declare const verifyClientSecret: (presented: string, stored: string, codeSecretValue: string) => Promise<boolean>;
44
+ /**
45
+ * Rotation-aware `verifyClientSecret`: a hash stored under an older
46
+ * `codeSecret` must keep verifying until it's re-hashed under the current
47
+ * one. Always tries every entry in `codeSecretValues` — even after an
48
+ * earlier one matches — for the *unknown-client* dummy-compare call site in
49
+ * `clientauth.ts`, where `stored` is a fixed dummy hash that never matches
50
+ * any real key: looping the same number of times whether or not a key list
51
+ * is configured for rotation keeps that call's cost from leaking how many
52
+ * keys are configured.
53
+ */
54
+ export declare const verifyClientSecretAny: (presented: string, stored: string, codeSecretValues: readonly string[]) => Promise<{
55
+ ok: boolean;
56
+ keyIndex: number | null;
57
+ }>;
44
58
  //# sourceMappingURL=secrethash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrethash.d.ts","sourceRoot":"","sources":["../../../src/oauth/crypto/secrethash.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAWH;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAC3B,QAAQ,MAAM,EACd,iBAAiB,MAAM,KACtB,OAAO,CAAC,MAAM,CAQhB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAC7B,WAAW,MAAM,EACjB,QAAQ,MAAM,EACd,iBAAiB,MAAM,KACtB,OAAO,CAAC,OAAO,CAIjB,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,qBAAqB,GAChC,WAAW,MAAM,EACjB,QAAQ,MAAM,EACd,kBAAkB,SAAS,MAAM,EAAE,KAClC,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAQlD,CAAC"}
@@ -27,19 +27,12 @@
27
27
  * are human-chosen, don't use this — do a real password hash inside your
28
28
  * own `verifySecret` instead.
29
29
  */
30
- import { timingSafeEqual } from "../auth/bearer.js";
30
+ import { timingSafeEqual } from "../../auth/bearer.js";
31
31
  import { bytesToBase64Url } from "./base64url.js";
32
+ import { hkdfKey } from "./hkdf.js";
32
33
  const HASH_PREFIX = "hmac-sha256$";
33
34
  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
- };
35
+ const HMAC_SHA256 = { name: "HMAC", hash: "SHA-256", length: 256 };
43
36
  /**
44
37
  * Hash a client secret for storage in your `ClientStore.secretHash` lookup.
45
38
  * Call this once, at registration time, with the same `codeSecret` value
@@ -47,7 +40,11 @@ const deriveHmacKey = async (codeSecretValue) => {
47
40
  * the result, never the plaintext secret.
48
41
  */
49
42
  export const hashClientSecret = async (secret, codeSecretValue) => {
50
- const key = await deriveHmacKey(codeSecretValue);
43
+ const key = await hkdfKey(codeSecretValue, {
44
+ info: HKDF_INFO,
45
+ algorithm: HMAC_SHA256,
46
+ usage: "sign",
47
+ });
51
48
  const sig = await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(secret));
52
49
  return `${HASH_PREFIX}${bytesToBase64Url(sig)}`;
53
50
  };
@@ -63,4 +60,23 @@ export const verifyClientSecret = async (presented, stored, codeSecretValue) =>
63
60
  const recomputed = await hashClientSecret(presented, codeSecretValue);
64
61
  return timingSafeEqual(recomputed, stored);
65
62
  };
63
+ /**
64
+ * Rotation-aware `verifyClientSecret`: a hash stored under an older
65
+ * `codeSecret` must keep verifying until it's re-hashed under the current
66
+ * one. Always tries every entry in `codeSecretValues` — even after an
67
+ * earlier one matches — for the *unknown-client* dummy-compare call site in
68
+ * `clientauth.ts`, where `stored` is a fixed dummy hash that never matches
69
+ * any real key: looping the same number of times whether or not a key list
70
+ * is configured for rotation keeps that call's cost from leaking how many
71
+ * keys are configured.
72
+ */
73
+ export const verifyClientSecretAny = async (presented, stored, codeSecretValues) => {
74
+ let matchedIndex = null;
75
+ for (const [keyIndex, codeSecretValue] of codeSecretValues.entries()) {
76
+ if (await verifyClientSecret(presented, stored, codeSecretValue)) {
77
+ matchedIndex ??= keyIndex;
78
+ }
79
+ }
80
+ return { ok: matchedIndex !== null, keyIndex: matchedIndex };
81
+ };
66
82
  //# sourceMappingURL=secrethash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrethash.js","sourceRoot":"","sources":["../../../src/oauth/crypto/secrethash.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,WAAW,GAAG,cAAc,CAAC;AACnC,MAAM,SAAS,GAAG,gCAAgC,CAAC;AAEnD,MAAM,WAAW,GAAqB,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;AAErF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,EACnC,MAAc,EACd,eAAuB,EACN,EAAE;IACnB,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,eAAe,EAAE;QACzC,IAAI,EAAE,SAAS;QACf,SAAS,EAAE,WAAW;QACtB,KAAK,EAAE,MAAM;KACd,CAAC,CAAC;IACH,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;AAEF;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,KAAK,EACxC,SAAiB,EACjB,MAAc,EACd,gBAAmC,EACgB,EAAE;IACrD,IAAI,YAAY,GAAkB,IAAI,CAAC;IACvC,KAAK,MAAM,CAAC,QAAQ,EAAE,eAAe,CAAC,IAAI,gBAAgB,CAAC,OAAO,EAAE,EAAE,CAAC;QACrE,IAAI,MAAM,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,CAAC;YACjE,YAAY,KAAK,QAAQ,CAAC;QAC5B,CAAC;IACH,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,YAAY,KAAK,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAC;AAC/D,CAAC,CAAC"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * `mcp-trellis/oauth` aliases for symbols that moved to
3
+ * `mcp-trellis/advanced`. Declared as consts so `@deprecated` reaches
4
+ * editors. Removed in 3.0.
5
+ */
6
+ import * as advanced from "../advanced.js";
7
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
8
+ export declare const readClientAuth: (request: Request, body: Record<string, string>) => advanced.ClientAuth | null;
9
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
10
+ export declare const firstClientAuthError: (auth: advanced.ClientAuth, options: import("./types.js").OAuthRouterOptions, request: Request) => Promise<Response | null>;
11
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
12
+ export declare const firstResourceError: (requested: string, expected: string) => Response | null;
13
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
14
+ export declare const resourceErrorInfo: (requested: string, expected: string) => import("./types.js").OAuthErrorInfo | null;
15
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
16
+ export declare const normalizeConfiguredPath: (path: string) => string;
17
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
18
+ export declare const firstScopeError: (requested: string[], advertised: string[]) => Response | null;
19
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
20
+ export declare const scopeErrorInfo: (requested: string[], advertised: string[]) => import("./types.js").OAuthErrorInfo | null;
21
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
22
+ export declare const parseScope: (raw: string) => string[];
23
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
24
+ export declare const requestedScopes: (raw: string, fallback: string[]) => string[];
25
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
26
+ export declare const formatScope: (scopes: string[]) => string;
27
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
28
+ export declare const issueAuthCode: (secret: string, record: Omit<advanced.AuthCodeRecord, "exp">, nowMs?: number) => Promise<string>;
29
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
30
+ export declare const consumeAuthCode: (secret: string | readonly string[], code: string, options?: {
31
+ codeStore?: advanced.CodeStore | undefined;
32
+ nowMs?: number | undefined;
33
+ }) => Promise<advanced.AuthCodeRecord | null>;
34
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
35
+ export declare const newClientId: () => string;
36
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
37
+ export declare const randomBase64Url: (byteLen: number) => string;
38
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
39
+ export declare const sha256Base64Url: (input: string) => Promise<string>;
40
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
41
+ export declare const verifyPkceS256: (verifier: string, challenge: string) => Promise<boolean>;
42
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
43
+ export declare const buildErrorRedirectUrl: (redirectUri: string, error: string, description: string, state: string, issuer?: string) => string;
44
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
45
+ export declare const advertisedScopes: (options: import("./types.js").OAuthRouterOptions) => string[];
46
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
47
+ export declare const defaultScopes: (options: import("./types.js").OAuthRouterOptions) => string[];
48
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
49
+ export declare const registeredClientsRequired: (options: import("./types.js").OAuthRouterOptions) => boolean;
50
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
51
+ export declare const unregisteredClientsAllowed: (options: import("./types.js").OAuthRouterOptions) => boolean;
52
+ //# sourceMappingURL=deprecated.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deprecated.d.ts","sourceRoot":"","sources":["../../src/oauth/deprecated.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAE3C,sEAAsE;AACtE,eAAO,MAAM,cAAc,gFAA0B,CAAC;AACtD,sEAAsE;AACtE,eAAO,MAAM,oBAAoB,6HAAgC,CAAC;AAClE,sEAAsE;AACtE,eAAO,MAAM,kBAAkB,0DAA8B,CAAC;AAC9D,sEAAsE;AACtE,eAAO,MAAM,iBAAiB,qFAA6B,CAAC;AAC5D,sEAAsE;AACtE,eAAO,MAAM,uBAAuB,0BAAmC,CAAC;AACxE,sEAAsE;AACtE,eAAO,MAAM,eAAe,gEAA2B,CAAC;AACxD,sEAAsE;AACtE,eAAO,MAAM,cAAc,2FAA0B,CAAC;AACtD,sEAAsE;AACtE,eAAO,MAAM,UAAU,2BAAsB,CAAC;AAC9C,sEAAsE;AACtE,eAAO,MAAM,eAAe,+CAA2B,CAAC;AACxD,sEAAsE;AACtE,eAAO,MAAM,WAAW,8BAAuB,CAAC;AAChD,sEAAsE;AACtE,eAAO,MAAM,aAAa,mGAAyB,CAAC;AACpD,sEAAsE;AACtE,eAAO,MAAM,eAAe;aAmBqzE,CAAC;SAA8B,CAAC;6CAnB1zE,CAAC;AACxD,sEAAsE;AACtE,eAAO,MAAM,WAAW,cAAuB,CAAC;AAChD,sEAAsE;AACtE,eAAO,MAAM,eAAe,6BAA2B,CAAC;AACxD,sEAAsE;AACtE,eAAO,MAAM,eAAe,oCAA2B,CAAC;AACxD,sEAAsE;AACtE,eAAO,MAAM,cAAc,2DAA0B,CAAC;AACtD,sEAAsE;AACtE,eAAO,MAAM,qBAAqB,qGAAiC,CAAC;AACpE,sEAAsE;AACtE,eAAO,MAAM,gBAAgB,gEAA4B,CAAC;AAC1D,sEAAsE;AACtE,eAAO,MAAM,aAAa,gEAAyB,CAAC;AACpD,sEAAsE;AACtE,eAAO,MAAM,yBAAyB,+DAAqC,CAAC;AAC5E,sEAAsE;AACtE,eAAO,MAAM,0BAA0B,+DAAsC,CAAC"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * `mcp-trellis/oauth` aliases for symbols that moved to
3
+ * `mcp-trellis/advanced`. Declared as consts so `@deprecated` reaches
4
+ * editors. Removed in 3.0.
5
+ */
6
+ import * as advanced from "../advanced.js";
7
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
8
+ export const readClientAuth = advanced.readClientAuth;
9
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
10
+ export const firstClientAuthError = advanced.firstClientAuthError;
11
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
12
+ export const firstResourceError = advanced.firstResourceError;
13
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
14
+ export const resourceErrorInfo = advanced.resourceErrorInfo;
15
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
16
+ export const normalizeConfiguredPath = advanced.normalizeConfiguredPath;
17
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
18
+ export const firstScopeError = advanced.firstScopeError;
19
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
20
+ export const scopeErrorInfo = advanced.scopeErrorInfo;
21
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
22
+ export const parseScope = advanced.parseScope;
23
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
24
+ export const requestedScopes = advanced.requestedScopes;
25
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
26
+ export const formatScope = advanced.formatScope;
27
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
28
+ export const issueAuthCode = advanced.issueAuthCode;
29
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
30
+ export const consumeAuthCode = advanced.consumeAuthCode;
31
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
32
+ export const newClientId = advanced.newClientId;
33
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
34
+ export const randomBase64Url = advanced.randomBase64Url;
35
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
36
+ export const sha256Base64Url = advanced.sha256Base64Url;
37
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
38
+ export const verifyPkceS256 = advanced.verifyPkceS256;
39
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
40
+ export const buildErrorRedirectUrl = advanced.buildErrorRedirectUrl;
41
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
42
+ export const advertisedScopes = advanced.advertisedScopes;
43
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
44
+ export const defaultScopes = advanced.defaultScopes;
45
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
46
+ export const registeredClientsRequired = advanced.registeredClientsRequired;
47
+ /** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
48
+ export const unregisteredClientsAllowed = advanced.unregisteredClientsAllowed;
49
+ //# sourceMappingURL=deprecated.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deprecated.js","sourceRoot":"","sources":["../../src/oauth/deprecated.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAE3C,sEAAsE;AACtE,MAAM,CAAC,MAAM,cAAc,GAAG,QAAQ,CAAC,cAAc,CAAC;AACtD,sEAAsE;AACtE,MAAM,CAAC,MAAM,oBAAoB,GAAG,QAAQ,CAAC,oBAAoB,CAAC;AAClE,sEAAsE;AACtE,MAAM,CAAC,MAAM,kBAAkB,GAAG,QAAQ,CAAC,kBAAkB,CAAC;AAC9D,sEAAsE;AACtE,MAAM,CAAC,MAAM,iBAAiB,GAAG,QAAQ,CAAC,iBAAiB,CAAC;AAC5D,sEAAsE;AACtE,MAAM,CAAC,MAAM,uBAAuB,GAAG,QAAQ,CAAC,uBAAuB,CAAC;AACxE,sEAAsE;AACtE,MAAM,CAAC,MAAM,eAAe,GAAG,QAAQ,CAAC,eAAe,CAAC;AACxD,sEAAsE;AACtE,MAAM,CAAC,MAAM,cAAc,GAAG,QAAQ,CAAC,cAAc,CAAC;AACtD,sEAAsE;AACtE,MAAM,CAAC,MAAM,UAAU,GAAG,QAAQ,CAAC,UAAU,CAAC;AAC9C,sEAAsE;AACtE,MAAM,CAAC,MAAM,eAAe,GAAG,QAAQ,CAAC,eAAe,CAAC;AACxD,sEAAsE;AACtE,MAAM,CAAC,MAAM,WAAW,GAAG,QAAQ,CAAC,WAAW,CAAC;AAChD,sEAAsE;AACtE,MAAM,CAAC,MAAM,aAAa,GAAG,QAAQ,CAAC,aAAa,CAAC;AACpD,sEAAsE;AACtE,MAAM,CAAC,MAAM,eAAe,GAAG,QAAQ,CAAC,eAAe,CAAC;AACxD,sEAAsE;AACtE,MAAM,CAAC,MAAM,WAAW,GAAG,QAAQ,CAAC,WAAW,CAAC;AAChD,sEAAsE;AACtE,MAAM,CAAC,MAAM,eAAe,GAAG,QAAQ,CAAC,eAAe,CAAC;AACxD,sEAAsE;AACtE,MAAM,CAAC,MAAM,eAAe,GAAG,QAAQ,CAAC,eAAe,CAAC;AACxD,sEAAsE;AACtE,MAAM,CAAC,MAAM,cAAc,GAAG,QAAQ,CAAC,cAAc,CAAC;AACtD,sEAAsE;AACtE,MAAM,CAAC,MAAM,qBAAqB,GAAG,QAAQ,CAAC,qBAAqB,CAAC;AACpE,sEAAsE;AACtE,MAAM,CAAC,MAAM,gBAAgB,GAAG,QAAQ,CAAC,gBAAgB,CAAC;AAC1D,sEAAsE;AACtE,MAAM,CAAC,MAAM,aAAa,GAAG,QAAQ,CAAC,aAAa,CAAC;AACpD,sEAAsE;AACtE,MAAM,CAAC,MAAM,yBAAyB,GAAG,QAAQ,CAAC,yBAAyB,CAAC;AAC5E,sEAAsE;AACtE,MAAM,CAAC,MAAM,0BAA0B,GAAG,QAAQ,CAAC,0BAA0B,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { ResolvedOAuthRouterOptions } from "../types.js";
2
+ export declare const handleAuthorize: (request: Request, options: ResolvedOAuthRouterOptions) => Promise<Response>;
3
+ //# sourceMappingURL=authorize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authorize.d.ts","sourceRoot":"","sources":["../../../src/oauth/endpoints/authorize.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAKV,0BAA0B,EAC3B,MAAM,aAAa,CAAC;AAiPrB,eAAO,MAAM,eAAe,GAC1B,SAAS,OAAO,EAChB,SAAS,0BAA0B,KAClC,OAAO,CAAC,QAAQ,CAuClB,CAAC"}