mcp-trellis 0.2.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/README.md +41 -561
  2. package/dist/adapters/node.d.ts +15 -6
  3. package/dist/adapters/node.d.ts.map +1 -1
  4. package/dist/adapters/node.js +56 -29
  5. package/dist/adapters/node.js.map +1 -1
  6. package/dist/adapters/origins.d.ts.map +1 -1
  7. package/dist/adapters/origins.js +1 -4
  8. package/dist/adapters/origins.js.map +1 -1
  9. package/dist/app.d.ts +27 -4
  10. package/dist/app.d.ts.map +1 -1
  11. package/dist/app.js +31 -18
  12. package/dist/app.js.map +1 -1
  13. package/dist/auth/bearer.d.ts.map +1 -1
  14. package/dist/auth/bearer.js +6 -1
  15. package/dist/auth/bearer.js.map +1 -1
  16. package/dist/body.d.ts +48 -0
  17. package/dist/body.d.ts.map +1 -0
  18. package/dist/body.js +145 -0
  19. package/dist/body.js.map +1 -0
  20. package/dist/clients.d.ts.map +1 -1
  21. package/dist/clients.js +2 -5
  22. package/dist/clients.js.map +1 -1
  23. package/dist/dispatch.d.ts +1 -1
  24. package/dist/dispatch.d.ts.map +1 -1
  25. package/dist/dispatch.js +58 -18
  26. package/dist/dispatch.js.map +1 -1
  27. package/dist/http.d.ts.map +1 -1
  28. package/dist/http.js.map +1 -1
  29. package/dist/jsonrpc.d.ts +1 -0
  30. package/dist/jsonrpc.d.ts.map +1 -1
  31. package/dist/jsonrpc.js +1 -0
  32. package/dist/jsonrpc.js.map +1 -1
  33. package/dist/methods.d.ts +38 -15
  34. package/dist/methods.d.ts.map +1 -1
  35. package/dist/methods.js +38 -22
  36. package/dist/methods.js.map +1 -1
  37. package/dist/oauth/authorize.d.ts.map +1 -1
  38. package/dist/oauth/authorize.js +100 -68
  39. package/dist/oauth/authorize.js.map +1 -1
  40. package/dist/oauth/base64url.d.ts +1 -1
  41. package/dist/oauth/base64url.d.ts.map +1 -1
  42. package/dist/oauth/base64url.js +1 -4
  43. package/dist/oauth/base64url.js.map +1 -1
  44. package/dist/oauth/clientauth.d.ts +7 -2
  45. package/dist/oauth/clientauth.d.ts.map +1 -1
  46. package/dist/oauth/clientauth.js +59 -16
  47. package/dist/oauth/clientauth.js.map +1 -1
  48. package/dist/oauth/codes.d.ts +4 -4
  49. package/dist/oauth/codes.d.ts.map +1 -1
  50. package/dist/oauth/codes.js +43 -51
  51. package/dist/oauth/codes.js.map +1 -1
  52. package/dist/oauth/consent.d.ts +65 -0
  53. package/dist/oauth/consent.d.ts.map +1 -0
  54. package/dist/oauth/consent.js +173 -0
  55. package/dist/oauth/consent.js.map +1 -0
  56. package/dist/oauth/constants.d.ts +4 -0
  57. package/dist/oauth/constants.d.ts.map +1 -1
  58. package/dist/oauth/constants.js +4 -0
  59. package/dist/oauth/constants.js.map +1 -1
  60. package/dist/oauth/metadata.d.ts +6 -6
  61. package/dist/oauth/metadata.d.ts.map +1 -1
  62. package/dist/oauth/metadata.js.map +1 -1
  63. package/dist/oauth/pkce.d.ts.map +1 -1
  64. package/dist/oauth/pkce.js +1 -1
  65. package/dist/oauth/pkce.js.map +1 -1
  66. package/dist/oauth/redirect.d.ts +6 -1
  67. package/dist/oauth/redirect.d.ts.map +1 -1
  68. package/dist/oauth/redirect.js +23 -10
  69. package/dist/oauth/redirect.js.map +1 -1
  70. package/dist/oauth/reqbody.d.ts +10 -0
  71. package/dist/oauth/reqbody.d.ts.map +1 -0
  72. package/dist/oauth/reqbody.js +29 -0
  73. package/dist/oauth/reqbody.js.map +1 -0
  74. package/dist/oauth/resource.d.ts +30 -0
  75. package/dist/oauth/resource.d.ts.map +1 -1
  76. package/dist/oauth/resource.js +38 -8
  77. package/dist/oauth/resource.js.map +1 -1
  78. package/dist/oauth/revoke.d.ts.map +1 -1
  79. package/dist/oauth/revoke.js +15 -3
  80. package/dist/oauth/revoke.js.map +1 -1
  81. package/dist/oauth/router.d.ts +14 -11
  82. package/dist/oauth/router.d.ts.map +1 -1
  83. package/dist/oauth/router.js +62 -22
  84. package/dist/oauth/router.js.map +1 -1
  85. package/dist/oauth/scope.d.ts +11 -2
  86. package/dist/oauth/scope.d.ts.map +1 -1
  87. package/dist/oauth/scope.js +18 -7
  88. package/dist/oauth/scope.js.map +1 -1
  89. package/dist/oauth/sealed.d.ts +17 -0
  90. package/dist/oauth/sealed.d.ts.map +1 -0
  91. package/dist/oauth/sealed.js +57 -0
  92. package/dist/oauth/sealed.js.map +1 -0
  93. package/dist/oauth/secrethash.d.ts +44 -0
  94. package/dist/oauth/secrethash.d.ts.map +1 -0
  95. package/dist/oauth/secrethash.js +66 -0
  96. package/dist/oauth/secrethash.js.map +1 -0
  97. package/dist/oauth/token.d.ts.map +1 -1
  98. package/dist/oauth/token.js +13 -34
  99. package/dist/oauth/token.js.map +1 -1
  100. package/dist/oauth/types.d.ts +106 -9
  101. package/dist/oauth/types.d.ts.map +1 -1
  102. package/dist/oauth/types.js +67 -4
  103. package/dist/oauth/types.js.map +1 -1
  104. package/dist/oauth/wellknown.d.ts.map +1 -1
  105. package/dist/oauth/wellknown.js +6 -4
  106. package/dist/oauth/wellknown.js.map +1 -1
  107. package/dist/protocol.d.ts +15 -1
  108. package/dist/protocol.d.ts.map +1 -1
  109. package/dist/protocol.js +16 -6
  110. package/dist/protocol.js.map +1 -1
  111. package/dist/registry.d.ts +4 -4
  112. package/dist/registry.d.ts.map +1 -1
  113. package/dist/registry.js +15 -7
  114. package/dist/registry.js.map +1 -1
  115. package/dist/server.d.ts +14 -10
  116. package/dist/server.d.ts.map +1 -1
  117. package/dist/server.js +8 -8
  118. package/dist/server.js.map +1 -1
  119. package/dist/tools.d.ts +11 -2
  120. package/dist/tools.d.ts.map +1 -1
  121. package/dist/tools.js +14 -13
  122. package/dist/tools.js.map +1 -1
  123. package/dist/validate.d.ts +16 -1
  124. package/dist/validate.d.ts.map +1 -1
  125. package/dist/validate.js +46 -8
  126. package/dist/validate.js.map +1 -1
  127. package/docs/ROADMAP.md +295 -0
  128. package/docs/guide.md +417 -0
  129. package/docs/reference.md +178 -0
  130. package/docs/security.md +63 -0
  131. package/package.json +7 -3
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Shared JSON/form body reader for `/token` and `/revoke`. `/token` has
3
+ * always accepted both; `/revoke` used to parse form-only, so a client that
4
+ * revoked with `Content-Type: application/json` (matching how it likely
5
+ * called `/token`) got a silent `invalid_request "token required"` — the
6
+ * caller believes the token is dead when the request was never understood.
7
+ */
8
+ import { DEFAULT_OAUTH_BODY_LIMIT, readBoundedText } from "../body.js";
9
+ const readJsonBody = async (request) => {
10
+ const text = await readBoundedText(request, DEFAULT_OAUTH_BODY_LIMIT);
11
+ const body = JSON.parse(text);
12
+ if (!body || typeof body !== "object" || Array.isArray(body)) {
13
+ throw new Error("malformed request body");
14
+ }
15
+ return Object.fromEntries(Object.entries(body)
16
+ .filter(([, value]) => value != null)
17
+ .map(([key, value]) => [key, String(value)]));
18
+ };
19
+ const readFormBody = async (request) => {
20
+ const text = await readBoundedText(request, DEFAULT_OAUTH_BODY_LIMIT);
21
+ const params = new URLSearchParams(text);
22
+ return Object.fromEntries([...params.entries()]);
23
+ };
24
+ /** JSON when `Content-Type` says so; form-encoded otherwise (the RFC 6749 default). */
25
+ export const readOAuthBody = async (request) => {
26
+ const ctype = request.headers.get("Content-Type") ?? "";
27
+ return ctype.includes("application/json") ? readJsonBody(request) : readFormBody(request);
28
+ };
29
+ //# sourceMappingURL=reqbody.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reqbody.js","sourceRoot":"","sources":["../../src/oauth/reqbody.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,wBAAwB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAEvE,MAAM,YAAY,GAAG,KAAK,EAAE,OAAgB,EAAmC,EAAE;IAC/E,MAAM,IAAI,GAAG,MAAM,eAAe,CAAC,OAAO,EAAE,wBAAwB,CAAC,CAAC;IACtE,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAY,CAAC;IACzC,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAC7D,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAC5C,CAAC;IACD,OAAO,MAAM,CAAC,WAAW,CACvB,MAAM,CAAC,OAAO,CAAC,IAA+B,CAAC;SAC5C,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,IAAI,IAAI,CAAC;SACpC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAC/C,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,YAAY,GAAG,KAAK,EAAE,OAAgB,EAAmC,EAAE;IAC/E,MAAM,IAAI,GAAG,MAAM,eAAe,CAAC,OAAO,EAAE,wBAAwB,CAAC,CAAC;IACtE,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,CAAC;IACzC,OAAO,MAAM,CAAC,WAAW,CAAC,CAAC,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;AACnD,CAAC,CAAC;AAEF,uFAAuF;AACvF,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,EAAE,OAAgB,EAAmC,EAAE;IACvF,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;IACxD,OAAO,KAAK,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;AAC5F,CAAC,CAAC"}
@@ -1,7 +1,37 @@
1
1
  /** Canonical MCP resource URI (RFC 8707 / RFC 9728). */
2
+ import { type OAuthErrorInfo } from "./types.js";
2
3
  export declare const DEFAULT_RESOURCE_PATH = "/mcp";
4
+ /**
5
+ * Strips a trailing slash from a *configured* path (`resourcePath`,
6
+ * `oauthPath`) so `"/mcp/"` and `"/mcp"` can't produce divergent canonical
7
+ * resources — `canonicalResource` doesn't strip one itself, but
8
+ * `normalizeResource` (used to compare an incoming request's resource
9
+ * against it) does, so leaving a trailing slash in the *configured* value
10
+ * silently made every request fail to match. `"/"` stays `"/"`. Apply once
11
+ * at construction, not per request.
12
+ */
13
+ export declare const normalizeConfiguredPath: (path: string) => string;
14
+ /**
15
+ * Normalizes `resourcePath` internally (trailing slash stripped) so every
16
+ * caller gets a consistent result regardless of whether it read a raw
17
+ * `options.resourcePath` or a value some other layer already normalized —
18
+ * `"/mcp/"` and `"/mcp"` must produce the same canonical resource, since
19
+ * `resourcesEqual` compares against a normalized incoming request either
20
+ * way (see `normalizeResource` below). Centralizing the fix here, instead
21
+ * of only in the callers that happen to normalize their own config,
22
+ * removes an entire class of "which of these read the normalized value"
23
+ * bugs at the source.
24
+ */
3
25
  export declare const canonicalResource: (origin: string, resourcePath?: string) => string;
4
26
  export declare const resourcesEqual: (requested: string, expected: string) => boolean;
27
+ /**
28
+ * Missing → invalid_request; mismatch → invalid_target. Info-only variant
29
+ * (no `Response` built) so a caller can choose how to deliver it —
30
+ * `/token` returns it directly as JSON via `firstResourceError` below;
31
+ * `/authorize` redirects it to the client's callback per RFC 6749 §4.1.2.1
32
+ * once `redirect_uri` itself is trusted (see `authorize.ts`).
33
+ */
34
+ export declare const resourceErrorInfo: (requested: string, expected: string) => OAuthErrorInfo | null;
5
35
  /** Missing → invalid_request; mismatch → invalid_target. */
6
36
  export declare const firstResourceError: (requested: string, expected: string) => Response | null;
7
37
  //# sourceMappingURL=resource.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../../src/oauth/resource.ts"],"names":[],"mappings":"AAAA,wDAAwD;AAKxD,eAAO,MAAM,qBAAqB,SAAS,CAAC;AAE5C,eAAO,MAAM,iBAAiB,GAC5B,QAAQ,MAAM,EACd,qBAAoC,KACnC,MAMF,CAAC;AAmBF,eAAO,MAAM,cAAc,GACzB,WAAW,MAAM,EACjB,UAAU,MAAM,KACf,OAGF,CAAC;AAEF,4DAA4D;AAC5D,eAAO,MAAM,kBAAkB,GAC7B,WAAW,MAAM,EACjB,UAAU,MAAM,KACf,QAAQ,GAAG,IAgBb,CAAC"}
1
+ {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../../src/oauth/resource.ts"],"names":[],"mappings":"AAAA,wDAAwD;AAGxD,OAAO,EAAE,KAAK,cAAc,EAAc,MAAM,YAAY,CAAC;AAE7D,eAAO,MAAM,qBAAqB,SAAS,CAAC;AAE5C;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB,GAAI,MAAM,MAAM,KAAG,MACA,CAAC;AAExD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,GAAI,QAAQ,MAAM,EAAE,qBAAoC,KAAG,MAIxF,CAAC;AAmBF,eAAO,MAAM,cAAc,GAAI,WAAW,MAAM,EAAE,UAAU,MAAM,KAAG,OAGpE,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,GAAI,WAAW,MAAM,EAAE,UAAU,MAAM,KAAG,cAAc,GAAG,IAQxF,CAAC;AAEF,4DAA4D;AAC5D,eAAO,MAAM,kBAAkB,GAAI,WAAW,MAAM,EAAE,UAAU,MAAM,KAAG,QAAQ,GAAG,IAGnF,CAAC"}
@@ -2,12 +2,31 @@
2
2
  import { OAUTH_ERRORS } from "./constants.js";
3
3
  import { oauthError } from "./types.js";
4
4
  export const DEFAULT_RESOURCE_PATH = "/mcp";
5
+ /**
6
+ * Strips a trailing slash from a *configured* path (`resourcePath`,
7
+ * `oauthPath`) so `"/mcp/"` and `"/mcp"` can't produce divergent canonical
8
+ * resources — `canonicalResource` doesn't strip one itself, but
9
+ * `normalizeResource` (used to compare an incoming request's resource
10
+ * against it) does, so leaving a trailing slash in the *configured* value
11
+ * silently made every request fail to match. `"/"` stays `"/"`. Apply once
12
+ * at construction, not per request.
13
+ */
14
+ export const normalizeConfiguredPath = (path) => path === "/" ? path : path.replace(/\/+$/, "") || "/";
15
+ /**
16
+ * Normalizes `resourcePath` internally (trailing slash stripped) so every
17
+ * caller gets a consistent result regardless of whether it read a raw
18
+ * `options.resourcePath` or a value some other layer already normalized —
19
+ * `"/mcp/"` and `"/mcp"` must produce the same canonical resource, since
20
+ * `resourcesEqual` compares against a normalized incoming request either
21
+ * way (see `normalizeResource` below). Centralizing the fix here, instead
22
+ * of only in the callers that happen to normalize their own config,
23
+ * removes an entire class of "which of these read the normalized value"
24
+ * bugs at the source.
25
+ */
5
26
  export const canonicalResource = (origin, resourcePath = DEFAULT_RESOURCE_PATH) => {
6
27
  const base = origin.replace(/\/$/, "");
7
- const path = resourcePath.startsWith("/")
8
- ? resourcePath
9
- : `/${resourcePath}`;
10
- return `${base}${path}`;
28
+ const withLeadingSlash = resourcePath.startsWith("/") ? resourcePath : `/${resourcePath}`;
29
+ return `${base}${normalizeConfiguredPath(withLeadingSlash)}`;
11
30
  };
12
31
  /**
13
32
  * Normalize for RFC 8707 comparison.
@@ -31,14 +50,25 @@ export const resourcesEqual = (requested, expected) => {
31
50
  const a = normalizeResource(requested);
32
51
  return a !== null && a === normalizeResource(expected);
33
52
  };
34
- /** Missing → invalid_request; mismatch → invalid_target. */
35
- export const firstResourceError = (requested, expected) => {
53
+ /**
54
+ * Missing → invalid_request; mismatch → invalid_target. Info-only variant
55
+ * (no `Response` built) so a caller can choose how to deliver it —
56
+ * `/token` returns it directly as JSON via `firstResourceError` below;
57
+ * `/authorize` redirects it to the client's callback per RFC 6749 §4.1.2.1
58
+ * once `redirect_uri` itself is trusted (see `authorize.ts`).
59
+ */
60
+ export const resourceErrorInfo = (requested, expected) => {
36
61
  if (!requested) {
37
- return oauthError(OAUTH_ERRORS.invalidRequest, 400, "resource required (RFC 8707)");
62
+ return { code: OAUTH_ERRORS.invalidRequest, description: "resource required (RFC 8707)" };
38
63
  }
39
64
  if (!resourcesEqual(requested, expected)) {
40
- return oauthError(OAUTH_ERRORS.invalidTarget, 400, "resource does not match this server");
65
+ return { code: OAUTH_ERRORS.invalidTarget, description: "resource does not match this server" };
41
66
  }
42
67
  return null;
43
68
  };
69
+ /** Missing → invalid_request; mismatch → invalid_target. */
70
+ export const firstResourceError = (requested, expected) => {
71
+ const issue = resourceErrorInfo(requested, expected);
72
+ return issue ? oauthError(issue.code, 400, issue.description) : null;
73
+ };
44
74
  //# sourceMappingURL=resource.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"resource.js","sourceRoot":"","sources":["../../src/oauth/resource.ts"],"names":[],"mappings":"AAAA,wDAAwD;AAExD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,MAAM,CAAC,MAAM,qBAAqB,GAAG,MAAM,CAAC;AAE5C,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAC/B,MAAc,EACd,YAAY,GAAG,qBAAqB,EAC5B,EAAE;IACV,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACvC,MAAM,IAAI,GAAG,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC;QACvC,CAAC,CAAC,YAAY;QACd,CAAC,CAAC,IAAI,YAAY,EAAE,CAAC;IACvB,OAAO,GAAG,IAAI,GAAG,IAAI,EAAE,CAAC;AAC1B,CAAC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,iBAAiB,GAAG,CAAC,GAAW,EAAiB,EAAE;IACvD,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QACvB,IAAI,CAAC,CAAC,IAAI;YAAE,OAAO,IAAI,CAAC;QACxB,MAAM,IAAI,GAAG,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC;QAClD,OAAO,GAAG,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC;IACtD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAAG,CAC5B,SAAiB,EACjB,QAAgB,EACP,EAAE;IACX,MAAM,CAAC,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IACvC,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,iBAAiB,CAAC,QAAQ,CAAC,CAAC;AACzD,CAAC,CAAC;AAEF,4DAA4D;AAC5D,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAChC,SAAiB,EACjB,QAAgB,EACC,EAAE;IACnB,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,UAAU,CACf,YAAY,CAAC,cAAc,EAC3B,GAAG,EACH,8BAA8B,CAC/B,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,QAAQ,CAAC,EAAE,CAAC;QACzC,OAAO,UAAU,CACf,YAAY,CAAC,aAAa,EAC1B,GAAG,EACH,qCAAqC,CACtC,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC,CAAC"}
1
+ {"version":3,"file":"resource.js","sourceRoot":"","sources":["../../src/oauth/resource.ts"],"names":[],"mappings":"AAAA,wDAAwD;AAExD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAuB,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7D,MAAM,CAAC,MAAM,qBAAqB,GAAG,MAAM,CAAC;AAE5C;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,IAAY,EAAU,EAAE,CAC9D,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC;AAExD;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,MAAc,EAAE,YAAY,GAAG,qBAAqB,EAAU,EAAE;IAChG,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACvC,MAAM,gBAAgB,GAAG,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,YAAY,EAAE,CAAC;IAC1F,OAAO,GAAG,IAAI,GAAG,uBAAuB,CAAC,gBAAgB,CAAC,EAAE,CAAC;AAC/D,CAAC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,iBAAiB,GAAG,CAAC,GAAW,EAAiB,EAAE;IACvD,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QACvB,IAAI,CAAC,CAAC,IAAI;YAAE,OAAO,IAAI,CAAC;QACxB,MAAM,IAAI,GAAG,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC;QAClD,OAAO,GAAG,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC;IACtD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,SAAiB,EAAE,QAAgB,EAAW,EAAE;IAC7E,MAAM,CAAC,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IACvC,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,iBAAiB,CAAC,QAAQ,CAAC,CAAC;AACzD,CAAC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,SAAiB,EAAE,QAAgB,EAAyB,EAAE;IAC9F,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,EAAE,IAAI,EAAE,YAAY,CAAC,cAAc,EAAE,WAAW,EAAE,8BAA8B,EAAE,CAAC;IAC5F,CAAC;IACD,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,QAAQ,CAAC,EAAE,CAAC;QACzC,OAAO,EAAE,IAAI,EAAE,YAAY,CAAC,aAAa,EAAE,WAAW,EAAE,qCAAqC,EAAE,CAAC;IAClG,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAEF,4DAA4D;AAC5D,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,SAAiB,EAAE,QAAgB,EAAmB,EAAE;IACzF,MAAM,KAAK,GAAG,iBAAiB,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IACrD,OAAO,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACvE,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"revoke.d.ts","sourceRoot":"","sources":["../../src/oauth/revoke.ts"],"names":[],"mappings":"AAAA,iCAAiC;AAKjC,OAAO,EAAc,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAUjE,eAAO,MAAM,YAAY,GACvB,SAAS,OAAO,EAChB,SAAS,kBAAkB,KAC1B,OAAO,CAAC,QAAQ,CAuBlB,CAAC"}
1
+ {"version":3,"file":"revoke.d.ts","sourceRoot":"","sources":["../../src/oauth/revoke.ts"],"names":[],"mappings":"AAAA,iCAAiC;AAOjC,OAAO,EAAE,KAAK,kBAAkB,EAAc,MAAM,YAAY,CAAC;AAQjE,eAAO,MAAM,YAAY,GACvB,SAAS,OAAO,EAChB,SAAS,kBAAkB,KAC1B,OAAO,CAAC,QAAQ,CA8BlB,CAAC"}
@@ -1,7 +1,9 @@
1
1
  /** RFC 7009 token revocation. */
2
+ import { BodyTooLargeError } from "../body.js";
2
3
  import { requireHttpMethod } from "../http.js";
3
4
  import { firstClientAuthError, readClientAuth } from "./clientauth.js";
4
5
  import { OAUTH_ERRORS } from "./constants.js";
6
+ import { readOAuthBody } from "./reqbody.js";
5
7
  import { oauthError } from "./types.js";
6
8
  const POST_ONLY = new Set(["POST"]);
7
9
  const NO_CORS = { cors: false };
@@ -10,20 +12,30 @@ export const handleRevoke = async (request, options) => {
10
12
  const methodError = requireHttpMethod(request, POST_ONLY, NO_CORS);
11
13
  if (methodError)
12
14
  return methodError;
13
- const body = Object.fromEntries(new URLSearchParams(await request.text()).entries());
15
+ let body;
16
+ try {
17
+ body = await readOAuthBody(request);
18
+ }
19
+ catch (exc) {
20
+ if (exc instanceof BodyTooLargeError) {
21
+ return oauthError(OAUTH_ERRORS.invalidRequest, 413, "request body too large");
22
+ }
23
+ return oauthError(OAUTH_ERRORS.invalidRequest, 400, "malformed request body");
24
+ }
14
25
  const token = body.token ?? "";
15
26
  if (!token)
16
27
  return oauthError(OAUTH_ERRORS.invalidRequest, 400, "token required");
17
28
  const auth = readClientAuth(request, body);
18
29
  if (!auth)
19
30
  return oauthError(OAUTH_ERRORS.invalidRequest, 400, "client_id required");
20
- const clientAuthError = await firstClientAuthError(auth, options);
31
+ const clientAuthError = await firstClientAuthError(auth, options, request);
21
32
  if (clientAuthError)
22
33
  return clientAuthError;
34
+ const hint = tokenTypeHintOf(body.token_type_hint);
23
35
  await options.ports.revokeToken?.({
24
36
  token,
25
37
  clientId: auth.clientId,
26
- tokenTypeHint: tokenTypeHintOf(body.token_type_hint),
38
+ ...(hint !== undefined ? { tokenTypeHint: hint } : {}),
27
39
  });
28
40
  return new Response(null, { status: 200, headers: { "Cache-Control": "no-store" } });
29
41
  };
@@ -1 +1 @@
1
- {"version":3,"file":"revoke.js","sourceRoot":"","sources":["../../src/oauth/revoke.ts"],"names":[],"mappings":"AAAA,iCAAiC;AAEjC,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAA2B,MAAM,YAAY,CAAC;AAEjE,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;AACpC,MAAM,OAAO,GAAG,EAAE,IAAI,EAAE,KAAK,EAAW,CAAC;AAEzC,MAAM,eAAe,GAAG,CACtB,IAAwB,EACsB,EAAE,CAChD,IAAI,KAAK,cAAc,IAAI,IAAI,KAAK,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;AAEzE,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAC/B,OAAgB,EAChB,OAA2B,EACR,EAAE;IACrB,MAAM,WAAW,GAAG,iBAAiB,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IACnE,IAAI,WAAW;QAAE,OAAO,WAAW,CAAC;IAEpC,MAAM,IAAI,GAAG,MAAM,CAAC,WAAW,CAC7B,IAAI,eAAe,CAAC,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CACpD,CAAC;IACF,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;IAC/B,IAAI,CAAC,KAAK;QAAE,OAAO,UAAU,CAAC,YAAY,CAAC,cAAc,EAAE,GAAG,EAAE,gBAAgB,CAAC,CAAC;IAElF,MAAM,IAAI,GAAG,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC3C,IAAI,CAAC,IAAI;QAAE,OAAO,UAAU,CAAC,YAAY,CAAC,cAAc,EAAE,GAAG,EAAE,oBAAoB,CAAC,CAAC;IAErF,MAAM,eAAe,GAAG,MAAM,oBAAoB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAClE,IAAI,eAAe;QAAE,OAAO,eAAe,CAAC;IAE5C,MAAM,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;QAChC,KAAK;QACL,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,aAAa,EAAE,eAAe,CAAC,IAAI,CAAC,eAAe,CAAC;KACrD,CAAC,CAAC;IAEH,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,eAAe,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC;AACvF,CAAC,CAAC"}
1
+ {"version":3,"file":"revoke.js","sourceRoot":"","sources":["../../src/oauth/revoke.ts"],"names":[],"mappings":"AAAA,iCAAiC;AAEjC,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAA2B,UAAU,EAAE,MAAM,YAAY,CAAC;AAEjE,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;AACpC,MAAM,OAAO,GAAG,EAAE,IAAI,EAAE,KAAK,EAAW,CAAC;AAEzC,MAAM,eAAe,GAAG,CAAC,IAAwB,EAAgD,EAAE,CACjG,IAAI,KAAK,cAAc,IAAI,IAAI,KAAK,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;AAEzE,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAC/B,OAAgB,EAChB,OAA2B,EACR,EAAE;IACrB,MAAM,WAAW,GAAG,iBAAiB,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IACnE,IAAI,WAAW;QAAE,OAAO,WAAW,CAAC;IAEpC,IAAI,IAA4B,CAAC;IACjC,IAAI,CAAC;QACH,IAAI,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,CAAC;IACtC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,iBAAiB,EAAE,CAAC;YACrC,OAAO,UAAU,CAAC,YAAY,CAAC,cAAc,EAAE,GAAG,EAAE,wBAAwB,CAAC,CAAC;QAChF,CAAC;QACD,OAAO,UAAU,CAAC,YAAY,CAAC,cAAc,EAAE,GAAG,EAAE,wBAAwB,CAAC,CAAC;IAChF,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;IAC/B,IAAI,CAAC,KAAK;QAAE,OAAO,UAAU,CAAC,YAAY,CAAC,cAAc,EAAE,GAAG,EAAE,gBAAgB,CAAC,CAAC;IAElF,MAAM,IAAI,GAAG,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC3C,IAAI,CAAC,IAAI;QAAE,OAAO,UAAU,CAAC,YAAY,CAAC,cAAc,EAAE,GAAG,EAAE,oBAAoB,CAAC,CAAC;IAErF,MAAM,eAAe,GAAG,MAAM,oBAAoB,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAC3E,IAAI,eAAe;QAAE,OAAO,eAAe,CAAC;IAE5C,MAAM,IAAI,GAAG,eAAe,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IACnD,MAAM,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;QAChC,KAAK;QACL,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACvD,CAAC,CAAC;IAEH,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,eAAe,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC;AACvF,CAAC,CAAC"}
@@ -1,18 +1,21 @@
1
1
  import { type OAuthRouterOptions } from "./types.js";
2
- export type { OAuthUser, MintedToken, OAuthPorts, OAuthRouterOptions, } from "./types.js";
2
+ export type { MintedToken, OAuthAuditEntry, OAuthPorts, OAuthRouterOptions, OAuthUser, } from "./types.js";
3
3
  export type OAuthRouter = {
4
4
  /** Returns a Response when this request is OAuth-related; otherwise null. */
5
5
  tryHandle: (request: Request) => Promise<Response | null>;
6
6
  };
7
7
  export declare const createOAuthRouter: (options: OAuthRouterOptions) => OAuthRouter;
8
- export { authorizationServerMetadata, protectedResourceMetadata, mcpWwwAuthenticate, } from "./metadata.js";
9
- export { canonicalResource, resourcesEqual, DEFAULT_RESOURCE_PATH, firstResourceError, } from "./resource.js";
10
- export { isAllowedRedirectUri, CLAUDE_CALLBACK } from "./redirect.js";
11
- export { issueAuthCode, consumeAuthCode, newClientId, type AuthCodeRecord, type CodeStore, } from "./codes.js";
12
- export { verifyPkceS256, sha256Base64Url, randomBase64Url } from "./pkce.js";
13
- export { GRANT_TYPES, OAUTH_ERRORS, DEFAULT_SCOPE, TOKEN_ENDPOINT_AUTH_METHODS, type TokenEndpointAuthMethod, } from "./constants.js";
14
- export { parseScope, formatScope, requestedScopes, firstScopeError, } from "./scope.js";
15
- export { readClientAuth, firstClientAuthError, type ClientAuth } from "./clientauth.js";
16
- export { unregisteredClientsAllowed } from "./types.js";
17
- export type { MintAccessTokenInput, RefreshAccessTokenInput, RevokeTokenInput, ClientStore, RegisteredClient, } from "./types.js";
8
+ export { type ClientAuth, firstClientAuthError, readClientAuth } from "./clientauth.js";
9
+ export { type AuthCodeRecord, type CodeStore, consumeAuthCode, issueAuthCode, newClientId, } from "./codes.js";
10
+ export type { ConsentOptions, ConsentRequest } from "./consent.js";
11
+ export { buildErrorRedirectUrl } from "./consent.js";
12
+ export { DEFAULT_SCOPE, GRANT_TYPES, OAUTH_ERRORS, TOKEN_ENDPOINT_AUTH_METHODS, type TokenEndpointAuthMethod, } from "./constants.js";
13
+ export { authorizationServerMetadata, mcpWwwAuthenticate, protectedResourceMetadata, } from "./metadata.js";
14
+ export { randomBase64Url, sha256Base64Url, verifyPkceS256 } from "./pkce.js";
15
+ export { CLAUDE_CALLBACK, isAllowedRedirectUri } from "./redirect.js";
16
+ export { canonicalResource, DEFAULT_RESOURCE_PATH, firstResourceError, normalizeConfiguredPath, resourceErrorInfo, resourcesEqual, } from "./resource.js";
17
+ export { firstScopeError, formatScope, parseScope, requestedScopes, scopeErrorInfo, } from "./scope.js";
18
+ export { hashClientSecret, verifyClientSecret } from "./secrethash.js";
19
+ export type { ClientAssertion, ClientStore, MintAccessTokenInput, OAuthErrorInfo, RefreshAccessTokenInput, RegisteredClient, RevokeTokenInput, } from "./types.js";
20
+ export { advertisedScopes, defaultScopes, registeredClientsRequired, unregisteredClientsAllowed, } from "./types.js";
18
21
  //# sourceMappingURL=router.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/oauth/router.ts"],"names":[],"mappings":"AAKA,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,YAAY,CAAC;AAMpB,YAAY,EACV,SAAS,EACT,WAAW,EACX,UAAU,EACV,kBAAkB,GACnB,MAAM,YAAY,CAAC;AAsDpB,MAAM,MAAM,WAAW,GAAG;IACxB,6EAA6E;IAC7E,SAAS,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;CAC3D,CAAC;AAIF,eAAO,MAAM,iBAAiB,GAC5B,SAAS,kBAAkB,KAC1B,WA4DF,CAAC;AAEF,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,EACzB,kBAAkB,GACnB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACtE,OAAO,EACL,aAAa,EACb,eAAe,EACf,WAAW,EACX,KAAK,cAAc,EACnB,KAAK,SAAS,GACf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC7E,OAAO,EACL,WAAW,EACX,YAAY,EACZ,aAAa,EACb,2BAA2B,EAC3B,KAAK,uBAAuB,GAC7B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,UAAU,EACV,WAAW,EACX,eAAe,EACf,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACxF,OAAO,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAC;AACxD,YAAY,EACV,oBAAoB,EACpB,uBAAuB,EACvB,gBAAgB,EAChB,WAAW,EACX,gBAAgB,GACjB,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/oauth/router.ts"],"names":[],"mappings":"AAUA,OAAO,EAIL,KAAK,kBAAkB,EAKxB,MAAM,YAAY,CAAC;AAGpB,YAAY,EACV,WAAW,EACX,eAAe,EACf,UAAU,EACV,kBAAkB,EAClB,SAAS,GACV,MAAM,YAAY,CAAC;AA8DpB,MAAM,MAAM,WAAW,GAAG;IACxB,6EAA6E;IAC7E,SAAS,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;CAC3D,CAAC;AAIF,eAAO,MAAM,iBAAiB,GAAI,SAAS,kBAAkB,KAAG,WA0F/D,CAAC;AAEF,OAAO,EAAE,KAAK,UAAU,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACxF,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,eAAe,EACf,aAAa,EACb,WAAW,GACZ,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnE,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EACL,aAAa,EACb,WAAW,EACX,YAAY,EACZ,2BAA2B,EAC3B,KAAK,uBAAuB,GAC7B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,2BAA2B,EAC3B,kBAAkB,EAClB,yBAAyB,GAC1B,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACtE,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,kBAAkB,EAClB,uBAAuB,EACvB,iBAAiB,EACjB,cAAc,GACf,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,eAAe,EACf,WAAW,EACX,UAAU,EACV,eAAe,EACf,cAAc,GACf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACvE,YAAY,EACV,eAAe,EACf,WAAW,EACX,oBAAoB,EACpB,cAAc,EACd,uBAAuB,EACvB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,yBAAyB,EACzB,0BAA0B,GAC3B,MAAM,YAAY,CAAC"}
@@ -1,12 +1,14 @@
1
+ import { BodyTooLargeError, DEFAULT_OAUTH_BODY_LIMIT, readBoundedText } from "../body.js";
1
2
  import { INTERNAL_ERROR, jsonResponse, requireHttpMethod } from "../http.js";
2
- import { newClientId } from "./codes.js";
3
+ import { handleAuthorize } from "./authorize.js";
4
+ import { handleConsent } from "./consent.js";
3
5
  import { GRANT_TYPES, OAUTH_ERRORS } from "./constants.js";
4
6
  import { CLAUDE_CALLBACK, isAllowedRedirectUri } from "./redirect.js";
5
- import { DEFAULT_RESOURCE_PATH } from "./resource.js";
6
- import { oauthError, unregisteredClientsAllowed, } from "./types.js";
7
- import { handleAuthorize } from "./authorize.js";
8
- import { handleToken } from "./token.js";
7
+ import { DEFAULT_RESOURCE_PATH, normalizeConfiguredPath } from "./resource.js";
9
8
  import { handleRevoke } from "./revoke.js";
9
+ import { seal } from "./sealed.js";
10
+ import { handleToken } from "./token.js";
11
+ import { assertCodeSecret, assertScopeConfig, oauthError, resolveSecret, safeOAuthAudit, unregisteredClientsAllowed, } from "./types.js";
10
12
  import { handleWellKnown } from "./wellknown.js";
11
13
  const POST_ONLY = new Set(["POST"]);
12
14
  const handleRegister = async (request, options) => {
@@ -16,7 +18,8 @@ const handleRegister = async (request, options) => {
16
18
  let suppliedRedirectUris = false;
17
19
  let redirectUris = [];
18
20
  try {
19
- const body = (await request.json());
21
+ const text = await readBoundedText(request, DEFAULT_OAUTH_BODY_LIMIT);
22
+ const body = JSON.parse(text);
20
23
  const raw = body.redirect_uris;
21
24
  if (Array.isArray(raw)) {
22
25
  suppliedRedirectUris = true;
@@ -25,7 +28,10 @@ const handleRegister = async (request, options) => {
25
28
  .filter((uri) => isAllowedRedirectUri(uri, options.redirect));
26
29
  }
27
30
  }
28
- catch {
31
+ catch (exc) {
32
+ if (exc instanceof BodyTooLargeError) {
33
+ return oauthError(OAUTH_ERRORS.invalidRequest, 413, "request body too large");
34
+ }
29
35
  redirectUris = [];
30
36
  }
31
37
  // No redirect_uris in the body: fall back to the Claude callback only if
@@ -40,8 +46,14 @@ const handleRegister = async (request, options) => {
40
46
  ? "no allowed redirect_uris"
41
47
  : "redirect_uris required — this server has no default redirect_uri");
42
48
  }
49
+ // Zero storage: the client_id *is* the registration record — a sealed,
50
+ // self-verifying assertion of the redirect_uris this call just validated.
51
+ // `/authorize` unseals it and binds the client to exactly this list,
52
+ // the same protection a stored registration would give a pre-registered id.
53
+ const secret = await resolveSecret(options.ports, request);
54
+ const clientId = await seal(secret, "client", { redirectUris });
43
55
  return jsonResponse({
44
- client_id: newClientId(),
56
+ client_id: clientId,
45
57
  client_id_issued_at: Math.floor(Date.now() / 1000),
46
58
  token_endpoint_auth_method: "none",
47
59
  redirect_uris: redirectUris,
@@ -54,8 +66,26 @@ export const createOAuthRouter = (options) => {
54
66
  throw new Error("allowUnregisteredClients: false requires ports.clientStore " +
55
67
  "so pre-registered clients can be resolved");
56
68
  }
57
- const resourcePath = options.resourcePath ?? DEFAULT_RESOURCE_PATH;
69
+ // Function-form codeSecret is re-validated on every call in resolveSecret,
70
+ // since its value can vary per request; a string form is fixed for the
71
+ // life of this router, so fail fast at construction instead of at the
72
+ // first request.
73
+ if (typeof options.ports.codeSecret === "string") {
74
+ assertCodeSecret(options.ports.codeSecret);
75
+ }
76
+ assertScopeConfig(options);
77
+ // Normalized once here, not per request — "/mcp/" and "/mcp" must never
78
+ // produce divergent canonical resources (canonicalResource doesn't strip
79
+ // a trailing slash itself; normalizeResource, used to compare an
80
+ // incoming request's resource against it, does).
81
+ const resourcePath = normalizeConfiguredPath(options.resourcePath ?? DEFAULT_RESOURCE_PATH);
58
82
  const oauthPath = options.oauthPath ?? "/mcp/oauth";
83
+ if (oauthPath === resourcePath) {
84
+ throw new Error(`oauthPath (${oauthPath}) must not equal resourcePath — they would shadow each other`);
85
+ }
86
+ if (resourcePath.startsWith("/.well-known")) {
87
+ throw new Error(`resourcePath (${resourcePath}) must not start with /.well-known — reserved for OAuth discovery documents`);
88
+ }
59
89
  const prmPaths = new Set([
60
90
  "/.well-known/oauth-protected-resource",
61
91
  `/.well-known/oauth-protected-resource${resourcePath}`,
@@ -71,6 +101,7 @@ export const createOAuthRouter = (options) => {
71
101
  }
72
102
  : {}),
73
103
  [`${oauthPath}/authorize`]: (request) => handleAuthorize(request, options),
104
+ [`${oauthPath}/consent`]: (request) => handleConsent(request, options),
74
105
  [`${oauthPath}/token`]: (request) => handleToken(request, options),
75
106
  ...(options.ports.revokeToken
76
107
  ? {
@@ -93,22 +124,31 @@ export const createOAuthRouter = (options) => {
93
124
  const route = routes[path];
94
125
  return route ? await route(request) : null;
95
126
  }
96
- catch {
97
- // A host port (resolveUser/mintAccessToken/clientStore/...) threw.
98
- // We only ever reach here on a route this router owns, so answer
99
- // with a real error instead of throwing out of `tryHandle`.
127
+ catch (exc) {
128
+ // A host port (resolveUser/mintAccessToken/clientStore/...) threw,
129
+ // or codeSecret failed validation (assertCodeSecret, e.g. a
130
+ // per-request function returning something too short). We only
131
+ // ever reach here on a route this router owns, so answer with a
132
+ // real error instead of throwing out of `tryHandle` — the caller
133
+ // gets the generic server_error; ports.audit gets the real reason.
134
+ await safeOAuthAudit(options, {
135
+ event: "server_error",
136
+ reason: exc instanceof Error ? exc.message : String(exc),
137
+ });
100
138
  return oauthError(OAUTH_ERRORS.serverError, 500, INTERNAL_ERROR);
101
139
  }
102
140
  },
103
141
  };
104
142
  };
105
- export { authorizationServerMetadata, protectedResourceMetadata, mcpWwwAuthenticate, } from "./metadata.js";
106
- export { canonicalResource, resourcesEqual, DEFAULT_RESOURCE_PATH, firstResourceError, } from "./resource.js";
107
- export { isAllowedRedirectUri, CLAUDE_CALLBACK } from "./redirect.js";
108
- export { issueAuthCode, consumeAuthCode, newClientId, } from "./codes.js";
109
- export { verifyPkceS256, sha256Base64Url, randomBase64Url } from "./pkce.js";
110
- export { GRANT_TYPES, OAUTH_ERRORS, DEFAULT_SCOPE, TOKEN_ENDPOINT_AUTH_METHODS, } from "./constants.js";
111
- export { parseScope, formatScope, requestedScopes, firstScopeError, } from "./scope.js";
112
- export { readClientAuth, firstClientAuthError } from "./clientauth.js";
113
- export { unregisteredClientsAllowed } from "./types.js";
143
+ export { firstClientAuthError, readClientAuth } from "./clientauth.js";
144
+ export { consumeAuthCode, issueAuthCode, newClientId, } from "./codes.js";
145
+ export { buildErrorRedirectUrl } from "./consent.js";
146
+ export { DEFAULT_SCOPE, GRANT_TYPES, OAUTH_ERRORS, TOKEN_ENDPOINT_AUTH_METHODS, } from "./constants.js";
147
+ export { authorizationServerMetadata, mcpWwwAuthenticate, protectedResourceMetadata, } from "./metadata.js";
148
+ export { randomBase64Url, sha256Base64Url, verifyPkceS256 } from "./pkce.js";
149
+ export { CLAUDE_CALLBACK, isAllowedRedirectUri } from "./redirect.js";
150
+ export { canonicalResource, DEFAULT_RESOURCE_PATH, firstResourceError, normalizeConfiguredPath, resourceErrorInfo, resourcesEqual, } from "./resource.js";
151
+ export { firstScopeError, formatScope, parseScope, requestedScopes, scopeErrorInfo, } from "./scope.js";
152
+ export { hashClientSecret, verifyClientSecret } from "./secrethash.js";
153
+ export { advertisedScopes, defaultScopes, registeredClientsRequired, unregisteredClientsAllowed, } from "./types.js";
114
154
  //# sourceMappingURL=router.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"router.js","sourceRoot":"","sources":["../../src/oauth/router.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"router.js","sourceRoot":"","sources":["../../src/oauth/router.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,20 @@
1
1
  /** OAuth scope handling — space-delimited sets (RFC 6749 §3.3). */
2
+ import { type OAuthErrorInfo } from "./types.js";
2
3
  export declare const parseScope: (raw: string) => string[];
3
4
  export declare const formatScope: (scopes: string[]) => string;
4
5
  /**
5
- * Scopes the client asked for, or the full advertised set when `scope` is omitted.
6
+ * Scopes the client asked for, or `fallback` when `scope` is omitted —
7
+ * `fallback` is the full advertised set only for single-scope servers; see
8
+ * `defaultScopes`/`assertScopeConfig` in `types.ts` for the multi-scope rule.
6
9
  * Duplicates are collapsed so the granted string is stable.
7
10
  */
8
- export declare const requestedScopes: (raw: string, advertised: string[]) => string[];
11
+ export declare const requestedScopes: (raw: string, fallback: string[]) => string[];
12
+ /**
13
+ * Any scope outside the advertised set → invalid_scope. Info-only variant
14
+ * — see `resource.ts`'s `resourceErrorInfo` for why this isn't a
15
+ * `Response` directly: `/authorize` needs to redirect this, not return it.
16
+ */
17
+ export declare const scopeErrorInfo: (requested: string[], advertised: string[]) => OAuthErrorInfo | null;
9
18
  /** Any scope outside the advertised set → invalid_scope. */
10
19
  export declare const firstScopeError: (requested: string[], advertised: string[]) => Response | null;
11
20
  //# sourceMappingURL=scope.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"scope.d.ts","sourceRoot":"","sources":["../../src/oauth/scope.ts"],"names":[],"mappings":"AAAA,mEAAmE;AAKnE,eAAO,MAAM,UAAU,GAAI,KAAK,MAAM,KAAG,MAAM,EAIP,CAAC;AAEzC,eAAO,MAAM,WAAW,GAAI,QAAQ,MAAM,EAAE,KAAG,MAA0B,CAAC;AAE1E;;;GAGG;AACH,eAAO,MAAM,eAAe,GAC1B,KAAK,MAAM,EACX,YAAY,MAAM,EAAE,KACnB,MAAM,EAGR,CAAC;AAEF,4DAA4D;AAC5D,eAAO,MAAM,eAAe,GAC1B,WAAW,MAAM,EAAE,EACnB,YAAY,MAAM,EAAE,KACnB,QAAQ,GAAG,IAQb,CAAC"}
1
+ {"version":3,"file":"scope.d.ts","sourceRoot":"","sources":["../../src/oauth/scope.ts"],"names":[],"mappings":"AAAA,mEAAmE;AAGnE,OAAO,EAAE,KAAK,cAAc,EAAc,MAAM,YAAY,CAAC;AAE7D,eAAO,MAAM,UAAU,GAAI,KAAK,MAAM,KAAG,MAAM,EAIP,CAAC;AAEzC,eAAO,MAAM,WAAW,GAAI,QAAQ,MAAM,EAAE,KAAG,MAA0B,CAAC;AAE1E;;;;;GAKG;AACH,eAAO,MAAM,eAAe,GAAI,KAAK,MAAM,EAAE,UAAU,MAAM,EAAE,KAAG,MAAM,EAGvE,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,cAAc,GACzB,WAAW,MAAM,EAAE,EACnB,YAAY,MAAM,EAAE,KACnB,cAAc,GAAG,IAKnB,CAAC;AAEF,4DAA4D;AAC5D,eAAO,MAAM,eAAe,GAAI,WAAW,MAAM,EAAE,EAAE,YAAY,MAAM,EAAE,KAAG,QAAQ,GAAG,IAGtF,CAAC"}
@@ -7,18 +7,29 @@ export const parseScope = (raw) => raw
7
7
  .filter((entry) => entry.length > 0);
8
8
  export const formatScope = (scopes) => scopes.join(" ");
9
9
  /**
10
- * Scopes the client asked for, or the full advertised set when `scope` is omitted.
10
+ * Scopes the client asked for, or `fallback` when `scope` is omitted —
11
+ * `fallback` is the full advertised set only for single-scope servers; see
12
+ * `defaultScopes`/`assertScopeConfig` in `types.ts` for the multi-scope rule.
11
13
  * Duplicates are collapsed so the granted string is stable.
12
14
  */
13
- export const requestedScopes = (raw, advertised) => {
14
- const parsed = raw ? parseScope(raw) : [...advertised];
15
+ export const requestedScopes = (raw, fallback) => {
16
+ const parsed = raw ? parseScope(raw) : [...fallback];
15
17
  return [...new Set(parsed)];
16
18
  };
19
+ /**
20
+ * Any scope outside the advertised set → invalid_scope. Info-only variant
21
+ * — see `resource.ts`'s `resourceErrorInfo` for why this isn't a
22
+ * `Response` directly: `/authorize` needs to redirect this, not return it.
23
+ */
24
+ export const scopeErrorInfo = (requested, advertised) => {
25
+ const unknown = requested.find((scope) => !advertised.includes(scope));
26
+ return unknown === undefined
27
+ ? null
28
+ : { code: OAUTH_ERRORS.invalidScope, description: `unsupported scope: ${unknown}` };
29
+ };
17
30
  /** Any scope outside the advertised set → invalid_scope. */
18
31
  export const firstScopeError = (requested, advertised) => {
19
- const unknown = requested.find((scope) => !advertised.includes(scope));
20
- if (unknown === undefined)
21
- return null;
22
- return oauthError(OAUTH_ERRORS.invalidScope, 400, `unsupported scope: ${unknown}`);
32
+ const issue = scopeErrorInfo(requested, advertised);
33
+ return issue ? oauthError(issue.code, 400, issue.description) : null;
23
34
  };
24
35
  //# sourceMappingURL=scope.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"scope.js","sourceRoot":"","sources":["../../src/oauth/scope.ts"],"names":[],"mappings":"AAAA,mEAAmE;AAEnE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,GAAW,EAAY,EAAE,CAClD,GAAG;KACA,KAAK,CAAC,GAAG,CAAC;KACV,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;KAC5B,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AAEzC,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,MAAgB,EAAU,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAE1E;;;GAGG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,GAAW,EACX,UAAoB,EACV,EAAE;IACZ,MAAM,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC;IACvD,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;AAC9B,CAAC,CAAC;AAEF,4DAA4D;AAC5D,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,SAAmB,EACnB,UAAoB,EACH,EAAE;IACnB,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;IACvE,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACvC,OAAO,UAAU,CACf,YAAY,CAAC,YAAY,EACzB,GAAG,EACH,sBAAsB,OAAO,EAAE,CAChC,CAAC;AACJ,CAAC,CAAC"}
1
+ {"version":3,"file":"scope.js","sourceRoot":"","sources":["../../src/oauth/scope.ts"],"names":[],"mappings":"AAAA,mEAAmE;AAEnE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAuB,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7D,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,GAAW,EAAY,EAAE,CAClD,GAAG;KACA,KAAK,CAAC,GAAG,CAAC;KACV,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;KAC5B,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AAEzC,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,MAAgB,EAAU,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAE1E;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,GAAW,EAAE,QAAkB,EAAY,EAAE;IAC3E,MAAM,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC;IACrD,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;AAC9B,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAC5B,SAAmB,EACnB,UAAoB,EACG,EAAE;IACzB,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;IACvE,OAAO,OAAO,KAAK,SAAS;QAC1B,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,EAAE,IAAI,EAAE,YAAY,CAAC,YAAY,EAAE,WAAW,EAAE,sBAAsB,OAAO,EAAE,EAAE,CAAC;AACxF,CAAC,CAAC;AAEF,4DAA4D;AAC5D,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,SAAmB,EAAE,UAAoB,EAAmB,EAAE;IAC5F,MAAM,KAAK,GAAG,cAAc,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IACpD,OAAO,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACvE,CAAC,CAAC"}
@@ -0,0 +1,17 @@
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`).
5
+ *
6
+ * Domain separation is mandatory, not a nice-to-have: a consent ticket must
7
+ * never unseal as a client-id assertion, and neither must ever unseal as an
8
+ * auth code. HKDF derives an independent AES-256 key per `SealedType` from
9
+ * the same secret, and the type is also bound in as AEAD associated data —
10
+ * two independent reasons decryption fails across types, not one.
11
+ */
12
+ export type SealedType = "consent" | "client" | "code";
13
+ /** Seals `payload` as an opaque, type-bound string. */
14
+ export declare const seal: <T>(secret: string, type: SealedType, payload: T) => Promise<string>;
15
+ /** Unseals a string sealed with the same `secret` and `type`; `null` on any mismatch or tamper. */
16
+ export declare const unseal: <T>(secret: string, type: SealedType, sealed: string) => Promise<T | null>;
17
+ //# sourceMappingURL=sealed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sealed.d.ts","sourceRoot":"","sources":["../../src/oauth/sealed.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAC;AA+BvD,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"}
@@ -0,0 +1,57 @@
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`).
5
+ *
6
+ * Domain separation is mandatory, not a nice-to-have: a consent ticket must
7
+ * never unseal as a client-id assertion, and neither must ever unseal as an
8
+ * auth code. HKDF derives an independent AES-256 key per `SealedType` from
9
+ * the same secret, and the type is also bound in as AEAD associated data —
10
+ * two independent reasons decryption fails across types, not one.
11
+ */
12
+ import { bytesToBase64Url, fromBase64Url } from "./base64url.js";
13
+ const HKDF_INFO_PREFIX = "mcp-trellis:sealed:";
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
+ };
24
+ /** Seals `payload` as an opaque, type-bound string. */
25
+ export const seal = async (secret, type, payload) => {
26
+ const key = await deriveKey(secret, type, "encrypt");
27
+ const iv = crypto.getRandomValues(new Uint8Array(IV_BYTES));
28
+ const additionalData = new TextEncoder().encode(type);
29
+ const plaintext = new TextEncoder().encode(JSON.stringify(payload));
30
+ const ciphertext = await crypto.subtle.encrypt({ name: "AES-GCM", iv, additionalData }, key, plaintext);
31
+ return `${bytesToBase64Url(iv)}.${bytesToBase64Url(ciphertext)}`;
32
+ };
33
+ /** Unseals a string sealed with the same `secret` and `type`; `null` on any mismatch or tamper. */
34
+ export const unseal = async (secret, type, sealed) => {
35
+ if (!sealed)
36
+ return null;
37
+ const parts = sealed.split(".");
38
+ if (parts.length !== 2)
39
+ return null;
40
+ const [ivPart, ciphertextPart] = parts;
41
+ if (!ivPart || !ciphertextPart)
42
+ return null;
43
+ try {
44
+ const key = await deriveKey(secret, type, "decrypt");
45
+ const iv = fromBase64Url(ivPart);
46
+ if (iv.byteLength !== IV_BYTES)
47
+ return null;
48
+ const ciphertext = fromBase64Url(ciphertextPart);
49
+ const additionalData = new TextEncoder().encode(type);
50
+ const plaintext = await crypto.subtle.decrypt({ name: "AES-GCM", iv, additionalData }, key, ciphertext);
51
+ return JSON.parse(new TextDecoder().decode(plaintext));
52
+ }
53
+ catch {
54
+ return null;
55
+ }
56
+ };
57
+ //# sourceMappingURL=sealed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sealed.js","sourceRoot":"","sources":["../../src/oauth/sealed.ts"],"names":[],"mappings":"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"}