acrylblends 0.6.2 → 0.6.4

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 (316) hide show
  1. package/README.md +5 -1
  2. package/docs/framework/README.md +3 -2
  3. package/docs/framework/UPSTREAM.md +1 -1
  4. package/docs/framework/cordis/acryl-in-a-cordis-app.md +6 -0
  5. package/docs/framework/getting-started/basic.md +1 -0
  6. package/docs/framework/getting-started/desktop.md +41 -0
  7. package/docs/framework/guides/jsx-dom.md +0 -3
  8. package/docs/framework/guides/jsx.md +24 -3
  9. package/docs/framework/helpers/css.md +0 -3
  10. package/docs/framework/helpers/ssg.md +3 -6
  11. package/docs/framework/middleware/builtin/jsx-renderer.md +0 -3
  12. package/docs/framework/upstream.json +3 -3
  13. package/lib/accepts.d.ts +1 -1
  14. package/lib/acryl-base.d.ts +1 -1
  15. package/lib/adapter.d.ts +1 -1
  16. package/lib/{app-BdrVHBlJ.d.ts → app-D8riatvv.d.ts} +1 -1
  17. package/lib/base-BpQy2Rk7.d.ts +783 -0
  18. package/lib/base-ddiVmiE7.js +721 -0
  19. package/lib/basic-auth.d.ts +1 -1
  20. package/lib/bearer-auth.d.ts +1 -1
  21. package/lib/body-limit.d.ts +1 -1
  22. package/lib/cache.d.ts +1 -1
  23. package/lib/cache.js +4 -4
  24. package/lib/{client-BHTr318g.js → client-DDLXwhBo.js} +2 -125
  25. package/lib/client.d.ts +2 -2
  26. package/lib/client.js +2 -1
  27. package/lib/combine.d.ts +1 -1
  28. package/lib/common-D8Z4JRCw.d.ts +46 -0
  29. package/lib/components-0o0CBYNv.js +268 -0
  30. package/lib/components-DdI7-ns7.js +24 -0
  31. package/lib/compress.d.ts +1 -1
  32. package/lib/conninfo.d.ts +2 -2
  33. package/lib/constants-BR9jM9BC.js +9 -0
  34. package/lib/{context-B5Y1lLha.d.ts → context-DS3fYyG-.d.ts} +2 -2
  35. package/lib/context-DxFdY_un.d.ts +250 -0
  36. package/lib/context-storage.d.ts +1 -1
  37. package/lib/{cookie-D9_nQbsM.d.ts → cookie-CCelEqky.d.ts} +1 -1
  38. package/lib/cookie.d.ts +3 -3
  39. package/lib/cors.d.ts +1 -1
  40. package/lib/csrf.d.ts +1 -1
  41. package/lib/css-D2jb7X8b.js +320 -0
  42. package/lib/css.d.ts +82 -0
  43. package/lib/css.js +134 -0
  44. package/lib/dev.d.ts +2 -2
  45. package/lib/etag.d.ts +1 -1
  46. package/lib/factory.d.ts +2 -2
  47. package/lib/hooks-BMw6BAyP.js +762 -0
  48. package/lib/html-SllAe0YT.d.ts +46 -0
  49. package/lib/html.d.ts +1 -1
  50. package/lib/{index-m12zcmph.d.ts → index-Bb7YJKCq.d.ts} +3 -3
  51. package/lib/{index-Kqou8Fjh.d.ts → index-BvFh1Gfx.d.ts} +2 -2
  52. package/lib/index-C850MeBA.d.ts +94 -0
  53. package/lib/{index-DtEoak1i.d.ts → index-CTy4N7ng.d.ts} +2 -2
  54. package/lib/index.d.ts +4 -4
  55. package/lib/ip-restriction.d.ts +2 -2
  56. package/lib/jsx/dom/client.d.ts +30 -0
  57. package/lib/jsx/dom/client.js +49 -0
  58. package/lib/jsx/dom/css.d.ts +84 -0
  59. package/lib/jsx/dom/css.js +2 -0
  60. package/lib/jsx/dom/jsx-dev-runtime.d.ts +3 -0
  61. package/lib/jsx/dom/jsx-dev-runtime.js +2 -0
  62. package/lib/jsx/dom/jsx-runtime.d.ts +3 -0
  63. package/lib/jsx/dom/jsx-runtime.js +2 -0
  64. package/lib/jsx/dom/server.d.ts +105 -0
  65. package/lib/jsx/dom/server.js +37 -0
  66. package/lib/jsx/dom.d.ts +5 -0
  67. package/lib/jsx/dom.js +74 -0
  68. package/lib/jsx/jsx-dev-runtime.d.ts +6 -0
  69. package/lib/jsx/jsx-dev-runtime.js +14 -0
  70. package/lib/jsx/jsx-runtime.d.ts +10 -0
  71. package/lib/jsx/jsx-runtime.js +30 -0
  72. package/lib/jsx/streaming.d.ts +2 -0
  73. package/lib/jsx/streaming.js +2 -0
  74. package/lib/jsx-dev-runtime-B22O-Npk.d.ts +7 -0
  75. package/lib/jsx-dev-runtime-D18Y35TI.js +354 -0
  76. package/lib/jsx-renderer.d.ts +75 -0
  77. package/lib/jsx-renderer.js +99 -0
  78. package/lib/jsx-runtime-BVVgDSdq.d.ts +1 -0
  79. package/lib/jsx.d.ts +3 -0
  80. package/lib/jsx.js +47 -0
  81. package/lib/jwk.d.ts +4 -4
  82. package/lib/{jwt-BA65QhnJ.d.ts → jwt-BARhotDz.d.ts} +1 -1
  83. package/lib/jwt.d.ts +4 -4
  84. package/lib/language.d.ts +1 -1
  85. package/lib/logger.d.ts +1 -1
  86. package/lib/loopback-only.d.ts +1 -1
  87. package/lib/method-not-allowed.d.ts +2 -2
  88. package/lib/method-override.d.ts +2 -2
  89. package/lib/mount.d.ts +1 -1
  90. package/lib/node-server/conninfo.d.ts +1 -1
  91. package/lib/node-server/serve-static.d.ts +1 -1
  92. package/lib/node-server/serve-static.js +1 -1
  93. package/lib/node-server.d.ts +1 -1
  94. package/lib/node-server.js +1 -1
  95. package/lib/powered-by.d.ts +1 -1
  96. package/lib/pretty-json.d.ts +1 -1
  97. package/lib/proxy.d.ts +1 -1
  98. package/lib/quick.d.ts +1 -1
  99. package/lib/request-id.d.ts +1 -1
  100. package/lib/request.d.ts +1 -1
  101. package/lib/route.d.ts +1 -1
  102. package/lib/schema-validator.d.ts +1 -1
  103. package/lib/secure-headers.d.ts +1 -1
  104. package/lib/{serve-static-Dw_m6KxR.js → serve-static-B14MF1Va.js} +2 -0
  105. package/lib/serve-static.d.ts +1 -1
  106. package/lib/ssg-BwjcA-Ge.js +389 -0
  107. package/lib/ssg.d.ts +156 -0
  108. package/lib/ssg.js +2 -0
  109. package/lib/streaming.d.ts +1 -1
  110. package/lib/testing.d.ts +4 -4
  111. package/lib/testing.js +1 -1
  112. package/lib/timeout.d.ts +1 -1
  113. package/lib/timing.d.ts +1 -1
  114. package/lib/tiny.d.ts +1 -1
  115. package/lib/trailing-slash.d.ts +1 -1
  116. package/lib/{types-ZM9gIiKh.d.ts → types-Dv_nV2cv.d.ts} +1 -1
  117. package/lib/{types-D9DEXMjE.d.ts → types-Lqx5BxQF.d.ts} +8 -1
  118. package/lib/{types-Bd29gh2o.d.ts → types-zNxyEpG2.d.ts} +3 -3
  119. package/lib/types.d.ts +1 -1
  120. package/lib/utils/body.d.ts +1 -1
  121. package/lib/utils/cookie.d.ts +1 -1
  122. package/lib/utils/crypto.d.ts +1 -1
  123. package/lib/utils/html.d.ts +1 -45
  124. package/lib/utils/ipaddr.d.ts +1 -1
  125. package/lib/utils/jwt.d.ts +1 -1
  126. package/lib/utils/types.d.ts +2 -2
  127. package/lib/utils-Cx61GFMC.js +125 -0
  128. package/lib/validator.d.ts +2 -2
  129. package/lib/ws.d.ts +1 -1
  130. package/package.json +53 -1
  131. package/plugins/acryl-inspect/upstream.json +1 -1
  132. package/plugins/acryl-node-server/upstream.json +1 -1
  133. package/plugins/acryl-router/src/core/helper/css/common.ts +337 -0
  134. package/plugins/acryl-router/src/core/helper/css/index.ts +262 -0
  135. package/plugins/acryl-router/src/core/helper/ssg/index.ts +14 -0
  136. package/plugins/acryl-router/src/core/helper/ssg/middleware.ts +83 -0
  137. package/plugins/acryl-router/src/core/helper/ssg/plugins.ts +72 -0
  138. package/plugins/acryl-router/src/core/helper/ssg/ssg.ts +471 -0
  139. package/plugins/acryl-router/src/core/helper/ssg/utils.ts +118 -0
  140. package/plugins/acryl-router/src/core/jsx/base.ts +490 -0
  141. package/plugins/acryl-router/src/core/jsx/children.ts +20 -0
  142. package/plugins/acryl-router/src/core/jsx/components.ts +257 -0
  143. package/plugins/acryl-router/src/core/jsx/constants.ts +6 -0
  144. package/plugins/acryl-router/src/core/jsx/context.ts +266 -0
  145. package/plugins/acryl-router/src/core/jsx/dom/client.ts +89 -0
  146. package/plugins/acryl-router/src/core/jsx/dom/components.ts +39 -0
  147. package/plugins/acryl-router/src/core/jsx/dom/context.ts +52 -0
  148. package/plugins/acryl-router/src/core/jsx/dom/css.ts +263 -0
  149. package/plugins/acryl-router/src/core/jsx/dom/hooks/index.ts +91 -0
  150. package/plugins/acryl-router/src/core/jsx/dom/index.ts +170 -0
  151. package/plugins/acryl-router/src/core/jsx/dom/intrinsic-element/components.ts +438 -0
  152. package/plugins/acryl-router/src/core/jsx/dom/jsx-dev-runtime.ts +23 -0
  153. package/plugins/acryl-router/src/core/jsx/dom/jsx-runtime.ts +8 -0
  154. package/plugins/acryl-router/src/core/jsx/dom/render.ts +879 -0
  155. package/plugins/acryl-router/src/core/jsx/dom/server.ts +76 -0
  156. package/plugins/acryl-router/src/core/jsx/dom/utils.ts +7 -0
  157. package/plugins/acryl-router/src/core/jsx/hooks/index.ts +437 -0
  158. package/plugins/acryl-router/src/core/jsx/index.ts +115 -0
  159. package/plugins/acryl-router/src/core/jsx/intrinsic-element/common.ts +23 -0
  160. package/plugins/acryl-router/src/core/jsx/intrinsic-element/components.ts +210 -0
  161. package/plugins/acryl-router/src/core/jsx/intrinsic-elements.ts +938 -0
  162. package/plugins/acryl-router/src/core/jsx/jsx-dev-runtime.ts +26 -0
  163. package/plugins/acryl-router/src/core/jsx/jsx-runtime.ts +51 -0
  164. package/plugins/acryl-router/src/core/jsx/streaming.ts +231 -0
  165. package/plugins/acryl-router/src/core/jsx/types.ts +41 -0
  166. package/plugins/acryl-router/src/core/jsx/utils.ts +202 -0
  167. package/plugins/acryl-router/src/core/middleware/cache/index.ts +4 -4
  168. package/plugins/acryl-router/src/core/middleware/jsx-renderer/index.ts +162 -0
  169. package/plugins/acryl-router/src/core/utils/types.ts +6 -0
  170. package/plugins/acryl-router/src/helpers/index.ts +2 -0
  171. package/plugins/acryl-router/src/middleware/index.ts +1 -0
  172. package/plugins/acryl-router/upstream.json +3 -3
  173. package/plugins/acryl-router-http/upstream.json +1 -1
  174. package/plugins/acryl-state/upstream.json +1 -1
  175. package/plugins/desktop/upstream.json +1 -1
  176. package/plugins/desktop-web/upstream.json +1 -1
  177. package/plugins/extensions/resolve.ts +4 -0
  178. package/plugins/save/plugin.json +1 -1
  179. package/shared/AGENTS.core.md +4 -0
  180. package/templates/agent/tsconfig.json +7 -1
  181. package/templates/todo/upstream.json +1 -1
  182. package/plugins/acryl-inspect/tests/report.spec.ts +0 -69
  183. package/plugins/acryl-inspect/tests/service.spec.ts +0 -121
  184. package/plugins/acryl-node-server/tests/adapter/app.ts +0 -76
  185. package/plugins/acryl-node-server/tests/adapter/assets/.static/plain.txt +0 -1
  186. package/plugins/acryl-node-server/tests/adapter/assets/favicon.ico +0 -0
  187. package/plugins/acryl-node-server/tests/adapter/assets/secret.txt +0 -1
  188. package/plugins/acryl-node-server/tests/adapter/assets/static/acryl.html +0 -1
  189. package/plugins/acryl-node-server/tests/adapter/assets/static/admin/secret.txt +0 -1
  190. package/plugins/acryl-node-server/tests/adapter/assets/static/data.json +0 -5
  191. package/plugins/acryl-node-server/tests/adapter/assets/static/extensionless +0 -1
  192. package/plugins/acryl-node-server/tests/adapter/assets/static/foo..bar.txt +0 -0
  193. package/plugins/acryl-node-server/tests/adapter/assets/static/index.html +0 -1
  194. package/plugins/acryl-node-server/tests/adapter/assets/static/plain.txt +0 -1
  195. package/plugins/acryl-node-server/tests/adapter/assets/static-with-context-path/plain.txt +0 -1
  196. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.bin +0 -1
  197. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.bin.br +0 -1
  198. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt +0 -1
  199. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt.br +0 -1
  200. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt.zst +0 -1
  201. package/plugins/acryl-node-server/tests/adapter/conninfo.test.ts +0 -34
  202. package/plugins/acryl-node-server/tests/adapter/content-length.test.ts +0 -248
  203. package/plugins/acryl-node-server/tests/adapter/early-hints.test.ts +0 -457
  204. package/plugins/acryl-node-server/tests/adapter/fixtures/keys/agent1-cert.pem +0 -23
  205. package/plugins/acryl-node-server/tests/adapter/fixtures/keys/agent1-key.pem +0 -27
  206. package/plugins/acryl-node-server/tests/adapter/headers.test.ts +0 -252
  207. package/plugins/acryl-node-server/tests/adapter/helpers/request.ts +0 -190
  208. package/plugins/acryl-node-server/tests/adapter/listener.test.ts +0 -727
  209. package/plugins/acryl-node-server/tests/adapter/request-client-disconnect.test.ts +0 -189
  210. package/plugins/acryl-node-server/tests/adapter/request.test.ts +0 -1230
  211. package/plugins/acryl-node-server/tests/adapter/response.test.ts +0 -285
  212. package/plugins/acryl-node-server/tests/adapter/serve-static.test.ts +0 -849
  213. package/plugins/acryl-node-server/tests/adapter/server-socket.test.ts +0 -790
  214. package/plugins/acryl-node-server/tests/adapter/server.test.ts +0 -1312
  215. package/plugins/acryl-node-server/tests/adapter/setup.ts +0 -16
  216. package/plugins/acryl-node-server/tests/adapter/url.test.ts +0 -76
  217. package/plugins/acryl-node-server/tests/adapter/utils/response.test.ts +0 -23
  218. package/plugins/acryl-node-server/tests/adapter/utils/stream.test.ts +0 -75
  219. package/plugins/acryl-node-server/tests/adapter/utils.test.ts +0 -235
  220. package/plugins/acryl-node-server/tests/adapter/websocket.test.ts +0 -260
  221. package/plugins/acryl-node-server/tests/server.spec.ts +0 -141
  222. package/plugins/acryl-router/src/core/app.test.ts +0 -3833
  223. package/plugins/acryl-router/src/core/client/client.test.ts +0 -2313
  224. package/plugins/acryl-router/src/core/client/types.test.ts +0 -149
  225. package/plugins/acryl-router/src/core/client/utils.test.ts +0 -405
  226. package/plugins/acryl-router/src/core/compose.test.ts +0 -751
  227. package/plugins/acryl-router/src/core/context.test.ts +0 -629
  228. package/plugins/acryl-router/src/core/helper/accepts/accepts.test.ts +0 -344
  229. package/plugins/acryl-router/src/core/helper/adapter/index.test.ts +0 -55
  230. package/plugins/acryl-router/src/core/helper/cookie/index.test.ts +0 -510
  231. package/plugins/acryl-router/src/core/helper/dev/index.test.ts +0 -181
  232. package/plugins/acryl-router/src/core/helper/factory/index.test.ts +0 -434
  233. package/plugins/acryl-router/src/core/helper/html/index.test.ts +0 -78
  234. package/plugins/acryl-router/src/core/helper/proxy/index.test.ts +0 -356
  235. package/plugins/acryl-router/src/core/helper/route/index.test.ts +0 -259
  236. package/plugins/acryl-router/src/core/helper/streaming/stream.test.ts +0 -128
  237. package/plugins/acryl-router/src/core/helper/streaming/text.test.ts +0 -34
  238. package/plugins/acryl-router/src/core/helper/testing/index.test.ts +0 -42
  239. package/plugins/acryl-router/src/core/helper/websocket/index.test.ts +0 -124
  240. package/plugins/acryl-router/src/core/http-exception.test.ts +0 -45
  241. package/plugins/acryl-router/src/core/middleware/basic-auth/index.test.ts +0 -434
  242. package/plugins/acryl-router/src/core/middleware/bearer-auth/index.test.ts +0 -1018
  243. package/plugins/acryl-router/src/core/middleware/body-limit/index.test.ts +0 -273
  244. package/plugins/acryl-router/src/core/middleware/cache/index.test.ts +0 -1211
  245. package/plugins/acryl-router/src/core/middleware/combine/index.test.ts +0 -355
  246. package/plugins/acryl-router/src/core/middleware/compress/index.test.ts +0 -535
  247. package/plugins/acryl-router/src/core/middleware/context-storage/index.test.ts +0 -43
  248. package/plugins/acryl-router/src/core/middleware/cors/index.test.ts +0 -532
  249. package/plugins/acryl-router/src/core/middleware/csrf/index.test.ts +0 -534
  250. package/plugins/acryl-router/src/core/middleware/etag/digest.test.ts +0 -21
  251. package/plugins/acryl-router/src/core/middleware/etag/index.test.ts +0 -503
  252. package/plugins/acryl-router/src/core/middleware/ip-restriction/index.test.ts +0 -217
  253. package/plugins/acryl-router/src/core/middleware/jwk/index.test.ts +0 -1135
  254. package/plugins/acryl-router/src/core/middleware/jwk/keys.test.json +0 -48
  255. package/plugins/acryl-router/src/core/middleware/jwt/index.test.ts +0 -802
  256. package/plugins/acryl-router/src/core/middleware/language/index.test.ts +0 -552
  257. package/plugins/acryl-router/src/core/middleware/logger/index.test.ts +0 -204
  258. package/plugins/acryl-router/src/core/middleware/method-not-allowed/index.test.ts +0 -292
  259. package/plugins/acryl-router/src/core/middleware/method-override/index.test.ts +0 -221
  260. package/plugins/acryl-router/src/core/middleware/mount/index.test.ts +0 -334
  261. package/plugins/acryl-router/src/core/middleware/powered-by/index.test.ts +0 -37
  262. package/plugins/acryl-router/src/core/middleware/pretty-json/index.test.ts +0 -218
  263. package/plugins/acryl-router/src/core/middleware/request-id/index.test.ts +0 -168
  264. package/plugins/acryl-router/src/core/middleware/secure-headers/index.test.ts +0 -671
  265. package/plugins/acryl-router/src/core/middleware/serve-static/index.test.ts +0 -450
  266. package/plugins/acryl-router/src/core/middleware/serve-static/path.test.ts +0 -50
  267. package/plugins/acryl-router/src/core/middleware/timeout/index.test.ts +0 -70
  268. package/plugins/acryl-router/src/core/middleware/timing/index.test.ts +0 -310
  269. package/plugins/acryl-router/src/core/middleware/trailing-slash/index.test.ts +0 -381
  270. package/plugins/acryl-router/src/core/preset/quick.test.ts +0 -42
  271. package/plugins/acryl-router/src/core/preset/tiny.test.ts +0 -42
  272. package/plugins/acryl-router/src/core/request.test.ts +0 -751
  273. package/plugins/acryl-router/src/core/router/common.case.test.ts +0 -1132
  274. package/plugins/acryl-router/src/core/router/linear-router/router.test.ts +0 -80
  275. package/plugins/acryl-router/src/core/router/pattern-router/router.test.ts +0 -46
  276. package/plugins/acryl-router/src/core/router/reg-exp-router/prepared-router.test.ts +0 -154
  277. package/plugins/acryl-router/src/core/router/reg-exp-router/router.test.ts +0 -265
  278. package/plugins/acryl-router/src/core/router/smart-router/router.test.ts +0 -13
  279. package/plugins/acryl-router/src/core/router/trie-router/node.test.ts +0 -896
  280. package/plugins/acryl-router/src/core/router/trie-router/router.test.ts +0 -8
  281. package/plugins/acryl-router/src/core/types.test.ts +0 -4134
  282. package/plugins/acryl-router/src/core/utils/accept.test.ts +0 -299
  283. package/plugins/acryl-router/src/core/utils/basic-auth.test.ts +0 -56
  284. package/plugins/acryl-router/src/core/utils/body.test.ts +0 -496
  285. package/plugins/acryl-router/src/core/utils/buffer.test.ts +0 -147
  286. package/plugins/acryl-router/src/core/utils/color.test.ts +0 -23
  287. package/plugins/acryl-router/src/core/utils/concurrent.test.ts +0 -69
  288. package/plugins/acryl-router/src/core/utils/cookie.test.ts +0 -460
  289. package/plugins/acryl-router/src/core/utils/crypto.test.ts +0 -43
  290. package/plugins/acryl-router/src/core/utils/encode.test.ts +0 -61
  291. package/plugins/acryl-router/src/core/utils/filepath.test.ts +0 -78
  292. package/plugins/acryl-router/src/core/utils/html.test.ts +0 -16
  293. package/plugins/acryl-router/src/core/utils/ipaddr.test.ts +0 -149
  294. package/plugins/acryl-router/src/core/utils/jwt/jwa.test.ts +0 -86
  295. package/plugins/acryl-router/src/core/utils/jwt/jwt.test.ts +0 -1788
  296. package/plugins/acryl-router/src/core/utils/mime.test.ts +0 -64
  297. package/plugins/acryl-router/src/core/utils/stream.test.ts +0 -212
  298. package/plugins/acryl-router/src/core/utils/types.test.ts +0 -295
  299. package/plugins/acryl-router/src/core/utils/url.test.ts +0 -384
  300. package/plugins/acryl-router/src/core/validator/utils.test.ts +0 -82
  301. package/plugins/acryl-router/src/core/validator/validator.test.ts +0 -1488
  302. package/plugins/acryl-router/tests/docs-examples.spec.ts +0 -152
  303. package/plugins/acryl-router/tests/live-install.spec.ts +0 -64
  304. package/plugins/acryl-router/tests/loader.spec.ts +0 -72
  305. package/plugins/acryl-router/tests/loopback-only.spec.ts +0 -80
  306. package/plugins/acryl-router/tests/no-dom/consumer.ts +0 -11
  307. package/plugins/acryl-router/tests/no-dom/tsconfig.json +0 -13
  308. package/plugins/acryl-router/tests/service.spec.ts +0 -426
  309. package/plugins/acryl-router/tests/shadow-state.spec.ts +0 -79
  310. package/plugins/acryl-router-http/tests/adapter.spec.ts +0 -174
  311. package/plugins/acryl-router-http/tests/stream.spec.ts +0 -133
  312. package/plugins/acryl-router-http/tests/surfaces.spec.ts +0 -101
  313. package/plugins/acryl-state/src/react.ts +0 -11
  314. package/plugins/acryl-state/tests/cell.spec.ts +0 -71
  315. package/plugins/acryl-state/tests/react.spec.ts +0 -39
  316. package/plugins/acryl-state/tests/service.spec.ts +0 -132
@@ -1,1135 +0,0 @@
1
- import { HttpResponse, http } from 'msw'
2
- import { setupServer } from 'msw/node'
3
- import { jwk } from './index.ts'
4
- import { setSignedCookie } from '../../helper/cookie/index.ts'
5
- import { Acryl } from '../../app.ts'
6
- import { HTTPException } from '../../http-exception.ts'
7
- import { encodeBase64Url } from '../../utils/encode.ts'
8
- import { Jwt } from '../../utils/jwt/index.ts'
9
- import type { AcrylJsonWebKey } from '../../utils/jwt/jws.ts'
10
- import { signing } from '../../utils/jwt/jws.ts'
11
- import { verifyWithJwks } from '../../utils/jwt/jwt.ts'
12
- import type { JWTPayload } from '../../utils/jwt/types.ts'
13
- import { utf8Encoder } from '../../utils/jwt/utf8.ts'
14
- import test_keys from './keys.test.json' with { type: 'json' }
15
-
16
- const verify_keys = test_keys.public_keys
17
-
18
- describe('JWK', () => {
19
- const server = setupServer(
20
- http.get('http://localhost/.well-known/jwks.json', () => {
21
- return HttpResponse.json({ keys: verify_keys })
22
- }),
23
- http.get('http://localhost/.well-known/missing-jwks.json', () => {
24
- return HttpResponse.json({})
25
- }),
26
- http.get('http://localhost/.well-known/bad-jwks.json', () => {
27
- return HttpResponse.json({ keys: 'bad-keys' })
28
- }),
29
- http.get('http://localhost/.well-known/404-jwks.json', () => {
30
- return HttpResponse.text('Not Found', { status: 404 })
31
- })
32
- )
33
- beforeAll(() => server.listen())
34
- afterEach(() => server.resetHandlers())
35
- afterAll(() => server.close())
36
-
37
- describe('verifyWithJwks', () => {
38
- it('Should throw error on missing keys/jwks_uri options', async () => {
39
- const credential = await Jwt.sign({ message: 'hello world' }, test_keys.private_keys[0])
40
- await expect(verifyWithJwks(credential, { allowedAlgorithms: ['RS256'] })).rejects.toThrow(
41
- 'verifyWithJwks requires options for either "keys" or "jwks_uri" or both'
42
- )
43
- })
44
- })
45
-
46
- describe('options.allow_anon = true', () => {
47
- let handlerExecuted: boolean
48
-
49
- beforeEach(() => {
50
- handlerExecuted = false
51
- })
52
-
53
- const app = new Acryl()
54
-
55
- app.use('/backend-auth-or-anon/*', jwk({ keys: verify_keys, allow_anon: true, alg: ['RS256'] }))
56
-
57
- app.get('/backend-auth-or-anon/*', (c) => {
58
- handlerExecuted = true
59
- const payload = c.get('jwtPayload')
60
- return c.json(payload ?? { message: 'hello anon' })
61
- })
62
-
63
- it('Should skip JWK if no token is present', async () => {
64
- const req = new Request('http://localhost/backend-auth-or-anon/a')
65
- const res = await app.request(req)
66
- expect(res).not.toBeNull()
67
- expect(res.status).toBe(200)
68
- expect(await res.json()).toEqual({ message: 'hello anon' })
69
- expect(handlerExecuted).toBeTruthy()
70
- })
71
-
72
- it('Should authorize if token is present', async () => {
73
- const credential = await Jwt.sign({ message: 'hello world' }, test_keys.private_keys[0])
74
- const req = new Request('http://localhost/backend-auth-or-anon/a')
75
- req.headers.set('Authorization', `Bearer ${credential}`)
76
- const res = await app.request(req)
77
- expect(res).not.toBeNull()
78
- expect(res.status).toBe(200)
79
- expect(await res.json()).toEqual({ message: 'hello world' })
80
- expect(handlerExecuted).toBeTruthy()
81
- })
82
-
83
- it('Should not authorize if a non-Bearer scheme is present', async () => {
84
- const credential = await Jwt.sign({ message: 'hello world' }, test_keys.private_keys[0])
85
- const url = 'http://localhost/backend-auth-or-anon/a'
86
- const req = new Request(url)
87
- req.headers.set('Authorization', `Basic ${credential}`)
88
- const res = await app.request(req)
89
- expect(res).not.toBeNull()
90
- expect(res.status).toBe(401)
91
- expect(res.headers.get('www-authenticate')).toEqual(
92
- `Bearer realm="${url}",error="invalid_request",error_description="invalid credentials structure"`
93
- )
94
- expect(handlerExecuted).toBeFalsy()
95
- })
96
-
97
- it('Should not authorize if an invalid Bearer token is present', async () => {
98
- const invalidToken =
99
- 'ssyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXNzYWdlIjoiaGVsbG8gd29ybGQifQ.B54pAqIiLbu170tGQ1rY06Twv__0qSHTA0ioQPIOvFE'
100
- const url = 'http://localhost/backend-auth-or-anon/a'
101
- const req = new Request(url)
102
- req.headers.set('Authorization', `Bearer ${invalidToken}`)
103
- const res = await app.request(req)
104
- expect(res).not.toBeNull()
105
- expect(res.status).toBe(401)
106
- expect(res.headers.get('www-authenticate')).toEqual(
107
- `Bearer realm="${url}",error="invalid_token",error_description="token verification failure"`
108
- )
109
- expect(handlerExecuted).toBeFalsy()
110
- })
111
- })
112
-
113
- describe('Credentials in header', () => {
114
- let handlerExecuted: boolean
115
-
116
- beforeEach(() => {
117
- handlerExecuted = false
118
- })
119
-
120
- const app = new Acryl()
121
-
122
- app.use('/auth-with-keys/*', jwk({ keys: verify_keys, alg: ['RS256'] }))
123
- app.use('/auth-with-keys-unicode/*', jwk({ keys: verify_keys, alg: ['RS256'] }))
124
- app.use('/auth-with-keys-nested/*', async (c, next) => {
125
- const auth = jwk({ keys: verify_keys, alg: ['RS256'] })
126
- return auth(c, next)
127
- })
128
- app.use(
129
- '/auth-with-keys-fn/*',
130
- jwk({
131
- keys: async () => {
132
- const response = await fetch('http://localhost/.well-known/jwks.json')
133
- const data = await response.json()
134
- return data.keys
135
- },
136
- alg: ['RS256'],
137
- })
138
- )
139
- app.use(
140
- '/auth-with-jwks_uri/*',
141
- jwk({
142
- jwks_uri: 'http://localhost/.well-known/jwks.json',
143
- alg: ['RS256'],
144
- })
145
- )
146
- app.use(
147
- '/auth-with-keys-and-jwks_uri/*',
148
- jwk({
149
- keys: verify_keys,
150
- jwks_uri: () => 'http://localhost/.well-known/jwks.json',
151
- alg: ['RS256'],
152
- })
153
- )
154
- app.use(
155
- '/auth-with-missing-jwks_uri/*',
156
- jwk({
157
- jwks_uri: 'http://localhost/.well-known/missing-jwks.json',
158
- alg: ['RS256'],
159
- })
160
- )
161
- app.use(
162
- '/auth-with-404-jwks_uri/*',
163
- jwk({
164
- jwks_uri: 'http://localhost/.well-known/404-jwks.json',
165
- alg: ['RS256'],
166
- })
167
- )
168
- app.use(
169
- '/auth-with-bad-jwks_uri/*',
170
- jwk({
171
- jwks_uri: 'http://localhost/.well-known/bad-jwks.json',
172
- alg: ['RS256'],
173
- })
174
- )
175
-
176
- app.get('/auth-with-keys/*', (c) => {
177
- handlerExecuted = true
178
- const payload = c.get('jwtPayload')
179
- return c.json(payload)
180
- })
181
- app.get('/auth-with-keys-unicode/*', (c) => {
182
- handlerExecuted = true
183
- const payload = c.get('jwtPayload')
184
- return c.json(payload)
185
- })
186
- app.get('/auth-with-keys-nested/*', (c) => {
187
- handlerExecuted = true
188
- const payload = c.get('jwtPayload')
189
- return c.json(payload)
190
- })
191
- app.get('/auth-with-keys-fn/*', (c) => {
192
- handlerExecuted = true
193
- const payload = c.get('jwtPayload')
194
- return c.json(payload)
195
- })
196
- app.get('/auth-with-jwks_uri/*', (c) => {
197
- handlerExecuted = true
198
- const payload = c.get('jwtPayload')
199
- return c.json(payload)
200
- })
201
- app.get('/auth-with-keys-and-jwks_uri/*', (c) => {
202
- handlerExecuted = true
203
- const payload = c.get('jwtPayload')
204
- return c.json(payload)
205
- })
206
- app.get('/auth-with-missing-jwks_uri/*', (c) => {
207
- handlerExecuted = true
208
- const payload = c.get('jwtPayload')
209
- return c.json(payload)
210
- })
211
- app.get('/auth-with-404-jwks_uri/*', (c) => {
212
- handlerExecuted = true
213
- const payload = c.get('jwtPayload')
214
- return c.json(payload)
215
- })
216
- app.get('/auth-with-bad-jwks_uri/*', (c) => {
217
- handlerExecuted = true
218
- const payload = c.get('jwtPayload')
219
- return c.json(payload)
220
- })
221
-
222
- it('Should throw an error if the middleware is missing both keys and jwks_uri (empty)', async () => {
223
- // @ts-expect-error - Testing runtime error with missing required alg option
224
- expect(() => app.use('/auth-with-empty-middleware/*', jwk({}))).toThrow(
225
- 'JWK auth middleware requires options for either "keys" or "jwks_uri"'
226
- )
227
- })
228
-
229
- it('Should throw an error when crypto.subtle is missing', async () => {
230
- const subtleSpy = vi.spyOn(global.crypto, 'subtle', 'get').mockReturnValue({
231
- importKey: undefined,
232
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
233
- } as any)
234
- expect(() =>
235
- app.use('/auth-with-bad-env/*', jwk({ keys: verify_keys, alg: ['RS256'] }))
236
- ).toThrow()
237
- subtleSpy.mockRestore()
238
- })
239
-
240
- it('Should return a server error if options.jwks_uri returns a 404', async () => {
241
- const credential = await Jwt.sign({ message: 'hello world' }, test_keys.private_keys[0])
242
- const req = new Request('http://localhost/auth-with-404-jwks_uri/a')
243
- req.headers.set('Authorization', `Bearer ${credential}`)
244
- const res = await app.request(req)
245
- expect(res).not.toBeNull()
246
- expect(res.status).toBe(500)
247
- })
248
-
249
- it('Should return a server error if the remotely fetched keys from options.jwks_uri are missing', async () => {
250
- const credential = await Jwt.sign({ message: 'hello world' }, test_keys.private_keys[0])
251
- const req = new Request('http://localhost/auth-with-missing-jwks_uri/a')
252
- req.headers.set('Authorization', `Bearer ${credential}`)
253
- const res = await app.request(req)
254
- expect(res).not.toBeNull()
255
- expect(res.status).toBe(500)
256
- })
257
-
258
- it('Should return a server error if the remotely fetched keys from options.jwks_uri are malformed', async () => {
259
- const credential = await Jwt.sign({ message: 'hello world' }, test_keys.private_keys[0])
260
- const req = new Request('http://localhost/auth-with-bad-jwks_uri/a')
261
- req.headers.set('Authorization', `Bearer ${credential}`)
262
- const res = await app.request(req)
263
- expect(res).not.toBeNull()
264
- expect(res.status).toBe(500)
265
- })
266
-
267
- it('Should not authorize requests with missing access token', async () => {
268
- const req = new Request('http://localhost/auth-with-keys/a')
269
- const res = await app.request(req)
270
- expect(res).not.toBeNull()
271
- expect(res.status).toBe(401)
272
- expect(await res.text()).toBe('Unauthorized')
273
- expect(handlerExecuted).toBeFalsy()
274
- })
275
-
276
- it('Should authorize from a static array passed to options.keys (key 1)', async () => {
277
- const credential = await Jwt.sign({ message: 'hello world' }, test_keys.private_keys[0])
278
- const req = new Request('http://localhost/auth-with-keys/a')
279
- req.headers.set('Authorization', `Bearer ${credential}`)
280
- const res = await app.request(req)
281
- expect(res).not.toBeNull()
282
- expect(res.status).toBe(200)
283
- expect(await res.json()).toEqual({ message: 'hello world' })
284
- expect(handlerExecuted).toBeTruthy()
285
- })
286
-
287
- it('Should authorize from a static array passed to options.keys (key 2)', async () => {
288
- const credential = await Jwt.sign({ message: 'hello world' }, test_keys.private_keys[1])
289
- const req = new Request('http://localhost/auth-with-keys/a')
290
- req.headers.set('Authorization', `Bearer ${credential}`)
291
- const res = await app.request(req)
292
- expect(res).not.toBeNull()
293
- expect(res.status).toBe(200)
294
- expect(await res.json()).toEqual({ message: 'hello world' })
295
- expect(handlerExecuted).toBeTruthy()
296
- expect(res.status).toBe(200)
297
- })
298
-
299
- it('Should not authorize a token without header', async () => {
300
- const encodeJwtPart = (part: unknown): string =>
301
- encodeBase64Url(utf8Encoder.encode(JSON.stringify(part)).buffer).replace(/=/g, '')
302
- const encodeSignaturePart = (buf: ArrayBufferLike): string =>
303
- encodeBase64Url(buf).replace(/=/g, '')
304
- const jwtSignWithoutHeader = async (payload: JWTPayload, privateKey: AcrylJsonWebKey) => {
305
- const encodedPayload = encodeJwtPart(payload)
306
- const signaturePart = await signing(
307
- privateKey,
308
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
309
- privateKey.alg as any,
310
- utf8Encoder.encode(encodedPayload)
311
- )
312
- const signature = encodeSignaturePart(signaturePart)
313
- return `${encodedPayload}.${signature}`
314
- }
315
- const credential = await jwtSignWithoutHeader(
316
- { message: 'hello world' },
317
- test_keys.private_keys[1]
318
- )
319
- const req = new Request('http://localhost/auth-with-keys/a')
320
- req.headers.set('Authorization', `Bearer ${credential}`)
321
- const res = await app.request(req)
322
- expect(res).not.toBeNull()
323
- expect(res.status).toBe(401)
324
- })
325
-
326
- it('Should not authorize a token with missing "kid" in header', async () => {
327
- const encodeJwtPart = (part: unknown): string =>
328
- encodeBase64Url(utf8Encoder.encode(JSON.stringify(part)).buffer).replace(/=/g, '')
329
- const encodeSignaturePart = (buf: ArrayBufferLike): string =>
330
- encodeBase64Url(buf).replace(/=/g, '')
331
- const jwtSignWithoutKid = async (payload: JWTPayload, privateKey: AcrylJsonWebKey) => {
332
- const encodedPayload = encodeJwtPart(payload)
333
- const encodedHeader = encodeJwtPart({ alg: privateKey.alg, typ: 'JWT' })
334
- const partialToken = `${encodedHeader}.${encodedPayload}`
335
- const signaturePart = await signing(
336
- privateKey,
337
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
338
- privateKey.alg as any,
339
- utf8Encoder.encode(partialToken)
340
- )
341
- const signature = encodeSignaturePart(signaturePart)
342
- return `${partialToken}.${signature}`
343
- }
344
- const credential = await jwtSignWithoutKid(
345
- { message: 'hello world' },
346
- test_keys.private_keys[1]
347
- )
348
- const req = new Request('http://localhost/auth-with-keys/a')
349
- req.headers.set('Authorization', `Bearer ${credential}`)
350
- const res = await app.request(req)
351
- expect(res).not.toBeNull()
352
- expect(res.status).toBe(401)
353
- })
354
-
355
- it('Should not authorize a token with invalid "kid" in header', async () => {
356
- const copy = structuredClone(test_keys.private_keys[1])
357
- copy.kid = 'invalid-kid'
358
- const credential = await Jwt.sign({ message: 'hello world' }, copy)
359
- const req = new Request('http://localhost/auth-with-keys/a')
360
- req.headers.set('Authorization', `Bearer ${credential}`)
361
- const res = await app.request(req)
362
- expect(res).not.toBeNull()
363
- expect(res.status).toBe(401)
364
- })
365
-
366
- it('Should authorize with Unicode payload from a static array passed to options.keys', async () => {
367
- const credential = await Jwt.sign({ message: 'hello world' }, test_keys.private_keys[0])
368
- const req = new Request('http://localhost/auth-with-keys-unicode/a')
369
- req.headers.set('Authorization', `bearer ${credential}`)
370
- const res = await app.request(req)
371
- expect(res).not.toBeNull()
372
- expect(res.status).toBe(200)
373
- expect(await res.json()).toEqual({ message: 'hello world' })
374
- expect(handlerExecuted).toBeTruthy()
375
- })
376
-
377
- it('Should authorize with mixed-case Bearer scheme', async () => {
378
- const credential = await Jwt.sign({ message: 'hello world' }, test_keys.private_keys[0])
379
- const req = new Request('http://localhost/auth-with-keys-unicode/a')
380
- req.headers.set('Authorization', `bEaReR ${credential}`)
381
- const res = await app.request(req)
382
- expect(res).not.toBeNull()
383
- expect(res.status).toBe(200)
384
- expect(await res.json()).toEqual({ message: 'hello world' })
385
- expect(handlerExecuted).toBeTruthy()
386
- })
387
-
388
- it('Should authorize from a function passed to options.keys', async () => {
389
- const credential = await Jwt.sign({ message: 'hello world' }, test_keys.private_keys[0])
390
- const req = new Request('http://localhost/auth-with-keys-fn/a')
391
- req.headers.set('Authorization', `Bearer ${credential}`)
392
- const res = await app.request(req)
393
- expect(res).not.toBeNull()
394
- expect(res.status).toBe(200)
395
- expect(await res.json()).toEqual({ message: 'hello world' })
396
- expect(handlerExecuted).toBeTruthy()
397
- })
398
-
399
- it('Should authorize from keys remotely fetched from options.jwks_uri', async () => {
400
- const credential = await Jwt.sign({ message: 'hello world' }, test_keys.private_keys[0])
401
- const req = new Request('http://localhost/auth-with-jwks_uri/a')
402
- req.headers.set('Authorization', `Bearer ${credential}`)
403
- const res = await app.request(req)
404
- expect(res).not.toBeNull()
405
- expect(res.status).toBe(200)
406
- expect(await res.json()).toEqual({ message: 'hello world' })
407
- expect(handlerExecuted).toBeTruthy()
408
- })
409
-
410
- it('Should authorize from keys and hard-coded and remotely fetched from options.jwks_uri', async () => {
411
- const credential = await Jwt.sign({ message: 'hello world' }, test_keys.private_keys[0])
412
- const req = new Request('http://localhost/auth-with-keys-and-jwks_uri/a')
413
- req.headers.set('Authorization', `Bearer ${credential}`)
414
- const res = await app.request(req)
415
- expect(res).not.toBeNull()
416
- expect(res.status).toBe(200)
417
- expect(await res.json()).toEqual({ message: 'hello world' })
418
- expect(handlerExecuted).toBeTruthy()
419
- })
420
-
421
- it('Should not authorize requests with non-Bearer scheme in header', async () => {
422
- const credential = await Jwt.sign({ message: 'hello world' }, test_keys.private_keys[0])
423
- const url = 'http://localhost/auth-with-keys-unicode/a'
424
- const req = new Request(url)
425
- req.headers.set('Authorization', `Basic ${credential}`)
426
- const res = await app.request(req)
427
- expect(res).not.toBeNull()
428
- expect(res.status).toBe(401)
429
- expect(res.headers.get('www-authenticate')).toEqual(
430
- `Bearer realm="${url}",error="invalid_request",error_description="invalid credentials structure"`
431
- )
432
- expect(handlerExecuted).toBeFalsy()
433
- })
434
-
435
- it('Should not authorize requests with invalid token in Bearer header', async () => {
436
- const invalidToken =
437
- 'ssyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXNzYWdlIjoiaGVsbG8gd29ybGQifQ.B54pAqIiLbu170tGQ1rY06Twv__0qSHTA0ioQPIOvFE'
438
-
439
- const url = 'http://localhost/auth-with-keys-unicode/a'
440
- const req = new Request(url)
441
- req.headers.set('Authorization', `Bearer ${invalidToken}`)
442
- const res = await app.request(req)
443
- expect(res).not.toBeNull()
444
- expect(res.status).toBe(401)
445
- expect(res.headers.get('www-authenticate')).toEqual(
446
- `Bearer realm="${url}",error="invalid_token",error_description="token verification failure"`
447
- )
448
- expect(handlerExecuted).toBeFalsy()
449
- })
450
-
451
- it('Should not authorize requests with malformed token structure in header', async () => {
452
- const invalid_token = 'invalid token'
453
- const url = 'http://localhost/auth-with-keys/a'
454
- const req = new Request(url)
455
- req.headers.set('Authorization', `Bearer ${invalid_token}`)
456
- const res = await app.request(req)
457
- expect(res).not.toBeNull()
458
- expect(res.status).toBe(401)
459
- expect(res.headers.get('www-authenticate')).toEqual(
460
- `Bearer realm="${url}",error="invalid_request",error_description="invalid credentials structure"`
461
- )
462
- expect(handlerExecuted).toBeFalsy()
463
- })
464
-
465
- it('Should not authorize requests without authorization in nested JWK middleware', async () => {
466
- const req = new Request('http://localhost/auth-with-keys-nested/a')
467
- const res = await app.request(req)
468
- expect(res).not.toBeNull()
469
- expect(res.status).toBe(401)
470
- expect(await res.text()).toBe('Unauthorized')
471
- expect(handlerExecuted).toBeFalsy()
472
- })
473
-
474
- it('Should authorize requests with authorization in nested JWK middleware', async () => {
475
- const credential = await Jwt.sign({ message: 'hello world' }, test_keys.private_keys[0])
476
- const req = new Request('http://localhost/auth-with-keys-nested/a')
477
- req.headers.set('Authorization', `Bearer ${credential}`)
478
- const res = await app.request(req)
479
- expect(res).not.toBeNull()
480
- expect(res.status).toBe(200)
481
- expect(await res.json()).toEqual({ message: 'hello world' })
482
- expect(handlerExecuted).toBeTruthy()
483
- })
484
- })
485
-
486
- describe('Credentials in custom header', () => {
487
- let handlerExecuted: boolean
488
-
489
- beforeEach(() => {
490
- handlerExecuted = false
491
- })
492
-
493
- const app = new Acryl()
494
-
495
- app.use(
496
- '/auth-with-keys/*',
497
- jwk({ keys: verify_keys, headerName: 'x-custom-auth-header', alg: ['RS256'] })
498
- )
499
-
500
- app.get('/auth-with-keys/*', (c) => {
501
- handlerExecuted = true
502
- const payload = c.get('jwtPayload')
503
- return c.json(payload)
504
- })
505
-
506
- it('Should not authorize', async () => {
507
- const req = new Request('http://localhost/auth-with-keys/a')
508
- const res = await app.request(req)
509
- expect(res).not.toBeNull()
510
- expect(res.status).toBe(401)
511
- expect(await res.text()).toBe('Unauthorized')
512
- expect(handlerExecuted).toBeFalsy()
513
- })
514
-
515
- it('Should not authorize even if default authorization header present', async () => {
516
- const credential = await Jwt.sign({ message: 'hello world' }, test_keys.private_keys[0])
517
-
518
- const req = new Request('http://localhost/auth-with-keys/a')
519
- req.headers.set('Authorization', `Bearer ${credential}`)
520
- const res = await app.request(req)
521
- expect(res).not.toBeNull()
522
- expect(res.status).toBe(401)
523
- expect(await res.text()).toBe('Unauthorized')
524
- expect(handlerExecuted).toBeFalsy()
525
- })
526
-
527
- it('Should authorize', async () => {
528
- const credential = await Jwt.sign({ message: 'hello world' }, test_keys.private_keys[1])
529
-
530
- const req = new Request('http://localhost/auth-with-keys/a')
531
- req.headers.set('x-custom-auth-header', `Bearer ${credential}`)
532
- const res = await app.request(req)
533
- expect(res).not.toBeNull()
534
- expect(res.status).toBe(200)
535
- expect(await res.json()).toEqual({ message: 'hello world' })
536
- expect(handlerExecuted).toBeTruthy()
537
- })
538
-
539
- it('Should authorize with mixed-case Bearer scheme in custom header', async () => {
540
- const credential = await Jwt.sign({ message: 'hello world' }, test_keys.private_keys[1])
541
-
542
- const req = new Request('http://localhost/auth-with-keys/a')
543
- req.headers.set('x-custom-auth-header', `bEaReR ${credential}`)
544
- const res = await app.request(req)
545
- expect(res).not.toBeNull()
546
- expect(res.status).toBe(200)
547
- expect(await res.json()).toEqual({ message: 'hello world' })
548
- expect(handlerExecuted).toBeTruthy()
549
- })
550
-
551
- it('Should not authorize with non-Bearer scheme in custom header', async () => {
552
- const credential = await Jwt.sign({ message: 'hello world' }, test_keys.private_keys[1])
553
- const url = 'http://localhost/auth-with-keys/a'
554
-
555
- const req = new Request(url)
556
- req.headers.set('x-custom-auth-header', `Basic ${credential}`)
557
- const res = await app.request(req)
558
- expect(res).not.toBeNull()
559
- expect(res.status).toBe(401)
560
- expect(res.headers.get('www-authenticate')).toEqual(
561
- `Bearer realm="${url}",error="invalid_request",error_description="invalid credentials structure"`
562
- )
563
- expect(handlerExecuted).toBeFalsy()
564
- })
565
-
566
- it('Should not authorize invalid token with Bearer scheme in custom header', async () => {
567
- const invalidToken =
568
- 'ssyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXNzYWdlIjoiaGVsbG8gd29ybGQifQ.B54pAqIiLbu170tGQ1rY06Twv__0qSHTA0ioQPIOvFE'
569
- const url = 'http://localhost/auth-with-keys/a'
570
-
571
- const req = new Request(url)
572
- req.headers.set('x-custom-auth-header', `Bearer ${invalidToken}`)
573
- const res = await app.request(req)
574
- expect(res).not.toBeNull()
575
- expect(res.status).toBe(401)
576
- expect(res.headers.get('www-authenticate')).toEqual(
577
- `Bearer realm="${url}",error="invalid_token",error_description="token verification failure"`
578
- )
579
- expect(handlerExecuted).toBeFalsy()
580
- })
581
- })
582
-
583
- describe('Credentials in cookie', () => {
584
- let handlerExecuted: boolean
585
-
586
- beforeEach(() => {
587
- handlerExecuted = false
588
- })
589
-
590
- const app = new Acryl()
591
-
592
- app.use('/auth-with-keys/*', jwk({ keys: verify_keys, cookie: 'access_token', alg: ['RS256'] }))
593
- app.use(
594
- '/auth-with-keys-unicode/*',
595
- jwk({ keys: verify_keys, cookie: 'access_token', alg: ['RS256'] })
596
- )
597
- app.use(
598
- '/auth-with-keys-prefixed/*',
599
- jwk({
600
- keys: verify_keys,
601
- cookie: { key: 'access_token', prefixOptions: 'host' },
602
- alg: ['RS256'],
603
- })
604
- )
605
- app.use(
606
- '/auth-with-keys-unprefixed/*',
607
- jwk({ keys: verify_keys, cookie: { key: 'access_token' }, alg: ['RS256'] })
608
- )
609
-
610
- app.get('/auth-with-keys/*', (c) => {
611
- handlerExecuted = true
612
- const payload = c.get('jwtPayload')
613
- return c.json(payload)
614
- })
615
- app.get('/auth-with-keys-prefixed/*', (c) => {
616
- handlerExecuted = true
617
- const payload = c.get('jwtPayload')
618
- return c.json(payload)
619
- })
620
- app.get('/auth-with-keys-unprefixed/*', (c) => {
621
- handlerExecuted = true
622
- const payload = c.get('jwtPayload')
623
- return c.json(payload)
624
- })
625
- app.get('/auth-with-keys-unicode/*', (c) => {
626
- handlerExecuted = true
627
- const payload = c.get('jwtPayload')
628
- return c.json(payload)
629
- })
630
-
631
- it('Should not authorize requests with missing access token', async () => {
632
- const req = new Request('http://localhost/auth-with-keys/a')
633
- const res = await app.request(req)
634
- expect(res).not.toBeNull()
635
- expect(res.status).toBe(401)
636
- expect(await res.text()).toBe('Unauthorized')
637
- expect(handlerExecuted).toBeFalsy()
638
- })
639
-
640
- it('Should authorize cookie from a static array passed to options.keys', async () => {
641
- const url = 'http://localhost/auth-with-keys/a'
642
- const credential = await Jwt.sign({ message: 'hello world' }, test_keys.private_keys[0])
643
- const req = new Request(url, {
644
- headers: new Headers({
645
- Cookie: `access_token=${credential}`,
646
- }),
647
- })
648
- const res = await app.request(req)
649
- expect(res).not.toBeNull()
650
- expect(await res.json()).toEqual({ message: 'hello world' })
651
- expect(res.status).toBe(200)
652
- expect(handlerExecuted).toBeTruthy()
653
- })
654
-
655
- it('Should authorize prefixed cookie from a static array passed to options.keys', async () => {
656
- const url = 'http://localhost/auth-with-keys-prefixed/a'
657
- const credential = await Jwt.sign({ message: 'hello world' }, test_keys.private_keys[0])
658
- const req = new Request(url, {
659
- headers: new Headers({
660
- Cookie: `__Host-access_token=${credential}`,
661
- }),
662
- })
663
- const res = await app.request(req)
664
- expect(res).not.toBeNull()
665
- expect(await res.json()).toEqual({ message: 'hello world' })
666
- expect(res.status).toBe(200)
667
- expect(handlerExecuted).toBeTruthy()
668
- })
669
-
670
- it('Should authorize unprefixed cookie from a static array passed to options.keys', async () => {
671
- const url = 'http://localhost/auth-with-keys-unprefixed/a'
672
- const credential = await Jwt.sign({ message: 'hello world' }, test_keys.private_keys[0])
673
- const req = new Request(url, {
674
- headers: new Headers({
675
- Cookie: `access_token=${credential}`,
676
- }),
677
- })
678
- const res = await app.request(req)
679
- expect(res).not.toBeNull()
680
- expect(await res.json()).toEqual({ message: 'hello world' })
681
- expect(res.status).toBe(200)
682
- expect(handlerExecuted).toBeTruthy()
683
- })
684
-
685
- it('Should authorize with Unicode payload from a static array passed to options.keys', async () => {
686
- const credential = await Jwt.sign({ message: 'hello world' }, test_keys.private_keys[0])
687
- const req = new Request('http://localhost/auth-with-keys-unicode/a', {
688
- headers: new Headers({
689
- Cookie: `access_token=${credential}`,
690
- }),
691
- })
692
- const res = await app.request(req)
693
- expect(res).not.toBeNull()
694
- expect(res.status).toBe(200)
695
- expect(await res.json()).toEqual({ message: 'hello world' })
696
- expect(handlerExecuted).toBeTruthy()
697
- })
698
-
699
- it('Should not authorize requests with invalid Unicode payload in cookie', async () => {
700
- const invalidToken =
701
- 'ssyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXNzYWdlIjoiaGVsbG8gd29ybGQifQ.B54pAqIiLbu170tGQ1rY06Twv__0qSHTA0ioQPIOvFE'
702
-
703
- const url = 'http://localhost/auth-with-keys-unicode/a'
704
- const req = new Request(url)
705
- req.headers.set('Cookie', `access_token=${invalidToken}`)
706
- const res = await app.request(req)
707
- expect(res).not.toBeNull()
708
- expect(res.status).toBe(401)
709
- expect(res.headers.get('www-authenticate')).toEqual(
710
- `Bearer realm="${url}",error="invalid_token",error_description="token verification failure"`
711
- )
712
- expect(handlerExecuted).toBeFalsy()
713
- })
714
-
715
- it('Should not authorize requests with malformed token structure in cookie', async () => {
716
- const invalidToken = 'invalid token'
717
- const url = 'http://localhost/auth-with-keys/a'
718
- const req = new Request(url)
719
- req.headers.set('Cookie', `access_token=${invalidToken}`)
720
- const res = await app.request(req)
721
- expect(res).not.toBeNull()
722
- expect(res.status).toBe(401)
723
- expect(res.headers.get('www-authenticate')).toEqual(
724
- `Bearer realm="${url}",error="invalid_token",error_description="token verification failure"`
725
- )
726
- expect(handlerExecuted).toBeFalsy()
727
- })
728
- })
729
-
730
- describe('Credentials in a signed cookie', () => {
731
- let handlerExecuted: boolean
732
-
733
- beforeEach(() => {
734
- handlerExecuted = false
735
- })
736
-
737
- const app = new Acryl()
738
- const test_secret = 'Shhh'
739
-
740
- app.use(
741
- '/auth-with-signed-cookie/*',
742
- jwk({
743
- keys: verify_keys,
744
- cookie: { key: 'access_token', secret: test_secret },
745
- alg: ['RS256'],
746
- })
747
- )
748
- app.use(
749
- '/auth-with-signed-with-prefix-options-cookie/*',
750
- jwk({
751
- keys: verify_keys,
752
- cookie: { key: 'access_token', secret: test_secret, prefixOptions: 'host' },
753
- alg: ['RS256'],
754
- })
755
- )
756
-
757
- app.get('/sign-cookie', async (c) => {
758
- const credential = await Jwt.sign(
759
- { message: 'signed hello world' },
760
- test_keys.private_keys[0]
761
- )
762
- await setSignedCookie(c, 'access_token', credential, test_secret)
763
- return c.text('OK')
764
- })
765
- app.get('/sign-cookie-with-prefix', async (c) => {
766
- const credential = await Jwt.sign(
767
- { message: 'signed hello world' },
768
- test_keys.private_keys[0]
769
- )
770
- await setSignedCookie(c, 'access_token', credential, test_secret, { prefix: 'host' })
771
- return c.text('OK')
772
- })
773
- app.get('/auth-with-signed-cookie/*', async (c) => {
774
- handlerExecuted = true
775
- const payload = c.get('jwtPayload')
776
- return c.json(payload)
777
- })
778
- app.get('/auth-with-signed-with-prefix-options-cookie/*', async (c) => {
779
- handlerExecuted = true
780
- const payload = c.get('jwtPayload')
781
- return c.json(payload)
782
- })
783
-
784
- it('Should authorize signed cookie', async () => {
785
- const url = 'http://localhost/auth-with-signed-cookie/a'
786
- const sign_res = await app.request('http://localhost/sign-cookie')
787
- const cookieHeader = sign_res.headers.get('Set-Cookie') as string
788
- expect(cookieHeader).not.toBeNull()
789
- const req = new Request(url)
790
- req.headers.set('Cookie', cookieHeader)
791
- const res = await app.request(req)
792
- expect(res).not.toBeNull()
793
- expect(res.status).toBe(200)
794
- expect(await res.json()).toEqual({ message: 'signed hello world' })
795
- expect(handlerExecuted).toBeTruthy()
796
- })
797
-
798
- it('Should authorize prefixed signed cookie', async () => {
799
- const url = 'http://localhost/auth-with-signed-with-prefix-options-cookie/a'
800
- const sign_res = await app.request('http://localhost/sign-cookie-with-prefix')
801
- const cookieHeader = sign_res.headers.get('Set-Cookie') as string
802
- expect(cookieHeader).not.toBeNull()
803
- const req = new Request(url)
804
- req.headers.set('Cookie', cookieHeader)
805
- const res = await app.request(req)
806
- expect(res).not.toBeNull()
807
- expect(res.status).toBe(200)
808
- expect(await res.json()).toEqual({ message: 'signed hello world' })
809
- expect(handlerExecuted).toBeTruthy()
810
- })
811
-
812
- it('Should not authorize an unsigned cookie', async () => {
813
- const url = 'http://localhost/auth-with-signed-cookie/a'
814
- const credential = await Jwt.sign(
815
- { message: 'unsigned hello world' },
816
- test_keys.private_keys[0]
817
- )
818
- const unsignedCookie = `access_token=${credential}`
819
- const req = new Request(url)
820
- req.headers.set('Cookie', unsignedCookie)
821
- const res = await app.request(req)
822
- expect(res.status).toBe(401)
823
- expect(await res.text()).toBe('Unauthorized')
824
- expect(handlerExecuted).toBeFalsy()
825
- })
826
- })
827
-
828
- describe('Error handling with `cause`', () => {
829
- const app = new Acryl()
830
-
831
- app.use('/auth-with-keys/*', jwk({ keys: verify_keys, alg: ['RS256'] }))
832
- app.get('/auth-with-keys/*', (c) => c.text('Authorized'))
833
-
834
- app.onError((e, c) => {
835
- if (e instanceof HTTPException && e.cause instanceof Error) {
836
- return c.json({ name: e.cause.name, message: e.cause.message }, 401)
837
- }
838
- return c.text(e.message, 401)
839
- })
840
-
841
- it('Should not authorize', async () => {
842
- const credential = 'abc.def.ghi'
843
- const req = new Request('http://localhost/auth-with-keys')
844
- req.headers.set('Authorization', `Bearer ${credential}`)
845
- const res = await app.request(req)
846
- expect(res.status).toBe(401)
847
- expect(await res.json()).toEqual({
848
- name: 'JwtTokenInvalid',
849
- message: `invalid JWT token: ${credential}`,
850
- })
851
- })
852
- })
853
-
854
- describe('Verification of token attributes', () => {
855
- let handlerExecuted: boolean
856
-
857
- beforeEach(() => {
858
- handlerExecuted = false
859
- })
860
-
861
- function inFuture() {
862
- return Date.now() / 1000 + 100
863
- }
864
-
865
- function inPast() {
866
- return Date.now() / 1000 - 100
867
- }
868
-
869
- const app = new Acryl()
870
-
871
- app.use('/auth-with-keys-default/*', jwk({ keys: verify_keys, alg: ['RS256'] }))
872
- app.use(
873
- '/auth-with-keys-and-issuer/*',
874
- jwk({ keys: verify_keys, verification: { iss: 'http://issuer.test' }, alg: ['RS256'] })
875
- )
876
-
877
- app.get('/auth-with-keys-default/*', (c) => {
878
- handlerExecuted = true
879
- const payload = c.get('jwtPayload')
880
- return c.json(payload)
881
- })
882
- app.get('/auth-with-keys-and-issuer/*', (c) => {
883
- handlerExecuted = true
884
- const payload = c.get('jwtPayload')
885
- return c.json(payload)
886
- })
887
-
888
- it('Should validate exp/nbf/iat and pass when good by default', async () => {
889
- const payload = {
890
- exp: inFuture(),
891
- nbf: inPast(),
892
- iat: inPast(),
893
- iss: 'http://not-checked.test',
894
- }
895
- const credential = await Jwt.sign(payload, test_keys.private_keys[0])
896
- const req = new Request('http://localhost/auth-with-keys-default/a')
897
- req.headers.set('Authorization', `Bearer ${credential}`)
898
- const res = await app.request(req)
899
- expect(res).not.toBeNull()
900
- expect(res.status).toBe(200)
901
- expect(await res.json()).toEqual(payload)
902
- expect(handlerExecuted).toBeTruthy()
903
- })
904
-
905
- it('Should validate exp and fail when bad', async () => {
906
- const payload = {
907
- exp: inPast(),
908
- }
909
- const credential = await Jwt.sign(payload, test_keys.private_keys[0])
910
- const req = new Request('http://localhost/auth-with-keys-default/a')
911
- req.headers.set('Authorization', `Bearer ${credential}`)
912
- const res = await app.request(req)
913
- expect(res).not.toBeNull()
914
- expect(res.status).toBe(401)
915
- expect(handlerExecuted).toBeFalsy()
916
- })
917
-
918
- it('Should validate nbf and fail when bad', async () => {
919
- const payload = {
920
- nbf: inFuture(),
921
- }
922
- const credential = await Jwt.sign(payload, test_keys.private_keys[0])
923
- const req = new Request('http://localhost/auth-with-keys-default/a')
924
- req.headers.set('Authorization', `Bearer ${credential}`)
925
- const res = await app.request(req)
926
- expect(res).not.toBeNull()
927
- expect(res.status).toBe(401)
928
- expect(handlerExecuted).toBeFalsy()
929
- })
930
-
931
- it('Should validate iat and fail when bad', async () => {
932
- const payload = {
933
- iat: inFuture(),
934
- }
935
- const credential = await Jwt.sign(payload, test_keys.private_keys[0])
936
- const req = new Request('http://localhost/auth-with-keys-default/a')
937
- req.headers.set('Authorization', `Bearer ${credential}`)
938
- const res = await app.request(req)
939
- expect(res).not.toBeNull()
940
- expect(res.status).toBe(401)
941
- expect(handlerExecuted).toBeFalsy()
942
- })
943
-
944
- it('Should validate iss when supplied', async () => {
945
- const payload = {
946
- iss: 'http://issuer.test',
947
- }
948
- const credential = await Jwt.sign(payload, test_keys.private_keys[0])
949
- const req = new Request('http://localhost/auth-with-keys-and-issuer/a')
950
- req.headers.set('Authorization', `Bearer ${credential}`)
951
- const res = await app.request(req)
952
- expect(res).not.toBeNull()
953
- expect(res.status).toBe(200)
954
- expect(await res.json()).toEqual(payload)
955
- expect(handlerExecuted).toBeTruthy()
956
- })
957
-
958
- it('Should reject missing iss when required', async () => {
959
- const payload = {
960
- // Nothing
961
- }
962
- const credential = await Jwt.sign(payload, test_keys.private_keys[0])
963
- const req = new Request('http://localhost/auth-with-keys-and-issuer/a')
964
- req.headers.set('Authorization', `Bearer ${credential}`)
965
- const res = await app.request(req)
966
- expect(res).not.toBeNull()
967
- expect(res.status).toBe(401)
968
- expect(handlerExecuted).toBeFalsy()
969
- })
970
-
971
- it('Should reject iss when different', async () => {
972
- const payload = {
973
- iss: 'http://bad-issuer.test',
974
- }
975
- const credential = await Jwt.sign(payload, test_keys.private_keys[0])
976
- const req = new Request('http://localhost/auth-with-keys-and-issuer/a')
977
- req.headers.set('Authorization', `Bearer ${credential}`)
978
- const res = await app.request(req)
979
- expect(res).not.toBeNull()
980
- expect(res.status).toBe(401)
981
- expect(handlerExecuted).toBeFalsy()
982
- })
983
- })
984
-
985
- describe('Algorithm whitelist (options.alg)', () => {
986
- let handlerExecuted: boolean
987
-
988
- beforeEach(() => {
989
- handlerExecuted = false
990
- })
991
-
992
- const app = new Acryl()
993
-
994
- // Only allow RS256
995
- app.use('/auth-whitelist-rs256/*', jwk({ keys: verify_keys, alg: ['RS256'] }))
996
- app.get('/auth-whitelist-rs256/*', (c) => {
997
- handlerExecuted = true
998
- return c.json(c.get('jwtPayload'))
999
- })
1000
-
1001
- // Allow multiple algorithms
1002
- app.use('/auth-whitelist-multi/*', jwk({ keys: verify_keys, alg: ['RS256', 'ES256'] }))
1003
- app.get('/auth-whitelist-multi/*', (c) => {
1004
- handlerExecuted = true
1005
- return c.json(c.get('jwtPayload'))
1006
- })
1007
-
1008
- // Note: Test for "no whitelist" was removed because alg is now required.
1009
- // This is a breaking change that enforces explicit algorithm specification for security.
1010
-
1011
- it('Should authorize RS256 token when RS256 is in whitelist', async () => {
1012
- const payload = { message: 'hello world' }
1013
- const credential = await Jwt.sign(payload, test_keys.private_keys[0]) // RS256 key
1014
- const req = new Request('http://localhost/auth-whitelist-rs256/a')
1015
- req.headers.set('Authorization', `Bearer ${credential}`)
1016
- const res = await app.request(req)
1017
- expect(res).not.toBeNull()
1018
- expect(res.status).toBe(200)
1019
- expect(await res.json()).toEqual(payload)
1020
- expect(handlerExecuted).toBeTruthy()
1021
- })
1022
-
1023
- it('Should reject token when algorithm is not in whitelist', async () => {
1024
- // Create a token with ES256 algorithm manually
1025
- const kid = 'acryl-test-kid-1' // Use existing kid but header will have different alg
1026
- const payload = { message: 'hello world' }
1027
-
1028
- // Generate ES256 key pair for signing
1029
- const keyPair = await crypto.subtle.generateKey(
1030
- {
1031
- name: 'ECDSA',
1032
- namedCurve: 'P-256',
1033
- },
1034
- true,
1035
- ['sign', 'verify']
1036
- )
1037
-
1038
- // Create JWT with ES256
1039
- const header = { alg: 'ES256', typ: 'JWT', kid }
1040
- const encode = (obj: object) =>
1041
- encodeBase64Url(utf8Encoder.encode(JSON.stringify(obj)).buffer)
1042
- const encodedHeader = encode(header)
1043
- const encodedPayload = encode(payload)
1044
- const signingInput = `${encodedHeader}.${encodedPayload}`
1045
-
1046
- const signatureBuffer = await signing(
1047
- keyPair.privateKey,
1048
- 'ES256',
1049
- utf8Encoder.encode(signingInput)
1050
- )
1051
- const signature = encodeBase64Url(signatureBuffer)
1052
-
1053
- const token = `${encodedHeader}.${encodedPayload}.${signature}`
1054
-
1055
- const url = 'http://localhost/auth-whitelist-rs256/a'
1056
- const req = new Request(url)
1057
- req.headers.set('Authorization', `Bearer ${token}`)
1058
- const res = await app.request(req)
1059
- expect(res).not.toBeNull()
1060
- expect(res.status).toBe(401)
1061
- expect(res.headers.get('www-authenticate')).toMatch(/token verification failure/)
1062
- expect(handlerExecuted).toBeFalsy()
1063
- })
1064
-
1065
- it('Should authorize RS256 token when multiple algorithms are in whitelist', async () => {
1066
- const payload = { message: 'hello world' }
1067
- const credential = await Jwt.sign(payload, test_keys.private_keys[0]) // RS256 key
1068
- const req = new Request('http://localhost/auth-whitelist-multi/a')
1069
- req.headers.set('Authorization', `Bearer ${credential}`)
1070
- const res = await app.request(req)
1071
- expect(res).not.toBeNull()
1072
- expect(res.status).toBe(200)
1073
- expect(await res.json()).toEqual(payload)
1074
- expect(handlerExecuted).toBeTruthy()
1075
- })
1076
-
1077
- // Note: Test for "no whitelist" was removed because alg is now required.
1078
- // This is a breaking change that enforces explicit algorithm specification for security.
1079
- })
1080
- describe('WWW-Authenticate realm', () => {
1081
- it('Should default to the request URL', async () => {
1082
- const app = new Acryl()
1083
- app.use('/auth/*', jwk({ keys: verify_keys, alg: ['RS256'] }))
1084
- app.get('/auth/*', (c) => c.text('ok'))
1085
-
1086
- const res = await app.request('http://localhost/auth/page?a=1')
1087
-
1088
- expect(res.status).toBe(401)
1089
- expect(res.headers.get('WWW-Authenticate')).toBe(
1090
- 'Bearer realm="http://localhost/auth/page?a=1",error="invalid_request",error_description="no authorization included in request"'
1091
- )
1092
- })
1093
-
1094
- it('Should use the configured realm', async () => {
1095
- const app = new Acryl()
1096
- app.use('/auth/*', jwk({ keys: verify_keys, alg: ['RS256'], realm: 'my-api' }))
1097
- app.get('/auth/*', (c) => c.text('ok'))
1098
-
1099
- const res = await app.request('http://localhost/auth/page?a=1')
1100
-
1101
- expect(res.status).toBe(401)
1102
- expect(res.headers.get('WWW-Authenticate')).toBe(
1103
- 'Bearer realm="my-api",error="invalid_request",error_description="no authorization included in request"'
1104
- )
1105
- })
1106
-
1107
- it('Should use the configured realm for an invalid token', async () => {
1108
- const app = new Acryl()
1109
- app.use('/auth/*', jwk({ keys: verify_keys, alg: ['RS256'], realm: 'my-api' }))
1110
- app.get('/auth/*', (c) => c.text('ok'))
1111
-
1112
- const req = new Request('http://localhost/auth/page')
1113
- req.headers.set('Authorization', 'Bearer invalid-token')
1114
- const res = await app.request(req)
1115
-
1116
- expect(res.status).toBe(401)
1117
- expect(res.headers.get('WWW-Authenticate')).toBe(
1118
- 'Bearer realm="my-api",error="invalid_token",error_description="token verification failure"'
1119
- )
1120
- })
1121
-
1122
- it('Should escape double quotes in the realm', async () => {
1123
- const app = new Acryl()
1124
- app.use('/auth/*', jwk({ keys: verify_keys, alg: ['RS256'], realm: 'my "quoted" api' }))
1125
- app.get('/auth/*', (c) => c.text('ok'))
1126
-
1127
- const res = await app.request('http://localhost/auth/page')
1128
-
1129
- expect(res.status).toBe(401)
1130
- expect(res.headers.get('WWW-Authenticate')).toBe(
1131
- 'Bearer realm="my \\"quoted\\" api",error="invalid_request",error_description="no authorization included in request"'
1132
- )
1133
- })
1134
- })
1135
- })