libre-webui 0.26.0 → 0.27.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 (249) hide show
  1. package/backend/dist/index.d.ts.map +1 -1
  2. package/backend/dist/index.js +7 -0
  3. package/backend/dist/index.js.map +1 -1
  4. package/backend/dist/persistence/postgresMfaPushRecoveryMigration.d.ts +9 -0
  5. package/backend/dist/persistence/postgresMfaPushRecoveryMigration.d.ts.map +1 -0
  6. package/backend/dist/persistence/postgresMfaPushRecoveryMigration.js +107 -0
  7. package/backend/dist/persistence/postgresMfaPushRecoveryMigration.js.map +1 -0
  8. package/backend/dist/persistence/postgresMigrationRegistry.d.ts.map +1 -1
  9. package/backend/dist/persistence/postgresMigrationRegistry.js +2 -0
  10. package/backend/dist/persistence/postgresMigrationRegistry.js.map +1 -1
  11. package/backend/dist/persistence/postgresResourceRepositories.d.ts.map +1 -1
  12. package/backend/dist/persistence/postgresResourceRepositories.js +162 -0
  13. package/backend/dist/persistence/postgresResourceRepositories.js.map +1 -1
  14. package/backend/dist/persistence/postgresSecurityRepositories.d.ts.map +1 -1
  15. package/backend/dist/persistence/postgresSecurityRepositories.js +150 -0
  16. package/backend/dist/persistence/postgresSecurityRepositories.js.map +1 -1
  17. package/backend/dist/persistence/resourceTypes.d.ts +59 -0
  18. package/backend/dist/persistence/resourceTypes.d.ts.map +1 -1
  19. package/backend/dist/persistence/resourceTypes.js.map +1 -1
  20. package/backend/dist/persistence/securityTypes.d.ts +59 -0
  21. package/backend/dist/persistence/securityTypes.d.ts.map +1 -1
  22. package/backend/dist/persistence/sqliteMigrations.d.ts +1 -0
  23. package/backend/dist/persistence/sqliteMigrations.d.ts.map +1 -1
  24. package/backend/dist/persistence/sqliteMigrations.js +194 -0
  25. package/backend/dist/persistence/sqliteMigrations.js.map +1 -1
  26. package/backend/dist/persistence/sqliteResourceRepositories.d.ts.map +1 -1
  27. package/backend/dist/persistence/sqliteResourceRepositories.js +134 -0
  28. package/backend/dist/persistence/sqliteResourceRepositories.js.map +1 -1
  29. package/backend/dist/persistence/sqliteSecurityRepositories.d.ts.map +1 -1
  30. package/backend/dist/persistence/sqliteSecurityRepositories.js +134 -0
  31. package/backend/dist/persistence/sqliteSecurityRepositories.js.map +1 -1
  32. package/backend/dist/persistence/sqliteToPostgresMigration.d.ts.map +1 -1
  33. package/backend/dist/persistence/sqliteToPostgresMigration.js +45 -0
  34. package/backend/dist/persistence/sqliteToPostgresMigration.js.map +1 -1
  35. package/backend/dist/platform/jobs/domainJobContracts.d.ts +2 -0
  36. package/backend/dist/platform/jobs/domainJobContracts.d.ts.map +1 -1
  37. package/backend/dist/platform/jobs/domainJobContracts.js +2 -0
  38. package/backend/dist/platform/jobs/domainJobContracts.js.map +1 -1
  39. package/backend/dist/platform/jobs/domainJobHandlers.d.ts.map +1 -1
  40. package/backend/dist/platform/jobs/domainJobHandlers.js +65 -2
  41. package/backend/dist/platform/jobs/domainJobHandlers.js.map +1 -1
  42. package/backend/dist/routes/auth.d.ts.map +1 -1
  43. package/backend/dist/routes/auth.js +419 -1
  44. package/backend/dist/routes/auth.js.map +1 -1
  45. package/backend/dist/routes/documents.d.ts.map +1 -1
  46. package/backend/dist/routes/documents.js +4 -0
  47. package/backend/dist/routes/documents.js.map +1 -1
  48. package/backend/dist/routes/push.d.ts +3 -0
  49. package/backend/dist/routes/push.d.ts.map +1 -0
  50. package/backend/dist/routes/push.js +105 -0
  51. package/backend/dist/routes/push.js.map +1 -0
  52. package/backend/dist/routes/recovery.d.ts +3 -0
  53. package/backend/dist/routes/recovery.d.ts.map +1 -0
  54. package/backend/dist/routes/recovery.js +83 -0
  55. package/backend/dist/routes/recovery.js.map +1 -0
  56. package/backend/dist/routes/users.d.ts.map +1 -1
  57. package/backend/dist/routes/users.js +35 -0
  58. package/backend/dist/routes/users.js.map +1 -1
  59. package/backend/dist/services/authService.d.ts +6 -0
  60. package/backend/dist/services/authService.d.ts.map +1 -1
  61. package/backend/dist/services/authService.js +9 -0
  62. package/backend/dist/services/authService.js.map +1 -1
  63. package/backend/dist/services/automationSchedulerService.d.ts.map +1 -1
  64. package/backend/dist/services/automationSchedulerService.js +7 -0
  65. package/backend/dist/services/automationSchedulerService.js.map +1 -1
  66. package/backend/dist/services/dataArchiveService.d.ts.map +1 -1
  67. package/backend/dist/services/dataArchiveService.js +3 -0
  68. package/backend/dist/services/dataArchiveService.js.map +1 -1
  69. package/backend/dist/services/documentMediaExtractionService.d.ts +40 -0
  70. package/backend/dist/services/documentMediaExtractionService.d.ts.map +1 -0
  71. package/backend/dist/services/documentMediaExtractionService.js +157 -0
  72. package/backend/dist/services/documentMediaExtractionService.js.map +1 -0
  73. package/backend/dist/services/documentService.d.ts +6 -0
  74. package/backend/dist/services/documentService.d.ts.map +1 -1
  75. package/backend/dist/services/documentService.js +58 -4
  76. package/backend/dist/services/documentService.js.map +1 -1
  77. package/backend/dist/services/encryptionService.d.ts +6 -0
  78. package/backend/dist/services/encryptionService.d.ts.map +1 -1
  79. package/backend/dist/services/encryptionService.js +12 -0
  80. package/backend/dist/services/encryptionService.js.map +1 -1
  81. package/backend/dist/services/mfaService.d.ts +64 -0
  82. package/backend/dist/services/mfaService.d.ts.map +1 -0
  83. package/backend/dist/services/mfaService.js +263 -0
  84. package/backend/dist/services/mfaService.js.map +1 -0
  85. package/backend/dist/services/notificationService.d.ts +5 -0
  86. package/backend/dist/services/notificationService.d.ts.map +1 -1
  87. package/backend/dist/services/notificationService.js +33 -1
  88. package/backend/dist/services/notificationService.js.map +1 -1
  89. package/backend/dist/services/preferencesService.d.ts +1 -1
  90. package/backend/dist/services/preferencesService.d.ts.map +1 -1
  91. package/backend/dist/services/preferencesService.js +1 -0
  92. package/backend/dist/services/preferencesService.js.map +1 -1
  93. package/backend/dist/services/recoveryDrillService.d.ts +57 -0
  94. package/backend/dist/services/recoveryDrillService.d.ts.map +1 -0
  95. package/backend/dist/services/recoveryDrillService.js +363 -0
  96. package/backend/dist/services/recoveryDrillService.js.map +1 -0
  97. package/backend/dist/services/webPushService.d.ts +71 -0
  98. package/backend/dist/services/webPushService.d.ts.map +1 -0
  99. package/backend/dist/services/webPushService.js +328 -0
  100. package/backend/dist/services/webPushService.js.map +1 -0
  101. package/backend/dist/services/webauthnService.d.ts +58 -0
  102. package/backend/dist/services/webauthnService.d.ts.map +1 -0
  103. package/backend/dist/services/webauthnService.js +300 -0
  104. package/backend/dist/services/webauthnService.js.map +1 -0
  105. package/backend/dist/types/index.d.ts +3 -2
  106. package/backend/dist/types/index.d.ts.map +1 -1
  107. package/backend/dist/types/index.js.map +1 -1
  108. package/backend/dist/utils/documentExtraction.d.ts +31 -3
  109. package/backend/dist/utils/documentExtraction.d.ts.map +1 -1
  110. package/backend/dist/utils/documentExtraction.js +106 -0
  111. package/backend/dist/utils/documentExtraction.js.map +1 -1
  112. package/backend/dist/utils/toolEgress.d.ts +2 -1
  113. package/backend/dist/utils/toolEgress.d.ts.map +1 -1
  114. package/backend/dist/utils/toolEgress.js +14 -2
  115. package/backend/dist/utils/toolEgress.js.map +1 -1
  116. package/backend/dist/utils/totp.d.ts +19 -0
  117. package/backend/dist/utils/totp.d.ts.map +1 -0
  118. package/backend/dist/utils/totp.js +117 -0
  119. package/backend/dist/utils/totp.js.map +1 -0
  120. package/backend/dist/utils/webauthn.d.ts +43 -0
  121. package/backend/dist/utils/webauthn.d.ts.map +1 -0
  122. package/backend/dist/utils/webauthn.js +294 -0
  123. package/backend/dist/utils/webauthn.js.map +1 -0
  124. package/backend/package.json +1 -1
  125. package/frontend/dist/artifact-runtime/.build-fingerprint +1 -1
  126. package/frontend/dist/assets/index-DBAf_VaS.js +47 -0
  127. package/frontend/dist/assets/index-kn_Axjts.css +1 -0
  128. package/frontend/dist/fonts/jetbrains-mono-latin-400-normal.woff2 +0 -0
  129. package/frontend/dist/fonts/jetbrains-mono-latin-700-normal.woff2 +0 -0
  130. package/frontend/dist/fonts/jetbrains-mono.css +24 -0
  131. package/frontend/dist/index.html +20 -13
  132. package/frontend/dist/js/ArtifactContainer-C9zNtCay.js +1 -0
  133. package/frontend/dist/js/{ArtifactDemoPage-D0JEIAbZ.js → ArtifactDemoPage-C-EBFBCt.js} +1 -1
  134. package/frontend/dist/js/ArtifactSandboxFrame-CTA1j_d1.js +111 -0
  135. package/frontend/dist/js/ArtifactSlideOutPanel-BOENbOiv.js +1 -0
  136. package/frontend/dist/js/AutomationsPage-C58MQ4te.js +1 -0
  137. package/frontend/dist/js/CalendarPage-BcdAZ292.js +1 -0
  138. package/frontend/dist/js/ChannelsPage-DPr9Yajs.js +1 -0
  139. package/frontend/dist/js/ChatPage-DsbB748s.js +14 -0
  140. package/frontend/dist/js/{EvaluationsPage-Bm8FRoeX.js → EvaluationsPage-FWIJu2fI.js} +1 -1
  141. package/frontend/dist/js/FirstTimeSetup-Cy-w6xko.js +1 -0
  142. package/frontend/dist/js/GalleryPage-DDAtxM-x.js +1 -0
  143. package/frontend/dist/js/{HomePage-BqNy-FWX.js → HomePage-DLKwmE7n.js} +1 -1
  144. package/frontend/dist/js/LibreClawPage-vKOj-9w9.js +1 -0
  145. package/frontend/dist/js/{ModelManagerModals-DX9eT5r2.js → ModelManagerModals-BY5TK38_.js} +1 -1
  146. package/frontend/dist/js/ModelSelector-CoCS6MZ9.js +1 -0
  147. package/frontend/dist/js/NotesPage-BElIpXgA.js +3 -0
  148. package/frontend/dist/js/OptimizedSyntaxHighlighter-xgyE3dRA.js +1 -0
  149. package/frontend/dist/js/PersonasPage-P40jVbfU.js +13 -0
  150. package/frontend/dist/js/{PluginUsagePage-Ctoka6JZ.js → PluginUsagePage-CKUEPCJN.js} +1 -1
  151. package/frontend/dist/js/RichMessageContent-Bm2WaEGi.js +3 -0
  152. package/frontend/dist/js/SettingsModal-DxSGzx9A.js +19 -0
  153. package/frontend/dist/js/SystemPage-BT07dTMT.js +1 -0
  154. package/frontend/dist/js/UserManagementPage-B9Vz5cm3.js +1 -0
  155. package/frontend/dist/js/WorkPage-qoN_e_jK.js +6 -0
  156. package/frontend/dist/js/api-D7Ff5M2Q.js +14 -0
  157. package/frontend/dist/js/appStore-DmV5wqDz.js +2 -0
  158. package/frontend/dist/js/ar-YHHTVP8f.js +2 -0
  159. package/frontend/dist/js/artifactHtml-DDmfpcB0.js +41 -0
  160. package/frontend/dist/js/{authStore-DbqqrfZv.js → authStore-BYW8-kP6.js} +3 -3
  161. package/frontend/dist/js/bn-C8VTxBVl.js +2 -0
  162. package/frontend/dist/js/chatStore-D-NTlArO.js +3 -0
  163. package/frontend/dist/js/{config-8KdgjtEx.js → config-BqZ5304Q.js} +1 -1
  164. package/frontend/dist/js/cs-CCngB5fC.js +2 -0
  165. package/frontend/dist/js/da-CM2PP2ma.js +2 -0
  166. package/frontend/dist/js/de-BqVkD-dw.js +2 -0
  167. package/frontend/dist/js/es-BtWsWBjZ.js +2 -0
  168. package/frontend/dist/js/{estree-BU5-LV_1.js → estree-DPkpBuOU.js} +1 -1
  169. package/frontend/dist/js/{evaluationsApi-CwZJsgeG.js → evaluationsApi-Dnf2dTd-.js} +1 -1
  170. package/frontend/dist/js/fr-C68w9abT.js +2 -0
  171. package/frontend/dist/js/hi-DtBlcyXZ.js +2 -0
  172. package/frontend/dist/js/{html-DeoAy2bo.js → html-BpGUXG8E.js} +1 -1
  173. package/frontend/dist/js/id-BWzIu8tn.js +2 -0
  174. package/frontend/dist/js/is-yeFwu4dJ.js +2 -0
  175. package/frontend/dist/js/it-AxEMG8Jg.js +2 -0
  176. package/frontend/dist/js/ja-B-VyokB8.js +2 -0
  177. package/frontend/dist/js/ko-CvwzYr9k.js +2 -0
  178. package/frontend/dist/js/{markdown-DOLsYkBY.js → markdown-DCBRWJSK.js} +1 -1
  179. package/frontend/dist/js/{messageCodeStyles-DhVgizF-.js → messageCodeStyles-BQwf7sSz.js} +2 -2
  180. package/frontend/dist/js/ms-BhEwb4b7.js +2 -0
  181. package/frontend/dist/js/nl-Dq-QRx1L.js +2 -0
  182. package/frontend/dist/js/pl-BNEy8mQm.js +2 -0
  183. package/frontend/dist/js/pt-_3Jl3FdY.js +2 -0
  184. package/frontend/dist/js/ru-IhQiJ8ta.js +2 -0
  185. package/frontend/dist/js/{standalone-BO5pzI8_.js → standalone-BFrK0R2A.js} +1 -1
  186. package/frontend/dist/js/sv-DWunXUNz.js +2 -0
  187. package/frontend/dist/js/{syntax-highlight-DcR9Vhb7.js → syntax-highlight-P2zo9o06.js} +1 -1
  188. package/frontend/dist/js/th-DVnW2yyh.js +2 -0
  189. package/frontend/dist/js/tr-Do8CFmhR.js +2 -0
  190. package/frontend/dist/js/{typescript-6GJ8APmn.js → typescript-DYVyvmm_.js} +1 -1
  191. package/frontend/dist/js/ui-db9meqAB.js +2 -0
  192. package/frontend/dist/js/ui-vendor-C0DZ8-Cm.js +177 -0
  193. package/frontend/dist/js/uk-BNPKIZga.js +2 -0
  194. package/frontend/dist/js/vi-CZMFo9je.js +2 -0
  195. package/frontend/dist/js/{workApi-BruWTlHM.js → workApi-CcprMH_1.js} +2 -2
  196. package/frontend/dist/js/zh-Pg0881v0.js +2 -0
  197. package/frontend/dist/manifest.webmanifest +32 -0
  198. package/frontend/dist/sw.js +125 -0
  199. package/package.json +2 -2
  200. package/frontend/dist/assets/index-BPsH9FII.js +0 -80
  201. package/frontend/dist/assets/index-BtXF0Oxa.css +0 -1
  202. package/frontend/dist/js/ArtifactContainer-CIoFoMv8.js +0 -1
  203. package/frontend/dist/js/ArtifactSandboxFrame-REazyIrx.js +0 -151
  204. package/frontend/dist/js/ArtifactSlideOutPanel-A-i94ghF.js +0 -1
  205. package/frontend/dist/js/AutomationsPage-Dow_lbIf.js +0 -1
  206. package/frontend/dist/js/CalendarPage-DLjasZOd.js +0 -1
  207. package/frontend/dist/js/ChannelsPage-C8hvTD7J.js +0 -1
  208. package/frontend/dist/js/ChatPage-BAgBHUES.js +0 -16
  209. package/frontend/dist/js/FirstTimeSetup-BxJapNul.js +0 -1
  210. package/frontend/dist/js/GalleryPage-DHz9DkK7.js +0 -1
  211. package/frontend/dist/js/LibreClawPage-BDzDILjY.js +0 -1
  212. package/frontend/dist/js/ModelSelector-ol8mK2ja.js +0 -1
  213. package/frontend/dist/js/NotesPage-DzHEx2O4.js +0 -3
  214. package/frontend/dist/js/OptimizedSyntaxHighlighter-CZNPwN0i.js +0 -1
  215. package/frontend/dist/js/PersonasPage-M_WKH2a9.js +0 -13
  216. package/frontend/dist/js/RichMessageContent-BqgafH4p.js +0 -3
  217. package/frontend/dist/js/SettingsModal-R9EDadAV.js +0 -19
  218. package/frontend/dist/js/SystemPage-aOkrSuY7.js +0 -1
  219. package/frontend/dist/js/UserManagementPage-BOuaX3ay.js +0 -1
  220. package/frontend/dist/js/WorkPage-DxZISerQ.js +0 -6
  221. package/frontend/dist/js/api-6bSMtSNh.js +0 -14
  222. package/frontend/dist/js/appStore-CdObhQBb.js +0 -2
  223. package/frontend/dist/js/ar-DT_VLBO3.js +0 -2
  224. package/frontend/dist/js/bn-BrvEqhGA.js +0 -2
  225. package/frontend/dist/js/chatStore-DbZPJNsu.js +0 -3
  226. package/frontend/dist/js/cs-BthnM_YI.js +0 -2
  227. package/frontend/dist/js/da-DCth-6_j.js +0 -2
  228. package/frontend/dist/js/de-BBWt0gJ_.js +0 -2
  229. package/frontend/dist/js/es-DuslOPqM.js +0 -2
  230. package/frontend/dist/js/fr-DZAeg46S.js +0 -2
  231. package/frontend/dist/js/hi-ijDSiY77.js +0 -2
  232. package/frontend/dist/js/id-BS6qS0oq.js +0 -2
  233. package/frontend/dist/js/is-CO2PMDte.js +0 -2
  234. package/frontend/dist/js/it-ChhxT4hs.js +0 -2
  235. package/frontend/dist/js/ja-cyeqUeJN.js +0 -2
  236. package/frontend/dist/js/ko-CfqHTLXj.js +0 -2
  237. package/frontend/dist/js/ms-DZ5pACe1.js +0 -2
  238. package/frontend/dist/js/nl-BsRsnOVp.js +0 -2
  239. package/frontend/dist/js/pl-DXSTAW4M.js +0 -2
  240. package/frontend/dist/js/pt-DmDx0JxB.js +0 -2
  241. package/frontend/dist/js/ru-d2g8EWfY.js +0 -2
  242. package/frontend/dist/js/sv-CIhYUTVQ.js +0 -2
  243. package/frontend/dist/js/th-B3wSxanB.js +0 -2
  244. package/frontend/dist/js/tr-R4UGgPOY.js +0 -2
  245. package/frontend/dist/js/ui-CV0LYmkM.js +0 -2
  246. package/frontend/dist/js/ui-vendor-CWjIBW-z.js +0 -177
  247. package/frontend/dist/js/uk-BzUF4Vkt.js +0 -2
  248. package/frontend/dist/js/vi-CKc6Fg4h.js +0 -2
  249. package/frontend/dist/js/zh-CXL-kDe8.js +0 -2
@@ -0,0 +1,328 @@
1
+ /*
2
+ * Libre WebUI
3
+ * Copyright (C) 2025 Kroonen AI, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at:
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ /**
18
+ * Browser Web Push, implemented directly on node:crypto:
19
+ *
20
+ * - RFC 8291 payload encryption (ECDH P-256 + HKDF + aes128gcm, one record).
21
+ * - RFC 8292 VAPID authorization (ES256 JWT bound to the endpoint origin).
22
+ * - Per-device subscriptions encrypted at rest, bound to the auth session
23
+ * that created them, and removed when that session is revoked.
24
+ *
25
+ * VAPID keys come from `VAPID_PUBLIC_KEY`/`VAPID_PRIVATE_KEY` (base64url,
26
+ * uncompressed point + raw scalar) or are generated once and persisted in
27
+ * system settings, encrypted. Deliveries go through the shared egress guard,
28
+ * so a subscription endpoint can never reach private address space.
29
+ */
30
+ import { createCipheriv, createECDH, createPrivateKey, hkdfSync, randomBytes, randomUUID, sign as cryptoSign, } from 'node:crypto';
31
+ import { isIP } from 'node:net';
32
+ import { getPersistence } from '../persistence/index.js';
33
+ import { encryptionService } from './encryptionService.js';
34
+ import { getSystemSetting, setSystemSetting } from './systemSettingsService.js';
35
+ import { isAllowlistedPrivateHost, secureToolRequest, validateToolServerUrl, } from '../utils/toolEgress.js';
36
+ import { isPublicIpAddress } from '../utils/webpageFetcher.js';
37
+ import { registerSessionRevocationListener, } from './authSessionService.js';
38
+ import { createLogger } from '../utils/logger.js';
39
+ const logger = createLogger('services:web-push');
40
+ const repositories = () => getPersistence(encryptionService).repositories.resources;
41
+ const MAX_SUBSCRIPTIONS_PER_USER = 10;
42
+ const MAX_PAYLOAD_BYTES = 3800;
43
+ const PUSH_TTL_SECONDS = 24 * 60 * 60;
44
+ const VAPID_JWT_LIFETIME_SECONDS = 12 * 60 * 60;
45
+ export class WebPushError extends Error {
46
+ constructor(message, statusCode = 400) {
47
+ super(message);
48
+ this.statusCode = statusCode;
49
+ }
50
+ }
51
+ /** The endpoint acknowledged but the subscription no longer exists. */
52
+ export class PushSubscriptionGoneError extends Error {
53
+ }
54
+ let cachedKeys = null;
55
+ const generateVapidKeys = () => {
56
+ const ecdh = createECDH('prime256v1');
57
+ ecdh.generateKeys();
58
+ return {
59
+ publicKey: ecdh.getPublicKey().toString('base64url'),
60
+ privateKey: ecdh.getPrivateKey().toString('base64url'),
61
+ };
62
+ };
63
+ const validKeyShape = (keys) => {
64
+ try {
65
+ const publicKey = Buffer.from(keys.publicKey, 'base64url');
66
+ const privateKey = Buffer.from(keys.privateKey, 'base64url');
67
+ return (publicKey.length === 65 &&
68
+ publicKey[0] === 0x04 &&
69
+ privateKey.length === 32);
70
+ }
71
+ catch {
72
+ return false;
73
+ }
74
+ };
75
+ export const getVapidKeys = async () => {
76
+ if (cachedKeys)
77
+ return cachedKeys;
78
+ const fromEnv = {
79
+ publicKey: process.env.VAPID_PUBLIC_KEY?.trim() ?? '',
80
+ privateKey: process.env.VAPID_PRIVATE_KEY?.trim() ?? '',
81
+ };
82
+ if (fromEnv.publicKey && fromEnv.privateKey) {
83
+ if (!validKeyShape(fromEnv)) {
84
+ throw new WebPushError('VAPID_PUBLIC_KEY/VAPID_PRIVATE_KEY are not valid base64url P-256 keys', 500);
85
+ }
86
+ cachedKeys = fromEnv;
87
+ return cachedKeys;
88
+ }
89
+ const stored = await getSystemSetting('web_push_vapid_keys');
90
+ if (stored) {
91
+ try {
92
+ const decoded = JSON.parse(encryptionService.decryptAuthenticated(stored));
93
+ if (validKeyShape(decoded)) {
94
+ cachedKeys = decoded;
95
+ return cachedKeys;
96
+ }
97
+ }
98
+ catch {
99
+ logger.warn('Stored VAPID keys were unreadable; regenerating');
100
+ }
101
+ }
102
+ const generated = generateVapidKeys();
103
+ await setSystemSetting('web_push_vapid_keys', encryptionService.encrypt(JSON.stringify(generated)));
104
+ cachedKeys = generated;
105
+ return cachedKeys;
106
+ };
107
+ export const getVapidPublicKey = async () => (await getVapidKeys()).publicKey;
108
+ /** Raw P-256 scalar + point → a PEM/KeyObject for ES256 signing. */
109
+ const vapidPrivateKeyObject = (keys) => {
110
+ const publicKey = Buffer.from(keys.publicKey, 'base64url');
111
+ return createPrivateKey({
112
+ key: {
113
+ kty: 'EC',
114
+ crv: 'P-256',
115
+ d: keys.privateKey,
116
+ x: publicKey.subarray(1, 33).toString('base64url'),
117
+ y: publicKey.subarray(33, 65).toString('base64url'),
118
+ },
119
+ format: 'jwk',
120
+ });
121
+ };
122
+ const base64UrlJson = (value) => Buffer.from(JSON.stringify(value), 'utf8').toString('base64url');
123
+ /** RFC 8292 VAPID JWT for one push-service origin. */
124
+ export const buildVapidAuthorization = async (endpointOrigin) => {
125
+ const keys = await getVapidKeys();
126
+ const header = base64UrlJson({ typ: 'JWT', alg: 'ES256' });
127
+ const payload = base64UrlJson({
128
+ aud: endpointOrigin,
129
+ exp: Math.floor(Date.now() / 1000) + VAPID_JWT_LIFETIME_SECONDS,
130
+ sub: process.env.VAPID_SUBJECT?.trim() || 'mailto:admin@localhost',
131
+ });
132
+ const signature = cryptoSign('sha256', Buffer.from(`${header}.${payload}`, 'utf8'), { key: vapidPrivateKeyObject(keys), dsaEncoding: 'ieee-p1363' }).toString('base64url');
133
+ return `vapid t=${header}.${payload}.${signature}, k=${keys.publicKey}`;
134
+ };
135
+ /**
136
+ * Encrypt one push message with aes128gcm content encoding. Returns the
137
+ * complete request body (header block + single record).
138
+ */
139
+ export const encryptPushPayload = (userAgentPublicKey, authSecret, plaintext, params = {}) => {
140
+ if (userAgentPublicKey.length !== 65 || userAgentPublicKey[0] !== 0x04) {
141
+ throw new WebPushError('Subscription p256dh key is not a P-256 point');
142
+ }
143
+ if (authSecret.length !== 16) {
144
+ throw new WebPushError('Subscription auth secret must be 16 bytes');
145
+ }
146
+ if (plaintext.length > MAX_PAYLOAD_BYTES) {
147
+ throw new WebPushError('Push payload is too large');
148
+ }
149
+ const ecdh = createECDH('prime256v1');
150
+ if (params.ephemeralPrivateKey) {
151
+ ecdh.setPrivateKey(params.ephemeralPrivateKey);
152
+ }
153
+ else {
154
+ ecdh.generateKeys();
155
+ }
156
+ const applicationServerPublicKey = ecdh.getPublicKey();
157
+ const sharedSecret = ecdh.computeSecret(userAgentPublicKey);
158
+ // RFC 8291 §3.3-3.4: combine the ECDH secret with the auth secret and both
159
+ // public keys, then derive the content key and nonce from a random salt.
160
+ const keyInfo = Buffer.concat([
161
+ Buffer.from('WebPush: info\0', 'utf8'),
162
+ userAgentPublicKey,
163
+ applicationServerPublicKey,
164
+ ]);
165
+ const inputKeyingMaterial = Buffer.from(hkdfSync('sha256', sharedSecret, authSecret, keyInfo, 32));
166
+ const salt = params.salt ?? randomBytes(16);
167
+ const contentEncryptionKey = Buffer.from(hkdfSync('sha256', inputKeyingMaterial, salt, Buffer.from('Content-Encoding: aes128gcm\0', 'utf8'), 16));
168
+ const nonce = Buffer.from(hkdfSync('sha256', inputKeyingMaterial, salt, Buffer.from('Content-Encoding: nonce\0', 'utf8'), 12));
169
+ const recordSize = Buffer.alloc(4);
170
+ recordSize.writeUInt32BE(4096);
171
+ const header = Buffer.concat([
172
+ salt,
173
+ recordSize,
174
+ Buffer.from([applicationServerPublicKey.length]),
175
+ applicationServerPublicKey,
176
+ ]);
177
+ const cipher = createCipheriv('aes-128-gcm', contentEncryptionKey, nonce);
178
+ const padded = Buffer.concat([plaintext, Buffer.from([0x02])]);
179
+ const ciphertext = Buffer.concat([
180
+ cipher.update(padded),
181
+ cipher.final(),
182
+ cipher.getAuthTag(),
183
+ ]);
184
+ return Buffer.concat([header, ciphertext]);
185
+ };
186
+ /* ------------------------------------------------------ subscriptions */
187
+ const endpointLookup = (endpoint) => encryptionService.purposeLookupToken('push-endpoint', endpoint);
188
+ const assertAcceptableEndpoint = (endpoint) => {
189
+ let url;
190
+ try {
191
+ url = validateToolServerUrl(endpoint);
192
+ }
193
+ catch {
194
+ throw new WebPushError('The push endpoint URL is not valid');
195
+ }
196
+ if (url.protocol !== 'https:') {
197
+ throw new WebPushError('Push endpoints must use HTTPS');
198
+ }
199
+ const literalHost = url.hostname.replace(/^\[|\]$/g, '');
200
+ if (isIP(literalHost) !== 0 &&
201
+ !isAllowlistedPrivateHost(url.hostname) &&
202
+ !isPublicIpAddress(literalHost)) {
203
+ throw new WebPushError('Push endpoints cannot target private or local addresses');
204
+ }
205
+ return url;
206
+ };
207
+ const decodeSubscriptionKey = (value, name, expectedLength) => {
208
+ if (typeof value !== 'string' || !value) {
209
+ throw new WebPushError(`Subscription ${name} key is required`);
210
+ }
211
+ const decoded = Buffer.from(value, 'base64url');
212
+ if (decoded.length !== expectedLength) {
213
+ throw new WebPushError(`Subscription ${name} key has the wrong length`);
214
+ }
215
+ return decoded;
216
+ };
217
+ export const subscribe = async (userId, sessionId, input, userAgent) => {
218
+ const endpoint = typeof input.endpoint === 'string' ? input.endpoint : '';
219
+ if (!endpoint || endpoint.length > 2048) {
220
+ throw new WebPushError('The push endpoint URL is not valid');
221
+ }
222
+ assertAcceptableEndpoint(endpoint);
223
+ decodeSubscriptionKey(input.keys?.p256dh, 'p256dh', 65);
224
+ decodeSubscriptionKey(input.keys?.auth, 'auth', 16);
225
+ const existing = await repositories().pushSubscriptions.listByUser(userId);
226
+ const lookup = endpointLookup(endpoint);
227
+ const alreadyMine = existing.some(row => row.endpoint_lookup === lookup);
228
+ if (!alreadyMine && existing.length >= MAX_SUBSCRIPTIONS_PER_USER) {
229
+ throw new WebPushError(`At most ${MAX_SUBSCRIPTIONS_PER_USER} push subscriptions are supported per account`, 409);
230
+ }
231
+ const record = {
232
+ id: randomUUID(),
233
+ user_id: userId,
234
+ session_id: sessionId,
235
+ endpoint_lookup: lookup,
236
+ subscription: encryptionService.encrypt(JSON.stringify({
237
+ endpoint,
238
+ keys: { p256dh: input.keys?.p256dh, auth: input.keys?.auth },
239
+ })),
240
+ user_agent: userAgent?.slice(0, 512) ?? null,
241
+ created_at: Date.now(),
242
+ last_used_at: null,
243
+ };
244
+ await repositories().pushSubscriptions.upsertByEndpoint(record);
245
+ const stored = await repositories().pushSubscriptions.findByLookup(lookup);
246
+ return { id: stored?.id ?? record.id };
247
+ };
248
+ export const unsubscribe = async (userId, endpoint) => repositories().pushSubscriptions.deleteByLookup(userId, endpointLookup(endpoint));
249
+ export const listSubscriptionsForUser = async (userId) => repositories().pushSubscriptions.listByUser(userId);
250
+ /* ------------------------------------------------------------ delivery */
251
+ const decryptSubscription = (record) => {
252
+ try {
253
+ const decoded = JSON.parse(encryptionService.decryptAuthenticated(record.subscription));
254
+ if (typeof decoded.endpoint === 'string' &&
255
+ typeof decoded.keys?.p256dh === 'string' &&
256
+ typeof decoded.keys?.auth === 'string') {
257
+ return decoded;
258
+ }
259
+ }
260
+ catch {
261
+ // Fall through: an unreadable envelope means the subscription is dead.
262
+ }
263
+ return null;
264
+ };
265
+ /**
266
+ * Deliver one message to one stored subscription. Returns false (and
267
+ * removes the row) when the push service reports the subscription gone.
268
+ */
269
+ export const deliverToSubscription = async (subscriptionId, message, signal) => {
270
+ const record = await repositories().pushSubscriptions.findById(subscriptionId);
271
+ if (!record)
272
+ return { delivered: false };
273
+ const subscription = decryptSubscription(record);
274
+ if (!subscription) {
275
+ await repositories().pushSubscriptions.delete(record.id);
276
+ return { delivered: false };
277
+ }
278
+ const endpointUrl = assertAcceptableEndpoint(subscription.endpoint);
279
+ const body = encryptPushPayload(Buffer.from(subscription.keys.p256dh, 'base64url'), Buffer.from(subscription.keys.auth, 'base64url'), Buffer.from(JSON.stringify(message), 'utf8'));
280
+ const authorization = await buildVapidAuthorization(endpointUrl.origin);
281
+ const response = await secureToolRequest({
282
+ url: subscription.endpoint,
283
+ method: 'POST',
284
+ headers: {
285
+ Authorization: authorization,
286
+ TTL: String(PUSH_TTL_SECONDS),
287
+ 'Content-Encoding': 'aes128gcm',
288
+ 'Content-Type': 'application/octet-stream',
289
+ Urgency: 'normal',
290
+ },
291
+ body,
292
+ timeoutMs: 10000,
293
+ maxResponseBytes: 16 * 1024,
294
+ ...(signal ? { signal } : {}),
295
+ });
296
+ if (response.status === 404 || response.status === 410) {
297
+ await repositories().pushSubscriptions.delete(record.id);
298
+ throw new PushSubscriptionGoneError(`Push subscription is gone (${response.status})`);
299
+ }
300
+ if (response.status >= 200 && response.status < 300) {
301
+ await repositories().pushSubscriptions.touch(record.id, Date.now());
302
+ return { delivered: true, status: response.status };
303
+ }
304
+ return { delivered: false, status: response.status };
305
+ };
306
+ /* ---------------------------------------------------------- lifecycle */
307
+ let revocationCleanupRegistered = false;
308
+ /** Remove device subscriptions when their auth session is revoked. */
309
+ export const registerPushSessionCleanup = () => {
310
+ if (revocationCleanupRegistered)
311
+ return;
312
+ revocationCleanupRegistered = true;
313
+ registerSessionRevocationListener((event) => {
314
+ void (async () => {
315
+ try {
316
+ for (const sessionId of event.sessionIds) {
317
+ await repositories().pushSubscriptions.deleteForSession(sessionId);
318
+ }
319
+ }
320
+ catch (error) {
321
+ logger.warn('Push subscription cleanup after revocation failed', {
322
+ error,
323
+ });
324
+ }
325
+ })();
326
+ });
327
+ };
328
+ //# sourceMappingURL=webPushService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webPushService.js","sourceRoot":"","sources":["../../src/services/webPushService.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ export declare class PasskeyError extends Error {
2
+ readonly statusCode: number;
3
+ constructor(message: string, statusCode?: number);
4
+ }
5
+ /** The relying-party id and origin policy for a request host. */
6
+ export declare const resolveRelyingParty: (requestHost: string) => {
7
+ rpId: string;
8
+ };
9
+ export interface PublicPasskey {
10
+ id: string;
11
+ name: string | null;
12
+ createdAt: number;
13
+ lastUsedAt: number | null;
14
+ }
15
+ export declare const listPasskeys: (userId: string) => Promise<PublicPasskey[]>;
16
+ export declare const deletePasskey: (userId: string, passkeyId: string) => Promise<boolean>;
17
+ export declare const deleteAllPasskeys: (userId: string) => Promise<number>;
18
+ export declare const registrationOptions: (user: {
19
+ id: string;
20
+ username: string;
21
+ }, requestHost: string) => Promise<{
22
+ challengeToken: string;
23
+ publicKey: Record<string, unknown>;
24
+ }>;
25
+ export interface RegistrationSubmission {
26
+ challengeToken: string;
27
+ name?: string;
28
+ credential: {
29
+ rawId: string;
30
+ response: {
31
+ clientDataJSON: string;
32
+ attestationObject: string;
33
+ transports?: string[];
34
+ };
35
+ };
36
+ }
37
+ export declare const registerPasskey: (userId: string, submission: RegistrationSubmission, requestHost: string) => Promise<PublicPasskey>;
38
+ export declare const loginOptions: (requestHost: string) => Promise<{
39
+ challengeToken: string;
40
+ publicKey: Record<string, unknown>;
41
+ }>;
42
+ export interface AssertionSubmission {
43
+ challengeToken: string;
44
+ credential: {
45
+ rawId: string;
46
+ response: {
47
+ clientDataJSON: string;
48
+ authenticatorData: string;
49
+ signature: string;
50
+ };
51
+ };
52
+ }
53
+ /** Verify a passwordless sign-in assertion; returns the credential owner. */
54
+ export declare const verifyPasskeyLogin: (submission: AssertionSubmission, requestHost: string) => Promise<{
55
+ userId: string;
56
+ passkeyId: string;
57
+ }>;
58
+ //# sourceMappingURL=webauthnService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webauthnService.d.ts","sourceRoot":"","sources":["../../src/services/webauthnService.ts"],"names":[],"mappings":"AAyDA,qBAAa,YAAa,SAAQ,KAAK;aAGnB,UAAU;IAF5B,YACE,OAAO,EAAE,MAAM,EACC,UAAU,SAAM,EAGjC;CACF;AAyBD,iEAAiE;AACjE,eAAO,MAAM,mBAAmB,gBAAiB,MAAM,KAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAQvE,CAAC;AAuEF,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AASD,eAAO,MAAM,YAAY,WAAkB,MAAM,KAAG,OAAO,CAAC,aAAa,EAAE,CACV,CAAC;AAElE,eAAO,MAAM,aAAa,WAChB,MAAM,aACH,MAAM,KAChB,OAAO,CAAC,OAAO,CAAqD,CAAC;AAExE,eAAO,MAAM,iBAAiB,WAAkB,MAAM,KAAG,OAAO,CAAC,MAAM,CACzB,CAAC;AAE/C,eAAO,MAAM,mBAAmB,SACxB;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,eACzB,MAAM,KAClB,OAAO,CAAC;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CA0CxE,CAAC;AAEF,MAAM,WAAW,sBAAsB;IACrC,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,EAAE;QACV,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE;YACR,cAAc,EAAE,MAAM,CAAC;YACvB,iBAAiB,EAAE,MAAM,CAAC;YAC1B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;SACvB,CAAC;KACH,CAAC;CACH;AAED,eAAO,MAAM,eAAe,WAClB,MAAM,cACF,sBAAsB,eACrB,MAAM,KAClB,OAAO,CAAC,aAAa,CAgGvB,CAAC;AAEF,eAAO,MAAM,YAAY,gBACV,MAAM,KAClB,OAAO,CAAC;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAYxE,CAAC;AAEF,MAAM,WAAW,mBAAmB;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE;QACV,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE;YACR,cAAc,EAAE,MAAM,CAAC;YACvB,iBAAiB,EAAE,MAAM,CAAC;YAC1B,SAAS,EAAE,MAAM,CAAC;SACnB,CAAC;KACH,CAAC;CACH;AAED,6EAA6E;AAC7E,eAAO,MAAM,kBAAkB,eACjB,mBAAmB,eAClB,MAAM,KAClB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAqF/C,CAAC"}