mcp-trellis 1.0.0 → 2.1.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 (296) hide show
  1. package/README.md +63 -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/clients.js +1 -1
  31. package/dist/clients.js.map +1 -1
  32. package/dist/deprecated.d.ts +42 -0
  33. package/dist/deprecated.d.ts.map +1 -0
  34. package/dist/deprecated.js +35 -0
  35. package/dist/deprecated.js.map +1 -0
  36. package/dist/{body.d.ts → http/body.d.ts} +27 -6
  37. package/dist/http/body.d.ts.map +1 -0
  38. package/dist/{body.js → http/body.js} +33 -13
  39. package/dist/http/body.js.map +1 -0
  40. package/dist/http/http.d.ts +42 -0
  41. package/dist/http/http.d.ts.map +1 -0
  42. package/dist/http/http.js +100 -0
  43. package/dist/http/http.js.map +1 -0
  44. package/dist/http/origins.d.ts +37 -0
  45. package/dist/http/origins.d.ts.map +1 -0
  46. package/dist/http/origins.js +87 -0
  47. package/dist/http/origins.js.map +1 -0
  48. package/dist/{dispatch.d.ts → mcp/dispatch.d.ts} +1 -1
  49. package/dist/mcp/dispatch.d.ts.map +1 -0
  50. package/dist/mcp/dispatch.js +99 -0
  51. package/dist/mcp/dispatch.js.map +1 -0
  52. package/dist/{jsonrpc.d.ts → mcp/jsonrpc.d.ts} +9 -0
  53. package/dist/mcp/jsonrpc.d.ts.map +1 -0
  54. package/dist/mcp/jsonrpc.js +88 -0
  55. package/dist/mcp/jsonrpc.js.map +1 -0
  56. package/dist/{methods.d.ts → mcp/methods.d.ts} +60 -11
  57. package/dist/mcp/methods.d.ts.map +1 -0
  58. package/dist/mcp/methods.js +84 -0
  59. package/dist/mcp/methods.js.map +1 -0
  60. package/dist/{protocol.d.ts → mcp/protocol.d.ts} +1 -3
  61. package/dist/mcp/protocol.d.ts.map +1 -0
  62. package/dist/{protocol.js → mcp/protocol.js} +1 -3
  63. package/dist/mcp/protocol.js.map +1 -0
  64. package/dist/mcp/registry.d.ts +58 -0
  65. package/dist/mcp/registry.d.ts.map +1 -0
  66. package/dist/mcp/registry.js +81 -0
  67. package/dist/mcp/registry.js.map +1 -0
  68. package/dist/mcp/rpc-handlers.d.ts +11 -0
  69. package/dist/mcp/rpc-handlers.d.ts.map +1 -0
  70. package/dist/mcp/rpc-handlers.js +133 -0
  71. package/dist/mcp/rpc-handlers.js.map +1 -0
  72. package/dist/{tools.d.ts → mcp/tools.d.ts} +20 -1
  73. package/dist/mcp/tools.d.ts.map +1 -0
  74. package/dist/mcp/tools.js +166 -0
  75. package/dist/mcp/tools.js.map +1 -0
  76. package/dist/{validate.d.ts → mcp/validate.d.ts} +18 -21
  77. package/dist/mcp/validate.d.ts.map +1 -0
  78. package/dist/{validate.js → mcp/validate.js} +59 -41
  79. package/dist/mcp/validate.js.map +1 -0
  80. package/dist/oauth/audit.d.ts +20 -0
  81. package/dist/oauth/audit.d.ts.map +1 -0
  82. package/dist/oauth/audit.js +29 -0
  83. package/dist/oauth/audit.js.map +1 -0
  84. package/dist/oauth/cimd/cimd-parse.d.ts +4 -0
  85. package/dist/oauth/cimd/cimd-parse.d.ts.map +1 -0
  86. package/dist/oauth/cimd/cimd-parse.js +70 -0
  87. package/dist/oauth/cimd/cimd-parse.js.map +1 -0
  88. package/dist/oauth/cimd/cimd-ssrf.d.ts +8 -0
  89. package/dist/oauth/cimd/cimd-ssrf.d.ts.map +1 -0
  90. package/dist/oauth/cimd/cimd-ssrf.js +193 -0
  91. package/dist/oauth/cimd/cimd-ssrf.js.map +1 -0
  92. package/dist/oauth/cimd/cimd.d.ts +36 -0
  93. package/dist/oauth/cimd/cimd.d.ts.map +1 -0
  94. package/dist/oauth/cimd/cimd.js +132 -0
  95. package/dist/oauth/cimd/cimd.js.map +1 -0
  96. package/dist/oauth/cimd/types.d.ts +12 -0
  97. package/dist/oauth/cimd/types.d.ts.map +1 -0
  98. package/dist/oauth/cimd/types.js +3 -0
  99. package/dist/oauth/cimd/types.js.map +1 -0
  100. package/dist/oauth/config.d.ts +51 -0
  101. package/dist/oauth/config.d.ts.map +1 -0
  102. package/dist/oauth/config.js +108 -0
  103. package/dist/oauth/config.js.map +1 -0
  104. package/dist/oauth/constants.d.ts +11 -0
  105. package/dist/oauth/constants.d.ts.map +1 -1
  106. package/dist/oauth/constants.js +11 -0
  107. package/dist/oauth/constants.js.map +1 -1
  108. package/dist/oauth/{base64url.d.ts → crypto/base64url.d.ts} +0 -1
  109. package/dist/oauth/crypto/base64url.d.ts.map +1 -0
  110. package/dist/oauth/{base64url.js → crypto/base64url.js} +0 -1
  111. package/dist/oauth/crypto/base64url.js.map +1 -0
  112. package/dist/oauth/crypto/clientauth.d.ts +13 -0
  113. package/dist/oauth/crypto/clientauth.d.ts.map +1 -0
  114. package/dist/oauth/crypto/clientauth.js +195 -0
  115. package/dist/oauth/crypto/clientauth.js.map +1 -0
  116. package/dist/oauth/crypto/codes.d.ts +65 -0
  117. package/dist/oauth/crypto/codes.d.ts.map +1 -0
  118. package/dist/oauth/crypto/codes.js +121 -0
  119. package/dist/oauth/crypto/codes.js.map +1 -0
  120. package/dist/oauth/crypto/hkdf.d.ts +13 -0
  121. package/dist/oauth/crypto/hkdf.d.ts.map +1 -0
  122. package/dist/oauth/crypto/hkdf.js +30 -0
  123. package/dist/oauth/crypto/hkdf.js.map +1 -0
  124. package/dist/oauth/crypto/keycache.d.ts +19 -0
  125. package/dist/oauth/crypto/keycache.d.ts.map +1 -0
  126. package/dist/oauth/crypto/keycache.js +44 -0
  127. package/dist/oauth/crypto/keycache.js.map +1 -0
  128. package/dist/oauth/crypto/pkce.d.ts.map +1 -0
  129. package/dist/oauth/{pkce.js → crypto/pkce.js} +1 -1
  130. package/dist/oauth/crypto/pkce.js.map +1 -0
  131. package/dist/oauth/{sealed.d.ts → crypto/sealed.d.ts} +12 -3
  132. package/dist/oauth/crypto/sealed.d.ts.map +1 -0
  133. package/dist/oauth/{sealed.js → crypto/sealed.js} +22 -14
  134. package/dist/oauth/crypto/sealed.js.map +1 -0
  135. package/dist/oauth/{secrethash.d.ts → crypto/secrethash.d.ts} +14 -0
  136. package/dist/oauth/crypto/secrethash.d.ts.map +1 -0
  137. package/dist/oauth/{secrethash.js → crypto/secrethash.js} +27 -11
  138. package/dist/oauth/crypto/secrethash.js.map +1 -0
  139. package/dist/oauth/deprecated.d.ts +52 -0
  140. package/dist/oauth/deprecated.d.ts.map +1 -0
  141. package/dist/oauth/deprecated.js +49 -0
  142. package/dist/oauth/deprecated.js.map +1 -0
  143. package/dist/oauth/endpoints/authorize.d.ts +3 -0
  144. package/dist/oauth/endpoints/authorize.d.ts.map +1 -0
  145. package/dist/oauth/endpoints/authorize.js +209 -0
  146. package/dist/oauth/endpoints/authorize.js.map +1 -0
  147. package/dist/oauth/endpoints/callback.d.ts +20 -0
  148. package/dist/oauth/endpoints/callback.d.ts.map +1 -0
  149. package/dist/oauth/endpoints/callback.js +31 -0
  150. package/dist/oauth/endpoints/callback.js.map +1 -0
  151. package/dist/oauth/endpoints/consent-page.d.ts +5 -0
  152. package/dist/oauth/endpoints/consent-page.d.ts.map +1 -0
  153. package/dist/oauth/endpoints/consent-page.js +63 -0
  154. package/dist/oauth/endpoints/consent-page.js.map +1 -0
  155. package/dist/oauth/endpoints/consent.d.ts +29 -0
  156. package/dist/oauth/endpoints/consent.d.ts.map +1 -0
  157. package/dist/oauth/endpoints/consent.js +98 -0
  158. package/dist/oauth/endpoints/consent.js.map +1 -0
  159. package/dist/oauth/{metadata.d.ts → endpoints/metadata.d.ts} +8 -2
  160. package/dist/oauth/endpoints/metadata.d.ts.map +1 -0
  161. package/dist/oauth/{metadata.js → endpoints/metadata.js} +10 -4
  162. package/dist/oauth/endpoints/metadata.js.map +1 -0
  163. package/dist/oauth/endpoints/register.d.ts +10 -0
  164. package/dist/oauth/endpoints/register.d.ts.map +1 -0
  165. package/dist/oauth/endpoints/register.js +96 -0
  166. package/dist/oauth/endpoints/register.js.map +1 -0
  167. package/dist/oauth/{revoke.d.ts → endpoints/revoke.d.ts} +1 -1
  168. package/dist/oauth/endpoints/revoke.d.ts.map +1 -0
  169. package/dist/oauth/{revoke.js → endpoints/revoke.js} +8 -18
  170. package/dist/oauth/endpoints/revoke.js.map +1 -0
  171. package/dist/oauth/endpoints/token.d.ts +3 -0
  172. package/dist/oauth/endpoints/token.d.ts.map +1 -0
  173. package/dist/oauth/{token.js → endpoints/token.js} +32 -25
  174. package/dist/oauth/endpoints/token.js.map +1 -0
  175. package/dist/oauth/{wellknown.d.ts → endpoints/wellknown.d.ts} +1 -1
  176. package/dist/oauth/endpoints/wellknown.d.ts.map +1 -0
  177. package/dist/oauth/{wellknown.js → endpoints/wellknown.js} +7 -9
  178. package/dist/oauth/endpoints/wellknown.js.map +1 -0
  179. package/dist/oauth/policy/redirect.d.ts.map +1 -0
  180. package/dist/oauth/policy/redirect.js.map +1 -0
  181. package/dist/oauth/policy/reqbody.d.ts +18 -0
  182. package/dist/oauth/policy/reqbody.d.ts.map +1 -0
  183. package/dist/oauth/policy/reqbody.js +68 -0
  184. package/dist/oauth/policy/reqbody.js.map +1 -0
  185. package/dist/oauth/{resource.d.ts → policy/resource.d.ts} +1 -1
  186. package/dist/oauth/policy/resource.d.ts.map +1 -0
  187. package/dist/oauth/{resource.js → policy/resource.js} +2 -2
  188. package/dist/oauth/policy/resource.js.map +1 -0
  189. package/dist/oauth/{scope.d.ts → policy/scope.d.ts} +2 -2
  190. package/dist/oauth/policy/scope.d.ts.map +1 -0
  191. package/dist/oauth/{scope.js → policy/scope.js} +3 -3
  192. package/dist/oauth/policy/scope.js.map +1 -0
  193. package/dist/oauth/router.d.ts +10 -13
  194. package/dist/oauth/router.d.ts.map +1 -1
  195. package/dist/oauth/router.js +93 -115
  196. package/dist/oauth/router.js.map +1 -1
  197. package/dist/oauth/types.d.ts +99 -45
  198. package/dist/oauth/types.d.ts.map +1 -1
  199. package/dist/oauth/types.js +1 -90
  200. package/dist/oauth/types.js.map +1 -1
  201. package/dist/server.d.ts +12 -12
  202. package/dist/server.d.ts.map +1 -1
  203. package/dist/server.js +9 -8
  204. package/dist/server.js.map +1 -1
  205. package/dist/util/defined.d.ts +11 -0
  206. package/dist/util/defined.d.ts.map +1 -0
  207. package/dist/util/defined.js +15 -0
  208. package/dist/util/defined.js.map +1 -0
  209. package/dist/util/json.d.ts +3 -0
  210. package/dist/util/json.d.ts.map +1 -0
  211. package/dist/util/json.js +3 -0
  212. package/dist/util/json.js.map +1 -0
  213. package/docs/ROADMAP.md +35 -47
  214. package/docs/compatibility.md +84 -0
  215. package/docs/diagrams/mcp-trellis-architecture.html +305 -0
  216. package/docs/guide.md +37 -13
  217. package/docs/reference.md +54 -22
  218. package/docs/security.md +58 -9
  219. package/docs/troubleshooting.md +19 -0
  220. package/package.json +23 -10
  221. package/dist/adapters/origins.d.ts +0 -19
  222. package/dist/adapters/origins.d.ts.map +0 -1
  223. package/dist/adapters/origins.js +0 -52
  224. package/dist/adapters/origins.js.map +0 -1
  225. package/dist/body.d.ts.map +0 -1
  226. package/dist/body.js.map +0 -1
  227. package/dist/dispatch.d.ts.map +0 -1
  228. package/dist/dispatch.js +0 -119
  229. package/dist/dispatch.js.map +0 -1
  230. package/dist/http.d.ts +0 -17
  231. package/dist/http.d.ts.map +0 -1
  232. package/dist/http.js +0 -38
  233. package/dist/http.js.map +0 -1
  234. package/dist/jsonrpc.d.ts.map +0 -1
  235. package/dist/jsonrpc.js +0 -18
  236. package/dist/jsonrpc.js.map +0 -1
  237. package/dist/methods.d.ts.map +0 -1
  238. package/dist/methods.js +0 -130
  239. package/dist/methods.js.map +0 -1
  240. package/dist/oauth/authorize.d.ts +0 -3
  241. package/dist/oauth/authorize.d.ts.map +0 -1
  242. package/dist/oauth/authorize.js +0 -141
  243. package/dist/oauth/authorize.js.map +0 -1
  244. package/dist/oauth/base64url.d.ts.map +0 -1
  245. package/dist/oauth/base64url.js.map +0 -1
  246. package/dist/oauth/clientauth.d.ts +0 -24
  247. package/dist/oauth/clientauth.d.ts.map +0 -1
  248. package/dist/oauth/clientauth.js +0 -119
  249. package/dist/oauth/clientauth.js.map +0 -1
  250. package/dist/oauth/codes.d.ts +0 -30
  251. package/dist/oauth/codes.d.ts.map +0 -1
  252. package/dist/oauth/codes.js +0 -85
  253. package/dist/oauth/codes.js.map +0 -1
  254. package/dist/oauth/consent.d.ts +0 -65
  255. package/dist/oauth/consent.d.ts.map +0 -1
  256. package/dist/oauth/consent.js +0 -173
  257. package/dist/oauth/consent.js.map +0 -1
  258. package/dist/oauth/metadata.d.ts.map +0 -1
  259. package/dist/oauth/metadata.js.map +0 -1
  260. package/dist/oauth/pkce.d.ts.map +0 -1
  261. package/dist/oauth/pkce.js.map +0 -1
  262. package/dist/oauth/redirect.d.ts.map +0 -1
  263. package/dist/oauth/redirect.js.map +0 -1
  264. package/dist/oauth/reqbody.d.ts +0 -10
  265. package/dist/oauth/reqbody.d.ts.map +0 -1
  266. package/dist/oauth/reqbody.js +0 -29
  267. package/dist/oauth/reqbody.js.map +0 -1
  268. package/dist/oauth/resource.d.ts.map +0 -1
  269. package/dist/oauth/resource.js.map +0 -1
  270. package/dist/oauth/revoke.d.ts.map +0 -1
  271. package/dist/oauth/revoke.js.map +0 -1
  272. package/dist/oauth/scope.d.ts.map +0 -1
  273. package/dist/oauth/scope.js.map +0 -1
  274. package/dist/oauth/sealed.d.ts.map +0 -1
  275. package/dist/oauth/sealed.js.map +0 -1
  276. package/dist/oauth/secrethash.d.ts.map +0 -1
  277. package/dist/oauth/secrethash.js.map +0 -1
  278. package/dist/oauth/token.d.ts +0 -3
  279. package/dist/oauth/token.d.ts.map +0 -1
  280. package/dist/oauth/token.js.map +0 -1
  281. package/dist/oauth/wellknown.d.ts.map +0 -1
  282. package/dist/oauth/wellknown.js.map +0 -1
  283. package/dist/protocol.d.ts.map +0 -1
  284. package/dist/protocol.js.map +0 -1
  285. package/dist/registry.d.ts +0 -37
  286. package/dist/registry.d.ts.map +0 -1
  287. package/dist/registry.js +0 -77
  288. package/dist/registry.js.map +0 -1
  289. package/dist/tools.d.ts.map +0 -1
  290. package/dist/tools.js +0 -98
  291. package/dist/tools.js.map +0 -1
  292. package/dist/validate.d.ts.map +0 -1
  293. package/dist/validate.js.map +0 -1
  294. /package/dist/oauth/{pkce.d.ts → crypto/pkce.d.ts} +0 -0
  295. /package/dist/oauth/{redirect.d.ts → policy/redirect.d.ts} +0 -0
  296. /package/dist/oauth/{redirect.js → policy/redirect.js} +0 -0
@@ -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":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,MAAM,gBAAgB,GAAG,qBAAqB,CAAC;AAC/C,MAAM,QAAQ,GAAG,EAAE,CAAC;AAEpB,MAAM,WAAW,GAAoB,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;AAEtE,+GAA+G;AAC/G,MAAM,UAAU,GAAG,CACjB,MAAc,EACd,IAAgB,EAChB,KAA4B,EACR,EAAE,CACtB,OAAO,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,GAAG,gBAAgB,GAAG,IAAI,EAAE,EAAE,SAAS,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC;AAEzF,uDAAuD;AACvD,MAAM,CAAC,MAAM,IAAI,GAAG,KAAK,EAAK,MAAc,EAAE,IAAgB,EAAE,OAAU,EAAmB,EAAE;IAC7F,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC,MAAM,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;IACtD,MAAM,EAAE,GAAG,MAAM,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC5D,MAAM,cAAc,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACtD,MAAM,SAAS,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IACpE,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,CAC5C,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,cAAc,EAAE,EACvC,GAAG,EACH,SAAS,CACV,CAAC;IACF,OAAO,GAAG,gBAAgB,CAAC,EAAE,CAAC,IAAI,gBAAgB,CAAC,UAAU,CAAC,EAAE,CAAC;AACnE,CAAC,CAAC;AAEF,mGAAmG;AACnG,MAAM,CAAC,MAAM,MAAM,GAAG,KAAK,EACzB,MAAc,EACd,IAAgB,EAChB,MAAc,EACK,EAAE;IACrB,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACpC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,GAAG,KAAK,CAAC;IACvC,IAAI,CAAC,MAAM,IAAI,CAAC,cAAc;QAAE,OAAO,IAAI,CAAC;IAE5C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC,MAAM,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;QACtD,MAAM,EAAE,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;QACjC,IAAI,EAAE,CAAC,UAAU,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QAC5C,MAAM,UAAU,GAAG,aAAa,CAAC,cAAc,CAAC,CAAC;QACjD,MAAM,cAAc,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACtD,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,CAC3C,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,cAAc,EAAE,EACvC,GAAG,EACH,UAAU,CACX,CAAC;QACF,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAM,CAAC;IAC9D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,KAAK,EAC5B,OAA0B,EAC1B,IAAgB,EAChB,MAAc,EACkC,EAAE;IAClD,KAAK,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;QACnD,MAAM,KAAK,GAAG,MAAM,MAAM,CAAI,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QACpD,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;IACjD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC,CAAC"}
@@ -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"}
@@ -0,0 +1,209 @@
1
+ import { GET_ONLY, requireHttpMethod } from "../../http/http.js";
2
+ import { auditLegacyKeyUsage } from "../audit.js";
3
+ import { cimdFetchOpts, isCimdClientId, resolveCimdClient } from "../cimd/cimd.js";
4
+ import { advertisedScopes, defaultScopes, oauthError, registeredClientsRequired, resolveSecrets, unregisteredClientError, } from "../config.js";
5
+ import { OAUTH_ERRORS } from "../constants.js";
6
+ import { issueAuthCode } from "../crypto/codes.js";
7
+ import { unsealAny } from "../crypto/sealed.js";
8
+ import { isAllowedRedirectUri } from "../policy/redirect.js";
9
+ import { canonicalResource, resourceErrorInfo } from "../policy/resource.js";
10
+ import { formatScope, requestedScopes, scopeErrorInfo } from "../policy/scope.js";
11
+ import { codeRedirect, errorRedirect, secureRedirect } from "./callback.js";
12
+ import { isPreApproved, issueConsentTicket, renderConsent } from "./consent.js";
13
+ /** BASE64URL(SHA-256(verifier)) without padding is always exactly 43 characters. */
14
+ const S256_CHALLENGE = /^[A-Za-z0-9_-]{43}$/;
15
+ /** Reflected into `Location` on denial — an unbounded `state` is a header-size DoS knob. */
16
+ const MAX_STATE_LENGTH = 2048;
17
+ /**
18
+ * A client bound to a known redirect-URI list — either pre-registered via
19
+ * `clientStore`, or a sealed assertion this server itself issued via
20
+ * `/register` (see `sealed.ts`). Unbound clients fall back to the global
21
+ * allowlist, unless `requireRegisteredClients` forbids that entirely.
22
+ */
23
+ const redirectAllowed = (redirectUri, boundRedirectUris, allowlist) => {
24
+ if (!redirectUri)
25
+ return false;
26
+ if (boundRedirectUris)
27
+ return boundRedirectUris.includes(redirectUri);
28
+ return isAllowedRedirectUri(redirectUri, allowlist);
29
+ };
30
+ const redirectToLogin = (request, url, options) => {
31
+ const next = `${url.pathname}${url.search}`;
32
+ const login = options.ports.loginUrl(request, next);
33
+ let loginTarget;
34
+ try {
35
+ loginTarget = new URL(login, url.origin);
36
+ }
37
+ catch {
38
+ return oauthError(OAUTH_ERRORS.invalidRequest, 500, "loginUrl must be absolute or same-origin relative");
39
+ }
40
+ if (loginTarget.protocol !== "http:" && loginTarget.protocol !== "https:") {
41
+ return oauthError(OAUTH_ERRORS.invalidRequest, 500, "loginUrl must be absolute or same-origin relative");
42
+ }
43
+ return secureRedirect(loginTarget.toString());
44
+ };
45
+ /**
46
+ * Resolves `client_id` against `clientStore`, this server's own sealed
47
+ * `/register` assertions, and (when enabled) CIMD — in that order — and
48
+ * rejects when none recognize it and the config requires one to. A sealed
49
+ * assertion binds its own `redirectUris` like a pre-registered client,
50
+ * without ever touching storage.
51
+ */
52
+ const resolveAuthorizeClient = async (options, clientId, secrets) => {
53
+ const registered = (await options.ports.clientStore?.get(clientId)) ?? null;
54
+ const assertion = registered ? null : await unsealClientAssertion(options, clientId, secrets);
55
+ const isCimdId = options.cimd === true && isCimdClientId(clientId);
56
+ const cimdDoc = !registered && !assertion && isCimdId
57
+ ? await resolveCimdClient(clientId, cimdFetchOpts(options))
58
+ : null;
59
+ if (!registered && !assertion && !cimdDoc) {
60
+ const unknown = unknownClientError(options, isCimdId);
61
+ if (unknown)
62
+ return unknown;
63
+ }
64
+ return {
65
+ registered,
66
+ boundRedirectUris: registered?.redirectUris ?? assertion?.redirectUris ?? cimdDoc?.redirect_uris ?? null,
67
+ };
68
+ };
69
+ /** This server's own `/register` assertion for `clientId`, auditing a legacy-key unseal. */
70
+ const unsealClientAssertion = async (options, clientId, secrets) => {
71
+ const unsealed = await unsealAny(secrets, "client", clientId);
72
+ await auditLegacyKeyUsage(options, {
73
+ clientId,
74
+ keyIndex: unsealed?.keyIndex,
75
+ source: "client assertion unsealed",
76
+ });
77
+ return unsealed?.value ?? null;
78
+ };
79
+ /** The rejection for a `client_id` nothing resolved, or null when the config admits it. */
80
+ const unknownClientError = (options, isCimdId) => {
81
+ const unregistered = unregisteredClientError(options, {
82
+ code: OAUTH_ERRORS.unauthorizedClient,
83
+ status: 400,
84
+ });
85
+ if (unregistered)
86
+ return unregistered;
87
+ if (isCimdId) {
88
+ return oauthError(OAUTH_ERRORS.unauthorizedClient, 400, "CIMD client_id could not be resolved");
89
+ }
90
+ if (!registeredClientsRequired(options))
91
+ return null;
92
+ return oauthError(OAUTH_ERRORS.unauthorizedClient, 400, "client_id must come from clientStore or this server's own /register");
93
+ };
94
+ /**
95
+ * Pre-trust tier: `redirect_uri` is not yet verified as belonging to a
96
+ * legitimate party, so these two stay direct JSON responses. Redirecting
97
+ * here is exactly the open-redirect risk RFC 6749 §4.1.2.1 exists to
98
+ * avoid — the URI itself is what's in question. An oversized `state` is
99
+ * included in this tier too, for a narrower reason: reflecting it back
100
+ * into the error redirect it would otherwise trigger recreates the
101
+ * header-size DoS the cap exists to prevent, on the one input the cap
102
+ * can't safely echo.
103
+ */
104
+ const firstPreTrustError = (callback, boundRedirectUris, options) => {
105
+ if (!redirectAllowed(callback.redirectUri, boundRedirectUris, options.redirect)) {
106
+ return oauthError(OAUTH_ERRORS.invalidRequest, 400, "invalid redirect_uri");
107
+ }
108
+ if (callback.state.length > MAX_STATE_LENGTH) {
109
+ return oauthError(OAUTH_ERRORS.invalidRequest, 400, `state must be at most ${MAX_STATE_LENGTH} characters`);
110
+ }
111
+ return null;
112
+ };
113
+ /**
114
+ * Post-trust tier: `redirect_uri` is now trusted, so every remaining error
115
+ * goes back to the client's own callback per RFC 6749 §4.1.2.1 instead of a
116
+ * bare JSON body the connector never parses.
117
+ */
118
+ const firstPostTrustError = ({ url, callback, expectedResource }, options) => {
119
+ const redirectError = (code, description) => errorRedirect(callback, code, description);
120
+ const responseType = url.searchParams.get("response_type") ?? "";
121
+ if (responseType !== "code") {
122
+ return redirectError(OAUTH_ERRORS.unsupportedResponseType, "response_type must be code");
123
+ }
124
+ const codeChallenge = url.searchParams.get("code_challenge") ?? "";
125
+ // RFC 7636 defaults an absent method to `plain`; this AS only supports
126
+ // S256, so an absent method is read as S256 for client compatibility —
127
+ // a plain-style challenge then fails the shape check below anyway.
128
+ const challengeMethod = url.searchParams.get("code_challenge_method") ?? "S256";
129
+ if (!codeChallenge || challengeMethod !== "S256") {
130
+ return redirectError(OAUTH_ERRORS.invalidRequest, "PKCE S256 code_challenge required");
131
+ }
132
+ if (!S256_CHALLENGE.test(codeChallenge)) {
133
+ return redirectError(OAUTH_ERRORS.invalidRequest, "code_challenge must be a base64url SHA-256 digest (43 characters)");
134
+ }
135
+ const resourceIssue = resourceErrorInfo(url.searchParams.get("resource") ?? "", expectedResource);
136
+ if (resourceIssue)
137
+ return redirectError(resourceIssue.code, resourceIssue.description);
138
+ const advertised = advertisedScopes(options);
139
+ const granted = requestedScopes(url.searchParams.get("scope") ?? "", defaultScopes(options));
140
+ const scopeIssue = scopeErrorInfo(granted, advertised);
141
+ if (scopeIssue)
142
+ return redirectError(scopeIssue.code, scopeIssue.description);
143
+ return { codeChallenge, granted };
144
+ };
145
+ /** Pre-approved clients skip straight to a code; everyone else gets a consent ticket. */
146
+ const issueAuthorizeOutcome = async (options, secrets, { context, clientId, registered, user, codeChallenge, granted }) => {
147
+ const { callback, expectedResource } = context;
148
+ const grant = {
149
+ clientId,
150
+ redirectUri: callback.redirectUri,
151
+ codeChallenge,
152
+ userId: user.id,
153
+ resource: expectedResource,
154
+ scope: formatScope(granted),
155
+ };
156
+ if (isPreApproved(options, clientId, registered)) {
157
+ return codeRedirect(callback, await issueAuthCode(secrets[0], grant));
158
+ }
159
+ const ticket = await issueConsentTicket(secrets[0], { ...grant, state: callback.state });
160
+ return renderConsent(options, {
161
+ clientId,
162
+ registeredClient: registered,
163
+ redirectUri: callback.redirectUri,
164
+ scope: granted,
165
+ resource: expectedResource,
166
+ user,
167
+ ticket,
168
+ oauthPath: options.oauthPath,
169
+ });
170
+ };
171
+ export const handleAuthorize = async (request, options) => {
172
+ const methodError = requireHttpMethod(request, GET_ONLY);
173
+ if (methodError)
174
+ return methodError;
175
+ const url = new URL(request.url);
176
+ const expectedResource = canonicalResource(url.origin, options.resourcePath);
177
+ const clientId = url.searchParams.get("client_id") ?? "";
178
+ if (!clientId) {
179
+ return oauthError(OAUTH_ERRORS.invalidRequest, 400, "client_id required");
180
+ }
181
+ const secrets = await resolveSecrets(options.ports, request);
182
+ const clientResolution = await resolveAuthorizeClient(options, clientId, secrets);
183
+ if (clientResolution instanceof Response)
184
+ return clientResolution;
185
+ const { registered, boundRedirectUris } = clientResolution;
186
+ const callback = {
187
+ redirectUri: url.searchParams.get("redirect_uri") ?? "",
188
+ state: url.searchParams.get("state") ?? "",
189
+ issuer: url.origin,
190
+ };
191
+ const preTrustError = firstPreTrustError(callback, boundRedirectUris, options);
192
+ if (preTrustError)
193
+ return preTrustError;
194
+ const context = { url, callback, expectedResource };
195
+ const validated = firstPostTrustError(context, options);
196
+ if (validated instanceof Response)
197
+ return validated;
198
+ const user = await options.ports.resolveUser(request);
199
+ if (!user)
200
+ return redirectToLogin(request, url, options);
201
+ return issueAuthorizeOutcome(options, secrets, {
202
+ ...validated,
203
+ context,
204
+ clientId,
205
+ registered,
206
+ user,
207
+ });
208
+ };
209
+ //# sourceMappingURL=authorize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authorize.js","sourceRoot":"","sources":["../../../src/oauth/endpoints/authorize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACnF,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,UAAU,EACV,yBAAyB,EACzB,cAAc,EACd,uBAAuB,GACxB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,oBAAoB,EAAiC,MAAM,uBAAuB,CAAC;AAC5F,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC7E,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAQlF,OAAO,EAAiB,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC3F,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAEhF,oFAAoF;AACpF,MAAM,cAAc,GAAG,qBAAqB,CAAC;AAC7C,4FAA4F;AAC5F,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAE9B;;;;;GAKG;AACH,MAAM,eAAe,GAAG,CACtB,WAAmB,EACnB,iBAAkC,EAClC,SAA+C,EACtC,EAAE;IACX,IAAI,CAAC,WAAW;QAAE,OAAO,KAAK,CAAC;IAC/B,IAAI,iBAAiB;QAAE,OAAO,iBAAiB,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IACtE,OAAO,oBAAoB,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;AACtD,CAAC,CAAC;AAEF,MAAM,eAAe,GAAG,CAAC,OAAgB,EAAE,GAAQ,EAAE,OAA2B,EAAY,EAAE;IAC5F,MAAM,IAAI,GAAG,GAAG,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC;IAC5C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACpD,IAAI,WAAgB,CAAC;IACrB,IAAI,CAAC;QACH,WAAW,GAAG,IAAI,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,UAAU,CACf,YAAY,CAAC,cAAc,EAC3B,GAAG,EACH,mDAAmD,CACpD,CAAC;IACJ,CAAC;IACD,IAAI,WAAW,CAAC,QAAQ,KAAK,OAAO,IAAI,WAAW,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC1E,OAAO,UAAU,CACf,YAAY,CAAC,cAAc,EAC3B,GAAG,EACH,mDAAmD,CACpD,CAAC;IACJ,CAAC;IACD,OAAO,cAAc,CAAC,WAAW,CAAC,QAAQ,EAAE,CAAC,CAAC;AAChD,CAAC,CAAC;AAOF;;;;;;GAMG;AACH,MAAM,sBAAsB,GAAG,KAAK,EAClC,OAA2B,EAC3B,QAAgB,EAChB,OAAuC,EACF,EAAE;IACvC,MAAM,UAAU,GAAG,CAAC,MAAM,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC,IAAI,IAAI,CAAC;IAC5E,MAAM,SAAS,GAAG,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,qBAAqB,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC9F,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,KAAK,IAAI,IAAI,cAAc,CAAC,QAAQ,CAAC,CAAC;IACnE,MAAM,OAAO,GACX,CAAC,UAAU,IAAI,CAAC,SAAS,IAAI,QAAQ;QACnC,CAAC,CAAC,MAAM,iBAAiB,CAAC,QAAQ,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;QAC3D,CAAC,CAAC,IAAI,CAAC;IAEX,IAAI,CAAC,UAAU,IAAI,CAAC,SAAS,IAAI,CAAC,OAAO,EAAE,CAAC;QAC1C,MAAM,OAAO,GAAG,kBAAkB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QACtD,IAAI,OAAO;YAAE,OAAO,OAAO,CAAC;IAC9B,CAAC;IACD,OAAO;QACL,UAAU;QACV,iBAAiB,EACf,UAAU,EAAE,YAAY,IAAI,SAAS,EAAE,YAAY,IAAI,OAAO,EAAE,aAAa,IAAI,IAAI;KACxF,CAAC;AACJ,CAAC,CAAC;AAEF,4FAA4F;AAC5F,MAAM,qBAAqB,GAAG,KAAK,EACjC,OAA2B,EAC3B,QAAgB,EAChB,OAAuC,EACN,EAAE;IACnC,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAkB,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC/E,MAAM,mBAAmB,CAAC,OAAO,EAAE;QACjC,QAAQ;QACR,QAAQ,EAAE,QAAQ,EAAE,QAAQ;QAC5B,MAAM,EAAE,2BAA2B;KACpC,CAAC,CAAC;IACH,OAAO,QAAQ,EAAE,KAAK,IAAI,IAAI,CAAC;AACjC,CAAC,CAAC;AAEF,2FAA2F;AAC3F,MAAM,kBAAkB,GAAG,CAAC,OAA2B,EAAE,QAAiB,EAAmB,EAAE;IAC7F,MAAM,YAAY,GAAG,uBAAuB,CAAC,OAAO,EAAE;QACpD,IAAI,EAAE,YAAY,CAAC,kBAAkB;QACrC,MAAM,EAAE,GAAG;KACZ,CAAC,CAAC;IACH,IAAI,YAAY;QAAE,OAAO,YAAY,CAAC;IACtC,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,UAAU,CAAC,YAAY,CAAC,kBAAkB,EAAE,GAAG,EAAE,sCAAsC,CAAC,CAAC;IAClG,CAAC;IACD,IAAI,CAAC,yBAAyB,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IACrD,OAAO,UAAU,CACf,YAAY,CAAC,kBAAkB,EAC/B,GAAG,EACH,qEAAqE,CACtE,CAAC;AACJ,CAAC,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,kBAAkB,GAAG,CACzB,QAAkB,EAClB,iBAAkC,EAClC,OAA2B,EACV,EAAE;IACnB,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,WAAW,EAAE,iBAAiB,EAAE,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAChF,OAAO,UAAU,CAAC,YAAY,CAAC,cAAc,EAAE,GAAG,EAAE,sBAAsB,CAAC,CAAC;IAC9E,CAAC;IACD,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,GAAG,gBAAgB,EAAE,CAAC;QAC7C,OAAO,UAAU,CACf,YAAY,CAAC,cAAc,EAC3B,GAAG,EACH,yBAAyB,gBAAgB,aAAa,CACvD,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAcF;;;;GAIG;AACH,MAAM,mBAAmB,GAAG,CAC1B,EAAE,GAAG,EAAE,QAAQ,EAAE,gBAAgB,EAAoB,EACrD,OAA2B,EACW,EAAE;IACxC,MAAM,aAAa,GAAG,CAAC,IAAY,EAAE,WAAmB,EAAY,EAAE,CACpE,aAAa,CAAC,QAAQ,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;IAE7C,MAAM,YAAY,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC;IACjE,IAAI,YAAY,KAAK,MAAM,EAAE,CAAC;QAC5B,OAAO,aAAa,CAAC,YAAY,CAAC,uBAAuB,EAAE,4BAA4B,CAAC,CAAC;IAC3F,CAAC;IAED,MAAM,aAAa,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,EAAE,CAAC;IACnE,uEAAuE;IACvE,uEAAuE;IACvE,mEAAmE;IACnE,MAAM,eAAe,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,uBAAuB,CAAC,IAAI,MAAM,CAAC;IAChF,IAAI,CAAC,aAAa,IAAI,eAAe,KAAK,MAAM,EAAE,CAAC;QACjD,OAAO,aAAa,CAAC,YAAY,CAAC,cAAc,EAAE,mCAAmC,CAAC,CAAC;IACzF,CAAC;IACD,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC;QACxC,OAAO,aAAa,CAClB,YAAY,CAAC,cAAc,EAC3B,mEAAmE,CACpE,CAAC;IACJ,CAAC;IAED,MAAM,aAAa,GAAG,iBAAiB,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,gBAAgB,CAAC,CAAC;IAClG,IAAI,aAAa;QAAE,OAAO,aAAa,CAAC,aAAa,CAAC,IAAI,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;IAEvF,MAAM,UAAU,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,eAAe,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC;IAC7F,MAAM,UAAU,GAAG,cAAc,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IACvD,IAAI,UAAU;QAAE,OAAO,aAAa,CAAC,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC;IAE9E,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC;AACpC,CAAC,CAAC;AAUF,yFAAyF;AACzF,MAAM,qBAAqB,GAAG,KAAK,EACjC,OAAmC,EACnC,OAAuC,EACvC,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAyB,EACnE,EAAE;IACrB,MAAM,EAAE,QAAQ,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC;IAC/C,MAAM,KAAK,GAAG;QACZ,QAAQ;QACR,WAAW,EAAE,QAAQ,CAAC,WAAW;QACjC,aAAa;QACb,MAAM,EAAE,IAAI,CAAC,EAAE;QACf,QAAQ,EAAE,gBAAgB;QAC1B,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC;KAC5B,CAAC;IAEF,IAAI,aAAa,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,EAAE,CAAC;QACjD,OAAO,YAAY,CAAC,QAAQ,EAAE,MAAM,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;IACxE,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;IACzF,OAAO,aAAa,CAAC,OAAO,EAAE;QAC5B,QAAQ;QACR,gBAAgB,EAAE,UAAU;QAC5B,WAAW,EAAE,QAAQ,CAAC,WAAW;QACjC,KAAK,EAAE,OAAO;QACd,QAAQ,EAAE,gBAAgB;QAC1B,IAAI;QACJ,MAAM;QACN,SAAS,EAAE,OAAO,CAAC,SAAS;KAC7B,CAAC,CAAC;AACL,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,EAClC,OAAgB,EAChB,OAAmC,EAChB,EAAE;IACrB,MAAM,WAAW,GAAG,iBAAiB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACzD,IAAI,WAAW;QAAE,OAAO,WAAW,CAAC;IAEpC,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,QAAQ,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;IACzD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,UAAU,CAAC,YAAY,CAAC,cAAc,EAAE,GAAG,EAAE,oBAAoB,CAAC,CAAC;IAC5E,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAC7D,MAAM,gBAAgB,GAAG,MAAM,sBAAsB,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAClF,IAAI,gBAAgB,YAAY,QAAQ;QAAE,OAAO,gBAAgB,CAAC;IAClE,MAAM,EAAE,UAAU,EAAE,iBAAiB,EAAE,GAAG,gBAAgB,CAAC;IAE3D,MAAM,QAAQ,GAAa;QACzB,WAAW,EAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE;QACvD,KAAK,EAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE;QAC1C,MAAM,EAAE,GAAG,CAAC,MAAM;KACnB,CAAC;IACF,MAAM,aAAa,GAAG,kBAAkB,CAAC,QAAQ,EAAE,iBAAiB,EAAE,OAAO,CAAC,CAAC;IAC/E,IAAI,aAAa;QAAE,OAAO,aAAa,CAAC;IAExC,MAAM,OAAO,GAAqB,EAAE,GAAG,EAAE,QAAQ,EAAE,gBAAgB,EAAE,CAAC;IACtE,MAAM,SAAS,GAAG,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACxD,IAAI,SAAS,YAAY,QAAQ;QAAE,OAAO,SAAS,CAAC;IAEpD,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IACtD,IAAI,CAAC,IAAI;QAAE,OAAO,eAAe,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;IAEzD,OAAO,qBAAqB,CAAC,OAAO,EAAE,OAAO,EAAE;QAC7C,GAAG,SAAS;QACZ,OAAO;QACP,QAAQ;QACR,UAAU;QACV,IAAI;KACL,CAAC,CAAC;AACL,CAAC,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Responses that send the user agent back to the client's own, already
3
+ * validated `redirect_uri` — the code on success, the error otherwise
4
+ * (RFC 6749 §4.1.2 / §4.1.2.1), with `iss` per RFC 9207.
5
+ */
6
+ /** The validated client callback one authorize/consent round trip returns to. */
7
+ export type Callback = {
8
+ redirectUri: string;
9
+ state: string;
10
+ issuer: string;
11
+ };
12
+ export declare const secureRedirect: (location: string) => Response;
13
+ export declare const codeRedirect: (callback: Callback, code: string) => Response;
14
+ export declare const errorRedirect: (callback: Callback, error: string, description?: string) => Response;
15
+ /**
16
+ * URL form of `errorRedirect`, for hosts composing their own authorize flow
17
+ * (`mcp-trellis/advanced`). `issuer`, when set, adds `iss` (RFC 9207).
18
+ */
19
+ export declare const buildErrorRedirectUrl: (redirectUri: string, error: string, description: string, state: string, issuer?: string) => string;
20
+ //# sourceMappingURL=callback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"callback.d.ts","sourceRoot":"","sources":["../../../src/oauth/endpoints/callback.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,iFAAiF;AACjF,MAAM,MAAM,QAAQ,GAAG;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,UAAU,MAAM,KAAG,QAQ9C,CAAC;AAeL,eAAO,MAAM,YAAY,GAAI,UAAU,QAAQ,EAAE,MAAM,MAAM,KAAG,QACf,CAAC;AAElD,eAAO,MAAM,aAAa,GAAI,UAAU,QAAQ,EAAE,OAAO,MAAM,EAAE,oBAAgB,KAAG,QACF,CAAC;AAEnF;;;GAGG;AACH,eAAO,MAAM,qBAAqB,GAChC,aAAa,MAAM,EACnB,OAAO,MAAM,EACb,aAAa,MAAM,EACnB,OAAO,MAAM,EACb,SAAS,MAAM,KACd,MAAgG,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Responses that send the user agent back to the client's own, already
3
+ * validated `redirect_uri` — the code on success, the error otherwise
4
+ * (RFC 6749 §4.1.2 / §4.1.2.1), with `iss` per RFC 9207.
5
+ */
6
+ export const secureRedirect = (location) => new Response(null, {
7
+ status: 302,
8
+ headers: {
9
+ Location: location,
10
+ "Referrer-Policy": "no-referrer",
11
+ "Cache-Control": "no-store",
12
+ },
13
+ });
14
+ /** Empty values are omitted, and `state` / `iss` always come last. */
15
+ const callbackUrl = (callback, params) => {
16
+ const target = new URL(callback.redirectUri);
17
+ const entries = [...Object.entries(params), ["state", callback.state], ["iss", callback.issuer]];
18
+ for (const [key, value] of entries) {
19
+ if (key && value)
20
+ target.searchParams.set(key, value);
21
+ }
22
+ return target.toString();
23
+ };
24
+ export const codeRedirect = (callback, code) => secureRedirect(callbackUrl(callback, { code }));
25
+ export const errorRedirect = (callback, error, description = "") => secureRedirect(callbackUrl(callback, { error, error_description: description }));
26
+ /**
27
+ * URL form of `errorRedirect`, for hosts composing their own authorize flow
28
+ * (`mcp-trellis/advanced`). `issuer`, when set, adds `iss` (RFC 9207).
29
+ */
30
+ export const buildErrorRedirectUrl = (redirectUri, error, description, state, issuer) => callbackUrl({ redirectUri, state, issuer }, { error, error_description: description });
31
+ //# sourceMappingURL=callback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"callback.js","sourceRoot":"","sources":["../../../src/oauth/endpoints/callback.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AASH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,QAAgB,EAAY,EAAE,CAC3D,IAAI,QAAQ,CAAC,IAAI,EAAE;IACjB,MAAM,EAAE,GAAG;IACX,OAAO,EAAE;QACP,QAAQ,EAAE,QAAQ;QAClB,iBAAiB,EAAE,aAAa;QAChC,eAAe,EAAE,UAAU;KAC5B;CACF,CAAC,CAAC;AAEL,sEAAsE;AACtE,MAAM,WAAW,GAAG,CAClB,QAA6E,EAC7E,MAA8B,EACtB,EAAE;IACV,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IACjG,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,OAAO,EAAE,CAAC;QACnC,IAAI,GAAG,IAAI,KAAK;YAAE,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,MAAM,CAAC,QAAQ,EAAE,CAAC;AAC3B,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,QAAkB,EAAE,IAAY,EAAY,EAAE,CACzE,cAAc,CAAC,WAAW,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AAElD,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,QAAkB,EAAE,KAAa,EAAE,WAAW,GAAG,EAAE,EAAY,EAAE,CAC7F,cAAc,CAAC,WAAW,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC;AAEnF;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACnC,WAAmB,EACnB,KAAa,EACb,WAAmB,EACnB,KAAa,EACb,MAAe,EACP,EAAE,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,CAAC,CAAC"}
@@ -0,0 +1,5 @@
1
+ /** The built-in consent interstitial, used when the host supplies no `consent.render`. */
2
+ import type { ConsentRequest } from "../types.js";
3
+ /** Built-in hardened interstitial — used whenever the host doesn't supply `consent.render`. */
4
+ export declare const renderBuiltInConsent: (input: ConsentRequest) => Response;
5
+ //# sourceMappingURL=consent-page.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consent-page.d.ts","sourceRoot":"","sources":["../../../src/oauth/endpoints/consent-page.ts"],"names":[],"mappings":"AAAA,0FAA0F;AAE1F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AA+BlD,+FAA+F;AAC/F,eAAO,MAAM,oBAAoB,GAAI,OAAO,cAAc,KAAG,QAmC5D,CAAC"}