@nodefony/security 10.0.0-alpha.1

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 (258) hide show
  1. package/LICENSE +544 -0
  2. package/README.md +182 -0
  3. package/dist/_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorate.js +9 -0
  4. package/dist/_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorateMetadata.js +6 -0
  5. package/dist/index.js +151 -0
  6. package/dist/nodefony/command/security-secrets.js +158 -0
  7. package/dist/nodefony/command/security-token.js +335 -0
  8. package/dist/nodefony/command/security-user-add.js +131 -0
  9. package/dist/nodefony/command/security-user-delete.js +102 -0
  10. package/dist/nodefony/command/security-user-list.js +77 -0
  11. package/dist/nodefony/config/config.js +366 -0
  12. package/dist/nodefony/config/defineModuleConfig.js +35 -0
  13. package/dist/nodefony/contracts/IAccessVoter.js +13 -0
  14. package/dist/nodefony/contracts/IApiKey.js +1 -0
  15. package/dist/nodefony/contracts/IAuditEvent.js +1 -0
  16. package/dist/nodefony/contracts/IAuditStore.js +1 -0
  17. package/dist/nodefony/contracts/IAuthenticator.js +1 -0
  18. package/dist/nodefony/contracts/IAuthorizationService.js +1 -0
  19. package/dist/nodefony/contracts/IFirewall.js +1 -0
  20. package/dist/nodefony/contracts/IFirewallDescription.js +1 -0
  21. package/dist/nodefony/contracts/IJwtKeystore.js +1 -0
  22. package/dist/nodefony/contracts/IOAuthProvider.js +1 -0
  23. package/dist/nodefony/contracts/ISecuredArea.js +1 -0
  24. package/dist/nodefony/contracts/IToken.js +1 -0
  25. package/dist/nodefony/contracts/ITokenStore.js +1 -0
  26. package/dist/nodefony/contracts/ITotpSecret.js +1 -0
  27. package/dist/nodefony/contracts/ITotpSecretStore.js +1 -0
  28. package/dist/nodefony/contracts/IWebAuthnCredential.js +1 -0
  29. package/dist/nodefony/contracts/IWebAuthnCredentialStore.js +1 -0
  30. package/dist/nodefony/contracts/IWebhookEndpoint.js +1 -0
  31. package/dist/nodefony/contracts/IWebhookStore.js +1 -0
  32. package/dist/nodefony/contracts/index.js +2 -0
  33. package/dist/nodefony/errors/AccessDeniedError.js +14 -0
  34. package/dist/nodefony/errors/ApiKeyError.js +21 -0
  35. package/dist/nodefony/errors/AuthenticationError.js +14 -0
  36. package/dist/nodefony/errors/CsrfError.js +23 -0
  37. package/dist/nodefony/errors/InvalidTargetError.js +39 -0
  38. package/dist/nodefony/errors/SsrfError.js +17 -0
  39. package/dist/nodefony/errors/ThrottledError.js +21 -0
  40. package/dist/nodefony/errors/UnverifiableTokenError.js +42 -0
  41. package/dist/nodefony/errors/WebAuthnError.js +21 -0
  42. package/dist/nodefony/errors/index.js +9 -0
  43. package/dist/nodefony/service/accessTokenVerifier.js +77 -0
  44. package/dist/nodefony/service/apiKeys.js +310 -0
  45. package/dist/nodefony/service/auditService.js +145 -0
  46. package/dist/nodefony/service/authFlow.js +332 -0
  47. package/dist/nodefony/service/authorization.js +95 -0
  48. package/dist/nodefony/service/cors.js +81 -0
  49. package/dist/nodefony/service/csrf.js +97 -0
  50. package/dist/nodefony/service/firewall.js +699 -0
  51. package/dist/nodefony/service/oauth2.js +153 -0
  52. package/dist/nodefony/service/securityHeaders.js +80 -0
  53. package/dist/nodefony/service/tokenService.js +486 -0
  54. package/dist/nodefony/service/totp.js +209 -0
  55. package/dist/nodefony/service/webAuthn.js +343 -0
  56. package/dist/nodefony/service/webhooks.js +539 -0
  57. package/dist/nodefony/src/RoleHierarchyWalker.js +77 -0
  58. package/dist/nodefony/src/SecuredArea.js +51 -0
  59. package/dist/nodefony/src/admin/SecurityAdminApi.js +495 -0
  60. package/dist/nodefony/src/admin/WebhookAdminApi.js +378 -0
  61. package/dist/nodefony/src/admin/adminAudit.js +37 -0
  62. package/dist/nodefony/src/admin/userRevocationCascade.js +40 -0
  63. package/dist/nodefony/src/apikey/apiKeyFormat.js +107 -0
  64. package/dist/nodefony/src/audit/MemoryAuditStore.js +121 -0
  65. package/dist/nodefony/src/audit/auditBridge.js +82 -0
  66. package/dist/nodefony/src/audit/auditFilters.js +60 -0
  67. package/dist/nodefony/src/audit/auditStoreRegistry.js +25 -0
  68. package/dist/nodefony/src/audit/readAuditContext.js +24 -0
  69. package/dist/nodefony/src/audit/recordAudit.js +16 -0
  70. package/dist/nodefony/src/authenticator/AnonymousAuthenticator.js +36 -0
  71. package/dist/nodefony/src/authenticator/ApiKeyAuthenticator.js +164 -0
  72. package/dist/nodefony/src/authenticator/ExternalJwtAuthenticator.js +224 -0
  73. package/dist/nodefony/src/authenticator/FirewallRealtimeAuthenticator.js +174 -0
  74. package/dist/nodefony/src/authenticator/JwtAuthenticator.js +176 -0
  75. package/dist/nodefony/src/authenticator/SessionAuthenticator.js +92 -0
  76. package/dist/nodefony/src/authenticator/UserPasswordAuthenticator.js +95 -0
  77. package/dist/nodefony/src/authenticator/authenticatorRegistry.js +63 -0
  78. package/dist/nodefony/src/authenticator/bearer.js +2 -0
  79. package/dist/nodefony/src/authenticator/externalSubject.js +36 -0
  80. package/dist/nodefony/src/authenticator/peekIssuer.js +56 -0
  81. package/dist/nodefony/src/crypto/secretCipher.js +79 -0
  82. package/dist/nodefony/src/csp.js +54 -0
  83. package/dist/nodefony/src/csrfToken.js +65 -0
  84. package/dist/nodefony/src/net/ssrfGuard.js +130 -0
  85. package/dist/nodefony/src/oauth/oauthProviderRegistry.js +37 -0
  86. package/dist/nodefony/src/oauth/providers/github.js +65 -0
  87. package/dist/nodefony/src/oauth/providers/oidc.js +48 -0
  88. package/dist/nodefony/src/realtime/UserRealtimeToken.js +94 -0
  89. package/dist/nodefony/src/realtime/frameAuthorizer.js +279 -0
  90. package/dist/nodefony/src/realtime/realtimeContracts.js +1 -0
  91. package/dist/nodefony/src/sessionIdentity.js +35 -0
  92. package/dist/nodefony/src/throttle/LoginThrottler.js +97 -0
  93. package/dist/nodefony/src/token/AnonymousToken.js +40 -0
  94. package/dist/nodefony/src/token/JwtKeystore.js +160 -0
  95. package/dist/nodefony/src/token/MemoryTokenStore.js +236 -0
  96. package/dist/nodefony/src/token/RemoteJwtVerifier.js +231 -0
  97. package/dist/nodefony/src/token/UserToken.js +67 -0
  98. package/dist/nodefony/src/token/jwtRuntime.js +19 -0
  99. package/dist/nodefony/src/token/secretFile.js +134 -0
  100. package/dist/nodefony/src/token/tokenCriteria.js +35 -0
  101. package/dist/nodefony/src/token/tokenFilters.js +72 -0
  102. package/dist/nodefony/src/token/tokenSort.js +40 -0
  103. package/dist/nodefony/src/token/tokenStatus.js +35 -0
  104. package/dist/nodefony/src/token/tokenStoreRegistry.js +25 -0
  105. package/dist/nodefony/src/totp/MemoryTotpSecretStore.js +97 -0
  106. package/dist/nodefony/src/totp/totpCipher.js +30 -0
  107. package/dist/nodefony/src/totp/totpCrypto.js +226 -0
  108. package/dist/nodefony/src/totp/totpOperations.js +129 -0
  109. package/dist/nodefony/src/totp/totpSecretStoreRegistry.js +18 -0
  110. package/dist/nodefony/src/voter/RoleVoter.js +32 -0
  111. package/dist/nodefony/src/voter/ScopeVoter.js +52 -0
  112. package/dist/nodefony/src/voter/voterRegistry.js +20 -0
  113. package/dist/nodefony/src/webauthn/MemoryWebAuthnCredentialStore.js +121 -0
  114. package/dist/nodefony/src/webauthn/webAuthnCredentialStoreRegistry.js +18 -0
  115. package/dist/nodefony/src/webhook/MemoryWebhookStore.js +87 -0
  116. package/dist/nodefony/src/webhook/WebhookDispatcher.js +208 -0
  117. package/dist/nodefony/src/webhook/webhookCipher.js +27 -0
  118. package/dist/nodefony/src/webhook/webhookDelivery.js +102 -0
  119. package/dist/nodefony/src/webhook/webhookFilters.js +56 -0
  120. package/dist/nodefony/src/webhook/webhookSignature.js +51 -0
  121. package/dist/nodefony/src/webhook/webhookSort.js +48 -0
  122. package/dist/nodefony/src/webhook/webhookStoreRegistry.js +18 -0
  123. package/dist/types/index.d.ts +157 -0
  124. package/dist/types/nodefony/command/security-secrets.d.ts +24 -0
  125. package/dist/types/nodefony/command/security-token.d.ts +44 -0
  126. package/dist/types/nodefony/command/security-user-add.d.ts +28 -0
  127. package/dist/types/nodefony/command/security-user-delete.d.ts +25 -0
  128. package/dist/types/nodefony/command/security-user-list.d.ts +28 -0
  129. package/dist/types/nodefony/config/config.d.ts +295 -0
  130. package/dist/types/nodefony/config/defineModuleConfig.d.ts +27 -0
  131. package/dist/types/nodefony/contracts/IAccessVoter.d.ts +23 -0
  132. package/dist/types/nodefony/contracts/IApiKey.d.ts +75 -0
  133. package/dist/types/nodefony/contracts/IAuditEvent.d.ts +94 -0
  134. package/dist/types/nodefony/contracts/IAuditStore.d.ts +80 -0
  135. package/dist/types/nodefony/contracts/IAuthenticator.d.ts +66 -0
  136. package/dist/types/nodefony/contracts/IAuthorizationService.d.ts +28 -0
  137. package/dist/types/nodefony/contracts/IFirewall.d.ts +64 -0
  138. package/dist/types/nodefony/contracts/IFirewallDescription.d.ts +120 -0
  139. package/dist/types/nodefony/contracts/IJwtKeystore.d.ts +40 -0
  140. package/dist/types/nodefony/contracts/IOAuthProvider.d.ts +51 -0
  141. package/dist/types/nodefony/contracts/ISecuredArea.d.ts +57 -0
  142. package/dist/types/nodefony/contracts/IToken.d.ts +41 -0
  143. package/dist/types/nodefony/contracts/ITokenStore.d.ts +240 -0
  144. package/dist/types/nodefony/contracts/ITotpSecret.d.ts +41 -0
  145. package/dist/types/nodefony/contracts/ITotpSecretStore.d.ts +88 -0
  146. package/dist/types/nodefony/contracts/IWebAuthnCredential.d.ts +56 -0
  147. package/dist/types/nodefony/contracts/IWebAuthnCredentialStore.d.ts +118 -0
  148. package/dist/types/nodefony/contracts/IWebhookEndpoint.d.ts +82 -0
  149. package/dist/types/nodefony/contracts/IWebhookStore.d.ts +85 -0
  150. package/dist/types/nodefony/contracts/index.d.ts +9 -0
  151. package/dist/types/nodefony/errors/AccessDeniedError.d.ts +10 -0
  152. package/dist/types/nodefony/errors/ApiKeyError.d.ts +17 -0
  153. package/dist/types/nodefony/errors/AuthenticationError.d.ts +10 -0
  154. package/dist/types/nodefony/errors/CsrfError.d.ts +19 -0
  155. package/dist/types/nodefony/errors/InvalidTargetError.d.ts +34 -0
  156. package/dist/types/nodefony/errors/SsrfError.d.ts +13 -0
  157. package/dist/types/nodefony/errors/ThrottledError.d.ts +16 -0
  158. package/dist/types/nodefony/errors/UnverifiableTokenError.d.ts +37 -0
  159. package/dist/types/nodefony/errors/WebAuthnError.d.ts +17 -0
  160. package/dist/types/nodefony/errors/index.d.ts +8 -0
  161. package/dist/types/nodefony/service/accessTokenVerifier.d.ts +29 -0
  162. package/dist/types/nodefony/service/apiKeys.d.ts +103 -0
  163. package/dist/types/nodefony/service/auditService.d.ts +30 -0
  164. package/dist/types/nodefony/service/authFlow.d.ts +123 -0
  165. package/dist/types/nodefony/service/authorization.d.ts +33 -0
  166. package/dist/types/nodefony/service/cors.d.ts +48 -0
  167. package/dist/types/nodefony/service/csrf.d.ts +57 -0
  168. package/dist/types/nodefony/service/firewall.d.ts +148 -0
  169. package/dist/types/nodefony/service/oauth2.d.ts +66 -0
  170. package/dist/types/nodefony/service/securityHeaders.d.ts +66 -0
  171. package/dist/types/nodefony/service/tokenService.d.ts +103 -0
  172. package/dist/types/nodefony/service/totp.d.ts +58 -0
  173. package/dist/types/nodefony/service/webAuthn.d.ts +123 -0
  174. package/dist/types/nodefony/service/webhooks.d.ts +160 -0
  175. package/dist/types/nodefony/src/RoleHierarchyWalker.d.ts +21 -0
  176. package/dist/types/nodefony/src/SecuredArea.d.ts +31 -0
  177. package/dist/types/nodefony/src/admin/SecurityAdminApi.d.ts +82 -0
  178. package/dist/types/nodefony/src/admin/WebhookAdminApi.d.ts +30 -0
  179. package/dist/types/nodefony/src/admin/adminAudit.d.ts +27 -0
  180. package/dist/types/nodefony/src/admin/userRevocationCascade.d.ts +31 -0
  181. package/dist/types/nodefony/src/apikey/apiKeyFormat.d.ts +43 -0
  182. package/dist/types/nodefony/src/audit/MemoryAuditStore.d.ts +33 -0
  183. package/dist/types/nodefony/src/audit/auditBridge.d.ts +49 -0
  184. package/dist/types/nodefony/src/audit/auditFilters.d.ts +56 -0
  185. package/dist/types/nodefony/src/audit/auditStoreRegistry.d.ts +37 -0
  186. package/dist/types/nodefony/src/audit/readAuditContext.d.ts +17 -0
  187. package/dist/types/nodefony/src/audit/recordAudit.d.ts +13 -0
  188. package/dist/types/nodefony/src/authenticator/AnonymousAuthenticator.d.ts +26 -0
  189. package/dist/types/nodefony/src/authenticator/ApiKeyAuthenticator.d.ts +74 -0
  190. package/dist/types/nodefony/src/authenticator/ExternalJwtAuthenticator.d.ts +132 -0
  191. package/dist/types/nodefony/src/authenticator/FirewallRealtimeAuthenticator.d.ts +78 -0
  192. package/dist/types/nodefony/src/authenticator/JwtAuthenticator.d.ts +69 -0
  193. package/dist/types/nodefony/src/authenticator/SessionAuthenticator.d.ts +70 -0
  194. package/dist/types/nodefony/src/authenticator/UserPasswordAuthenticator.d.ts +53 -0
  195. package/dist/types/nodefony/src/authenticator/authenticatorRegistry.d.ts +39 -0
  196. package/dist/types/nodefony/src/authenticator/bearer.d.ts +22 -0
  197. package/dist/types/nodefony/src/authenticator/externalSubject.d.ts +27 -0
  198. package/dist/types/nodefony/src/authenticator/peekIssuer.d.ts +31 -0
  199. package/dist/types/nodefony/src/crypto/secretCipher.d.ts +31 -0
  200. package/dist/types/nodefony/src/csp.d.ts +39 -0
  201. package/dist/types/nodefony/src/csrfToken.d.ts +36 -0
  202. package/dist/types/nodefony/src/net/ssrfGuard.d.ts +43 -0
  203. package/dist/types/nodefony/src/oauth/oauthProviderRegistry.d.ts +45 -0
  204. package/dist/types/nodefony/src/oauth/providers/github.d.ts +9 -0
  205. package/dist/types/nodefony/src/oauth/providers/oidc.d.ts +35 -0
  206. package/dist/types/nodefony/src/realtime/UserRealtimeToken.d.ts +62 -0
  207. package/dist/types/nodefony/src/realtime/frameAuthorizer.d.ts +171 -0
  208. package/dist/types/nodefony/src/realtime/realtimeContracts.d.ts +139 -0
  209. package/dist/types/nodefony/src/sessionIdentity.d.ts +20 -0
  210. package/dist/types/nodefony/src/throttle/LoginThrottler.d.ts +68 -0
  211. package/dist/types/nodefony/src/token/AnonymousToken.d.ts +23 -0
  212. package/dist/types/nodefony/src/token/JwtKeystore.d.ts +43 -0
  213. package/dist/types/nodefony/src/token/MemoryTokenStore.d.ts +66 -0
  214. package/dist/types/nodefony/src/token/RemoteJwtVerifier.d.ts +149 -0
  215. package/dist/types/nodefony/src/token/UserToken.d.ts +41 -0
  216. package/dist/types/nodefony/src/token/jwtRuntime.d.ts +28 -0
  217. package/dist/types/nodefony/src/token/secretFile.d.ts +70 -0
  218. package/dist/types/nodefony/src/token/tokenCriteria.d.ts +20 -0
  219. package/dist/types/nodefony/src/token/tokenFilters.d.ts +76 -0
  220. package/dist/types/nodefony/src/token/tokenSort.d.ts +33 -0
  221. package/dist/types/nodefony/src/token/tokenStatus.d.ts +38 -0
  222. package/dist/types/nodefony/src/token/tokenStoreRegistry.d.ts +38 -0
  223. package/dist/types/nodefony/src/totp/MemoryTotpSecretStore.d.ts +43 -0
  224. package/dist/types/nodefony/src/totp/totpCipher.d.ts +9 -0
  225. package/dist/types/nodefony/src/totp/totpCrypto.d.ts +164 -0
  226. package/dist/types/nodefony/src/totp/totpOperations.d.ts +73 -0
  227. package/dist/types/nodefony/src/totp/totpSecretStoreRegistry.d.ts +27 -0
  228. package/dist/types/nodefony/src/voter/RoleVoter.d.ts +25 -0
  229. package/dist/types/nodefony/src/voter/ScopeVoter.d.ts +30 -0
  230. package/dist/types/nodefony/src/voter/voterRegistry.d.ts +33 -0
  231. package/dist/types/nodefony/src/webauthn/MemoryWebAuthnCredentialStore.d.ts +39 -0
  232. package/dist/types/nodefony/src/webauthn/webAuthnCredentialStoreRegistry.d.ts +26 -0
  233. package/dist/types/nodefony/src/webhook/MemoryWebhookStore.d.ts +37 -0
  234. package/dist/types/nodefony/src/webhook/WebhookDispatcher.d.ts +69 -0
  235. package/dist/types/nodefony/src/webhook/webhookCipher.d.ts +8 -0
  236. package/dist/types/nodefony/src/webhook/webhookDelivery.d.ts +28 -0
  237. package/dist/types/nodefony/src/webhook/webhookFilters.d.ts +64 -0
  238. package/dist/types/nodefony/src/webhook/webhookSignature.d.ts +20 -0
  239. package/dist/types/nodefony/src/webhook/webhookSort.d.ts +39 -0
  240. package/dist/types/nodefony/src/webhook/webhookStoreRegistry.d.ts +31 -0
  241. package/docs/api-keys.md +691 -0
  242. package/docs/audit.md +751 -0
  243. package/docs/authenticators.md +487 -0
  244. package/docs/authorization.md +497 -0
  245. package/docs/cors.md +497 -0
  246. package/docs/csrf.md +392 -0
  247. package/docs/external-jwt.md +181 -0
  248. package/docs/firewall.md +546 -0
  249. package/docs/headers.md +616 -0
  250. package/docs/index.md +207 -0
  251. package/docs/lexique.md +190 -0
  252. package/docs/oauth2.md +575 -0
  253. package/docs/obtenir-un-jeton.md +225 -0
  254. package/docs/tokens.md +520 -0
  255. package/docs/totp.md +804 -0
  256. package/docs/webauthn.md +733 -0
  257. package/docs/webhooks.md +1016 -0
  258. package/package.json +83 -0
@@ -0,0 +1,27 @@
1
+ import { decryptSecret, deriveKey, encryptSecret, generateEphemeralKey } from "../crypto/secretCipher.js";
2
+ import { Buffer } from "node:buffer";
3
+ //#region nodefony/src/webhook/webhookCipher.ts
4
+ /**
5
+ * Chiffrement réversible du secret de signature webhook au repos — façade de
6
+ * domaine au-dessus de {@link ../crypto/secretCipher} (calque {@link ../totp/totpCipher}).
7
+ *
8
+ * Le secret de signature (HMAC) doit être **relu** par le serveur à chaque
9
+ * livraison → chiffré (AES-256-GCM), jamais haché. Le contexte HKDF est **propre
10
+ * au domaine webhook** : un blob webhook ne se déchiffre pas avec la clé TOTP, et
11
+ * réciproquement (séparation de domaine cryptographique). Ne pas modifier
12
+ * {@link WEBHOOK_DERIVATION} sous peine de rendre illisibles les secrets stockés.
13
+ */
14
+ /** Contexte HKDF figé du domaine webhook. **Constantes — ne pas changer.** */
15
+ const WEBHOOK_DERIVATION = {
16
+ salt: Buffer.from("nodefony.webhook.hkdf.v1"),
17
+ info: Buffer.from("webhook-secret-encryption")
18
+ };
19
+ /**
20
+ * Dérive la clé AES-256 du domaine webhook via HKDF-SHA256 (RFC 5869).
21
+ * Déterministe (clé lisible cross-pod).
22
+ */
23
+ function deriveWebhookKey(material) {
24
+ return deriveKey(material, WEBHOOK_DERIVATION);
25
+ }
26
+ //#endregion
27
+ export { decryptSecret, deriveWebhookKey, encryptSecret, generateEphemeralKey };
@@ -0,0 +1,102 @@
1
+ import { Buffer } from "node:buffer";
2
+ import { isIP } from "node:net";
3
+ import { request } from "node:https";
4
+ import { request as request$1 } from "node:http";
5
+ //#region nodefony/src/webhook/webhookDelivery.ts
6
+ /**
7
+ * Émetteur HTTP d'une livraison webhook — `node:http(s)` natif (0 dép). Trois
8
+ * propriétés de sécurité par rapport à `fetch` :
9
+ *
10
+ * 1. **Pas de suivi de redirection** : `node:http(s)` ne suit JAMAIS les 3xx
11
+ * (contrairement à `fetch`) → un `302 → 169.254.169.254` ne contourne pas le
12
+ * contrôle SSRF ; le 3xx est rendu tel quel (échec de config).
13
+ * 2. **Pin de l'IP validée** (anti DNS-rebinding) : `lookup` force la connexion TCP
14
+ * vers l'IP déjà contrôlée par `assertPublicUrl`, tout en gardant le hostname
15
+ * pour le SNI/TLS et l'en-tête `Host` → pas de re-résolution entre contrôle et
16
+ * connexion.
17
+ * 3. **Timeout dur** : la requête est détruite au-delà du délai.
18
+ */
19
+ /** Taille max du corps de réponse capturé (observabilité, anti-mémoire). */
20
+ const RESPONSE_BODY_CAP = 2048;
21
+ /**
22
+ * POST le corps signé vers l'URL, en pinnant l'IP validée et sans suivre les
23
+ * redirections. Ne lève jamais : tout échec est encodé dans {@link IDeliveryResult}.
24
+ */
25
+ function deliverWebhook(url, body, headers, opts) {
26
+ let u;
27
+ try {
28
+ u = new URL(url);
29
+ } catch {
30
+ return Promise.resolve({
31
+ ok: false,
32
+ status: null,
33
+ error: "URL invalide"
34
+ });
35
+ }
36
+ const isHttps = u.protocol === "https:";
37
+ if (!isHttps && !(u.protocol === "http:" && opts.allowHttp)) return Promise.resolve({
38
+ ok: false,
39
+ status: null,
40
+ error: `protocole ${u.protocol} non autorisé`
41
+ });
42
+ const pinned = opts.addresses && opts.addresses.length > 0 ? opts.addresses[0] : null;
43
+ const lookup = pinned ? (_hostname, options, callback) => {
44
+ const family = isIP(pinned) === 6 ? 6 : 4;
45
+ if (options.all) callback(null, [{
46
+ address: pinned,
47
+ family
48
+ }], family);
49
+ else callback(null, pinned, family);
50
+ } : void 0;
51
+ const payload = Buffer.from(body, "utf8");
52
+ const requestFn = isHttps ? request : request$1;
53
+ return new Promise((resolve) => {
54
+ let settled = false;
55
+ const done = (r) => {
56
+ if (!settled) {
57
+ settled = true;
58
+ resolve(r);
59
+ }
60
+ };
61
+ const req = requestFn(url, {
62
+ method: "POST",
63
+ headers: {
64
+ ...headers,
65
+ "content-length": String(payload.length)
66
+ },
67
+ lookup
68
+ }, (res) => {
69
+ const status = res.statusCode ?? 0;
70
+ const ok = status >= 200 && status < 300;
71
+ let resBody = "";
72
+ res.on("data", (c) => {
73
+ if (resBody.length < RESPONSE_BODY_CAP) {
74
+ resBody += c.toString("utf8");
75
+ if (resBody.length > RESPONSE_BODY_CAP) resBody = resBody.slice(0, RESPONSE_BODY_CAP);
76
+ }
77
+ });
78
+ res.on("end", () => done({
79
+ ok,
80
+ status,
81
+ error: ok ? null : `HTTP ${status}`,
82
+ responseBody: resBody.length > 0 ? resBody : null
83
+ }));
84
+ res.on("error", (e) => done({
85
+ ok: false,
86
+ status,
87
+ error: e.message,
88
+ responseBody: resBody.length > 0 ? resBody : null
89
+ }));
90
+ });
91
+ req.setTimeout(opts.timeoutMs, () => req.destroy(/* @__PURE__ */ new Error("timeout")));
92
+ req.on("error", (e) => done({
93
+ ok: false,
94
+ status: null,
95
+ error: e.message
96
+ }));
97
+ req.write(payload);
98
+ req.end();
99
+ });
100
+ }
101
+ //#endregion
102
+ export { deliverWebhook };
@@ -0,0 +1,56 @@
1
+ //#region nodefony/src/webhook/webhookFilters.ts
2
+ /**
3
+ * **Le vocabulaire de filtre des endpoints webhook**, en noms PUBLICS — ceux
4
+ * qu'un client écrit dans l'URL (`?enabled=false&event=user.created`).
5
+ *
6
+ * Frère de `WEBHOOK_SORTABLE_FIELDS`, et posé au même endroit pour la même
7
+ * raison : le vocabulaire appartient au propriétaire du contrat, la mécanique de
8
+ * lecture au cœur (`parseFilters`).
9
+ *
10
+ * `event` reste une chaîne LIBRE, et non une énumération : le catalogue
11
+ * d'événements est celui de l'application qui émet, pas du framework. Le refus
12
+ * porte donc sur la forme (paramètre inconnu, valeur mal formée), jamais sur le
13
+ * nom d'un événement — le refuser reviendrait à décider à la place de l'app ce
14
+ * qu'elle a le droit de publier.
15
+ */
16
+ const WEBHOOK_FILTERS = {
17
+ /** `true` = actifs seulement, `false` = désactivés seulement, absent = les deux. */
18
+ enabled: "boolean",
19
+ /** Endpoints abonnés à CET événement (« qui écoute `user.created` ? »). */
20
+ event: "string",
21
+ /** `true` = en échec (`failureCount > 0`), `false` = sains, absent = les deux. */
22
+ failing: "boolean"
23
+ };
24
+ /**
25
+ * **Les facettes des endpoints** — les questions fermées que la console pose à
26
+ * la collection ENTIÈRE pour ses cartes de tête.
27
+ *
28
+ * Chacune est un filtre du contrat, et c'est la règle : une carte affiche un
29
+ * nombre que le tableau doit pouvoir montrer. `failing` recoupe volontairement
30
+ * `active` et `disabled` — un endpoint peut être actif ET en échec — d'où
31
+ * l'interdiction de déduire une facette d'une autre par soustraction.
32
+ */
33
+ const WEBHOOK_FACETS = {
34
+ /** Tous les endpoints configurés. */
35
+ total: {},
36
+ /** Endpoints actifs (le dispatcher leur livre). */
37
+ active: { enabled: true },
38
+ /** Endpoints désactivés — à la main ou par coupe-circuit. */
39
+ disabled: { enabled: false },
40
+ /** Endpoints en échec, actifs ou non. */
41
+ failing: { failing: true }
42
+ };
43
+ /**
44
+ * Ce que l'endpoint de COMPTEURS accepte de filtrer — `WEBHOOK_FILTERS` **moins**
45
+ * les champs que les facettes décomposent (`enabled`, `failing`).
46
+ *
47
+ * Les demander ici rendrait une réponse contradictoire : le total suivrait le
48
+ * filtre pendant que chaque facette l'écraserait par le sien. `event` reste,
49
+ * parce qu'il découpe une AUTRE dimension — « combien d'endpoints écoutent
50
+ * `user.created`, et dans quel état sont-ils ? » est une question cohérente.
51
+ *
52
+ * Un test verrouille l'accord entre cette liste et {@link WEBHOOK_FACETS}.
53
+ */
54
+ const WEBHOOK_STATS_FILTERS = { event: "string" };
55
+ //#endregion
56
+ export { WEBHOOK_FACETS, WEBHOOK_FILTERS, WEBHOOK_STATS_FILTERS };
@@ -0,0 +1,51 @@
1
+ import { createHmac } from "node:crypto";
2
+ import { Buffer } from "node:buffer";
3
+ //#region nodefony/src/webhook/webhookSignature.ts
4
+ /**
5
+ * Signature des webhooks sortants — schéma **Standard Webhooks v1**
6
+ * (standardwebhooks.com), choisi plutôt que RFC 9421 (trop lourd pour un webhook,
7
+ * friction consommateur) ou un HMAC maison (GitHub/Stripe). Symétrique HMAC-SHA256
8
+ * par défaut (`v1`) ; slot asymétrique Ed25519 (`v1a`) réservé.
9
+ *
10
+ * Base signée = `{id}.{timestamp}.{body}` → le `webhook-id` et le
11
+ * `webhook-timestamp` sont **couverts** (anti-replay : le consommateur vérifie le
12
+ * timestamp dans une tolérance et déduplique par id). Le consommateur recalcule le
13
+ * HMAC avec le secret partagé et compare en temps constant.
14
+ */
15
+ const SECRET_PREFIX = "whsec_";
16
+ /**
17
+ * Décode un secret `whsec_<base64>` en octets de clé HMAC. Tolère un secret déjà
18
+ * sans préfixe (base64 brut).
19
+ */
20
+ function parseWebhookSecret(secret) {
21
+ const b64 = secret.startsWith(SECRET_PREFIX) ? secret.slice(6) : secret;
22
+ return Buffer.from(b64, "base64");
23
+ }
24
+ /** Construit la base à signer : `{id}.{timestamp}.{body}` (Standard Webhooks). */
25
+ function buildSignatureBase(id, timestampS, body) {
26
+ return `${id}.${timestampS}.${body}`;
27
+ }
28
+ /**
29
+ * Signe un message webhook (Standard Webhooks `v1`). Retourne la valeur du header
30
+ * `webhook-signature` : `v1,<base64(HMAC-SHA256(secret, base))>`.
31
+ *
32
+ * @param secret - secret `whsec_…` de l'endpoint (en clair).
33
+ * @param id - identifiant du message (`webhook-id`).
34
+ * @param timestampS - horodatage epoch **secondes** (`webhook-timestamp`).
35
+ * @param body - corps JSON sérialisé envoyé.
36
+ */
37
+ function signStandardWebhook(secret, id, timestampS, body) {
38
+ const key = parseWebhookSecret(secret);
39
+ const base = buildSignatureBase(id, timestampS, body);
40
+ return `v1,${createHmac("sha256", key).update(base).digest("base64")}`;
41
+ }
42
+ /** En-têtes de signature Standard Webhooks à poser sur la requête sortante. */
43
+ function webhookSignatureHeaders(secret, id, timestampS, body) {
44
+ return {
45
+ "webhook-id": id,
46
+ "webhook-timestamp": String(timestampS),
47
+ "webhook-signature": signStandardWebhook(secret, id, timestampS, body)
48
+ };
49
+ }
50
+ //#endregion
51
+ export { buildSignatureBase, parseWebhookSecret, signStandardWebhook, webhookSignatureHeaders };
@@ -0,0 +1,48 @@
1
+ //#region nodefony/src/webhook/webhookSort.ts
2
+ /**
3
+ * **Le vocabulaire de tri des endpoints webhook**, en noms PUBLICS — ceux qu'un
4
+ * client écrit dans l'URL (`?order=failureCount:DESC`), jamais des noms de
5
+ * colonne.
6
+ *
7
+ * Il vit ici, chez le propriétaire du contrat (`@nodefony/security`), et non dans
8
+ * chaque backend : c'est ce qui garantit qu'une console offre le même tri, que
9
+ * l'application tourne sur mémoire, SQL ou Mongo. Un store dont le schéma nomme
10
+ * un champ autrement traduit **chez lui** — cf {@link translateWebhookOrderMongo}.
11
+ *
12
+ * - `createdAt` / `updatedAt` — quand l'abonnement a été posé, quand il a bougé ;
13
+ * - `url` — la destination, colonne principale de la console ;
14
+ * - `enabled` — regroupe les abonnements coupés ;
15
+ * - `failureCount` — « qui échoue le plus », l'axe d'exploitation d'un webhook ;
16
+ * - `id` — utile surtout en départage.
17
+ *
18
+ * **Ce qui n'y est PAS, et pourquoi** : `lastDeliveryAt`, `lastDeliveryStatus` et
19
+ * `lastDeliveryError` sont *nullables* — un endpoint qui n'a jamais livré n'a pas
20
+ * de dernière livraison — et le placement des valeurs absentes n'est pas le même
21
+ * d'un moteur à l'autre (PostgreSQL les met en tête d'un tri `DESC`, SQLite,
22
+ * MySQL et le tri en mémoire en queue). Les déclarer offrirait un tri dont
23
+ * l'ordre dépendrait de la base configurée.
24
+ *
25
+ * ⚠️ Cette liste est aussi une **garde de sécurité**, pas seulement une capacité :
26
+ * un nom de champ ne se lie pas en paramètre SQL, il se **concatène** dans le
27
+ * `ORDER BY`. Tout backend qui construit sa requête à la main filtre donc avec
28
+ * elle, en défense en profondeur — sans compter sur le fait que le data plane
29
+ * ait déjà refusé.
30
+ */
31
+ const WEBHOOK_SORTABLE_FIELDS = [
32
+ "createdAt",
33
+ "updatedAt",
34
+ "url",
35
+ "enabled",
36
+ "failureCount",
37
+ "id"
38
+ ];
39
+ /**
40
+ * Ordre contractuel appliqué quand le client n'en demande aucun : les
41
+ * abonnements les plus récents d'abord, départagés par identifiant pour rester
42
+ * **déterministe** à horodatage égal — sans ce tiebreaker, deux endpoints créés
43
+ * dans la même milliseconde peuvent changer de page entre deux appels, et l'un
44
+ * des deux ne jamais apparaître.
45
+ */
46
+ const WEBHOOK_DEFAULT_ORDER = [["createdAt", "DESC"], ["id", "ASC"]];
47
+ //#endregion
48
+ export { WEBHOOK_DEFAULT_ORDER, WEBHOOK_SORTABLE_FIELDS };
@@ -0,0 +1,18 @@
1
+ import { MemoryWebhookStore } from "./MemoryWebhookStore.js";
2
+ //#region nodefony/src/webhook/webhookStoreRegistry.ts
3
+ const factories = /* @__PURE__ */ new Map();
4
+ /** Enregistre (ou remplace) la fabrique d'un store d'endpoints webhook. */
5
+ function registerWebhookStore(name, factory) {
6
+ factories.set(name, factory);
7
+ }
8
+ /** Fabrique d'un store par nom, ou `undefined` si inconnu. */
9
+ function getWebhookStoreFactory(name) {
10
+ return factories.get(name);
11
+ }
12
+ /** Noms enregistrés (validation boot, introspection Studio, tests). */
13
+ function listWebhookStores() {
14
+ return [...factories.keys()];
15
+ }
16
+ registerWebhookStore("memory", () => new MemoryWebhookStore());
17
+ //#endregion
18
+ export { getWebhookStoreFactory, listWebhookStores, registerWebhookStore };
@@ -0,0 +1,157 @@
1
+ import { Kernel, Module } from "nodefony";
2
+ import Firewall from "./nodefony/service/firewall.js";
3
+ import AuthFlow from "./nodefony/service/authFlow.js";
4
+ import TokenService from "./nodefony/service/tokenService.js";
5
+ import AccessTokenVerifierService from "./nodefony/service/accessTokenVerifier.js";
6
+ import Authorization from "./nodefony/service/authorization.js";
7
+ import WebAuthnService from "./nodefony/service/webAuthn.js";
8
+ import OAuth2Service from "./nodefony/service/oauth2.js";
9
+ import ApiKeyService from "./nodefony/service/apiKeys.js";
10
+ import AuditService from "./nodefony/service/auditService.js";
11
+ import TotpService from "./nodefony/service/totp.js";
12
+ import WebhookService from "./nodefony/service/webhooks.js";
13
+ import type { ISecurityConfigInput } from "./nodefony/config/defineModuleConfig.js";
14
+ declare module "nodefony" {
15
+ interface NodefonyModuleConfig {
16
+ "@nodefony/security": ISecurityConfigInput;
17
+ }
18
+ }
19
+ /**
20
+ * `@nodefony/security` — couche de sécurité de Nodefony (refonte 2026, P6).
21
+ *
22
+ * Identité **hybride** : session serveur cookie opaque (BFF) par défaut web/Studio,
23
+ * JWT réservé API/M2M/agents. Pattern **`IAuthenticator`** + registre de fabriques
24
+ * (pluggable), **Zero Trust** par défaut, config type-safe `defineSecurityConfig()`
25
+ * + Zod validée au boot (fail-closed si invalide). Consomme `@nodefony/user`
26
+ * (IUser/IUserProvider/IPasswordVerifier) — jamais l'inverse.
27
+ *
28
+ * Livré : firewall (zones, mode first|all, challenge RFC 7235), authenticators
29
+ * `anonymous`/`userpassword` (Basic RFC 7617)/`session` (BFF, J3), flux
30
+ * login/logout/me (`AuthFlow`, anti-fixation + throttling partagé).
31
+ * Jwt/oauth2/mtls/apikey, CORS, CSRF, autorisation par décorateurs et data
32
+ * plane Studio arrivent aux sessions suivantes (plan J0→J10).
33
+ */
34
+ declare class Security extends Module {
35
+ constructor(kernel: Kernel);
36
+ /**
37
+ * JSON Schema de la config security → data plane admin (config riche Studio).
38
+ * ⚠️ Les VALEURS effectives des secrets (csrf/jwt/oauth) sont redactées côté
39
+ * serveur (`safeConfig`) AVANT envoi — le schéma ne décrit que la structure.
40
+ */
41
+ configSchema(): unknown;
42
+ /**
43
+ * Enregistre les data planes admin **sécurité** (`/nodefony/security/api/*`,
44
+ * P6.14) ET **utilisateur** (`/nodefony/user/api/*`, P6.15) auprès du broker
45
+ * AVANT que framework ne monte les routes (`onReady`). Le data plane user est
46
+ * DÉFINI dans `@nodefony/user` (propriétaire du domaine) mais monté ici, car
47
+ * `@nodefony/user` est une lib pure non-bootable et `security` en dépend déjà.
48
+ * No-op si le broker est absent (Studio non chargé) ou déjà enregistré.
49
+ */
50
+ onKernelBoot(): Promise<this>;
51
+ }
52
+ export default Security;
53
+ export { Firewall };
54
+ export { AuthFlow };
55
+ export { TokenService };
56
+ export { AccessTokenVerifierService };
57
+ export { RemoteJwtVerifier } from "./nodefony/src/token/RemoteJwtVerifier.js";
58
+ export type { ITrustedIssuer, IRemoteJwtVerifierOptions, } from "./nodefony/src/token/RemoteJwtVerifier.js";
59
+ export { ApiKeyService };
60
+ export { Authorization };
61
+ export { WebAuthnService };
62
+ export { OAuth2Service };
63
+ export { AuditService };
64
+ export { TotpService };
65
+ export { WebhookService };
66
+ export type { IWebhookRegisterInput, IWebhookSecretReveal, IWebhookDeliveryPolicy, } from "./nodefony/service/webhooks.js";
67
+ export type { ISafeUser, ILoginOutcome } from "./nodefony/service/authFlow.js";
68
+ export type { ITokenResponse } from "./nodefony/service/tokenService.js";
69
+ export type { IApiKeyView, IApiKeyCreated, IApiKeyCapabilities, ICreateApiKeyOptions, } from "./nodefony/contracts/IApiKey.js";
70
+ export { ApiKeyError } from "./nodefony/errors/ApiKeyError.js";
71
+ export { SecuredArea } from "./nodefony/src/SecuredArea.js";
72
+ export { Csrf } from "./nodefony/service/csrf.js";
73
+ export { CsrfTokenManager } from "./nodefony/src/csrfToken.js";
74
+ export { Cors } from "./nodefony/service/cors.js";
75
+ export { SecurityHeaders } from "./nodefony/service/securityHeaders.js";
76
+ export { RoleHierarchyWalker } from "./nodefony/src/RoleHierarchyWalker.js";
77
+ export { AnonymousToken } from "./nodefony/src/token/AnonymousToken.js";
78
+ export { UserToken } from "./nodefony/src/token/UserToken.js";
79
+ export { RoleVoter } from "./nodefony/src/voter/RoleVoter.js";
80
+ export { ScopeVoter } from "./nodefony/src/voter/ScopeVoter.js";
81
+ export { registerVoterFactory, listVoterFactories, } from "./nodefony/src/voter/voterRegistry.js";
82
+ export type { VoterFactory, IVoterFactoryContext, } from "./nodefony/src/voter/voterRegistry.js";
83
+ export { AnonymousAuthenticator } from "./nodefony/src/authenticator/AnonymousAuthenticator.js";
84
+ export { UserPasswordAuthenticator } from "./nodefony/src/authenticator/UserPasswordAuthenticator.js";
85
+ export { SessionAuthenticator } from "./nodefony/src/authenticator/SessionAuthenticator.js";
86
+ export { JwtAuthenticator } from "./nodefony/src/authenticator/JwtAuthenticator.js";
87
+ export { ApiKeyAuthenticator } from "./nodefony/src/authenticator/ApiKeyAuthenticator.js";
88
+ export type { IApiKeyAuthenticatorRuntime } from "./nodefony/src/authenticator/ApiKeyAuthenticator.js";
89
+ export { ExternalJwtAuthenticator } from "./nodefony/src/authenticator/ExternalJwtAuthenticator.js";
90
+ export type { ExternalSubjectPolicy, IExternalJwtAuthenticatorOptions, } from "./nodefony/src/authenticator/ExternalJwtAuthenticator.js";
91
+ export { peekIssuer } from "./nodefony/src/authenticator/peekIssuer.js";
92
+ export { generateApiKey, parseApiKey, hashApiKey, looksLikeApiKey, } from "./nodefony/src/apikey/apiKeyFormat.js";
93
+ export type { IGeneratedApiKey, IParsedApiKey, } from "./nodefony/src/apikey/apiKeyFormat.js";
94
+ export { registerAuthenticatorFactory, getAuthenticatorFactory, listAuthenticatorFactories, } from "./nodefony/src/authenticator/authenticatorRegistry.js";
95
+ export type { AuthenticatorFactory, IAuthenticatorFactoryContext, } from "./nodefony/src/authenticator/authenticatorRegistry.js";
96
+ export { MemoryTokenStore } from "./nodefony/src/token/MemoryTokenStore.js";
97
+ export type { TokenStoreSnapshot } from "./nodefony/src/token/MemoryTokenStore.js";
98
+ export { TOKEN_SORTABLE_FIELDS, TOKEN_DEFAULT_ORDER, } from "./nodefony/src/token/tokenSort.js";
99
+ export { tokenStatusCriteria } from "./nodefony/src/token/tokenCriteria.js";
100
+ export { tokenStatusOf, matchesTokenStatus, } from "./nodefony/src/token/tokenStatus.js";
101
+ export type { ITokenLifetime } from "./nodefony/src/token/tokenStatus.js";
102
+ export { TOKEN_FACETS } from "./nodefony/src/token/tokenFilters.js";
103
+ export type { ITokenCounts } from "./nodefony/src/token/tokenFilters.js";
104
+ export { JwtKeystore } from "./nodefony/src/token/JwtKeystore.js";
105
+ export { resolveJwtRuntime } from "./nodefony/src/token/jwtRuntime.js";
106
+ export type { IJwtRuntime } from "./nodefony/src/token/jwtRuntime.js";
107
+ export { registerTokenStore, getTokenStoreFactory, listTokenStores, } from "./nodefony/src/token/tokenStoreRegistry.js";
108
+ export type { TokenStoreFactory, ITokenStoreFactoryContext, } from "./nodefony/src/token/tokenStoreRegistry.js";
109
+ export { MemoryWebAuthnCredentialStore } from "./nodefony/src/webauthn/MemoryWebAuthnCredentialStore.js";
110
+ export type { WebAuthnStoreSnapshot } from "./nodefony/src/webauthn/MemoryWebAuthnCredentialStore.js";
111
+ export { registerWebAuthnStore, getWebAuthnStoreFactory, listWebAuthnStores, } from "./nodefony/src/webauthn/webAuthnCredentialStoreRegistry.js";
112
+ export type { WebAuthnStoreFactory, IWebAuthnStoreFactoryContext, } from "./nodefony/src/webauthn/webAuthnCredentialStoreRegistry.js";
113
+ export type { IWebAuthnUser, IWebAuthnAssertionResult, } from "./nodefony/service/webAuthn.js";
114
+ export type { IWebAuthnCredential } from "./nodefony/contracts/IWebAuthnCredential.js";
115
+ export type { IWebAuthnCredentialStore, IWebAuthnCredentialSummary, IWebAuthnListQuery, WebAuthnAuthUpdate, } from "./nodefony/contracts/IWebAuthnCredentialStore.js";
116
+ export type { IOAuthAuthorization } from "./nodefony/service/oauth2.js";
117
+ export type { IOAuthProvider } from "./nodefony/contracts/IOAuthProvider.js";
118
+ export { registerOAuthProvider, getOAuthProviderFactory, listOAuthProviders, } from "./nodefony/src/oauth/oauthProviderRegistry.js";
119
+ export type { OAuthProviderFactory, IOAuthProviderContext, } from "./nodefony/src/oauth/oauthProviderRegistry.js";
120
+ export { MemoryAuditStore } from "./nodefony/src/audit/MemoryAuditStore.js";
121
+ export type { AuditStoreSnapshot } from "./nodefony/src/audit/MemoryAuditStore.js";
122
+ export type { IAuditEvent, IAuditEventDraft, IAuditEventFlags, AuditCategory, AuditOutcome, } from "./nodefony/contracts/IAuditEvent.js";
123
+ export type { IAuditStore, IAuditSink, IAuditListQuery, } from "./nodefony/contracts/IAuditStore.js";
124
+ export { registerAuditStore, getAuditStoreFactory, listAuditStores, } from "./nodefony/src/audit/auditStoreRegistry.js";
125
+ export type { AuditStoreFactory, IAuditStoreFactoryContext, } from "./nodefony/src/audit/auditStoreRegistry.js";
126
+ export { recordAudit } from "./nodefony/src/audit/recordAudit.js";
127
+ export { readAuditContext } from "./nodefony/src/audit/readAuditContext.js";
128
+ export type { AuditContextInfo } from "./nodefony/src/audit/readAuditContext.js";
129
+ export { createSecurityAdminApi, registerSecurityAdminApi, parseAuditQuery, } from "./nodefony/src/admin/SecurityAdminApi.js";
130
+ export { MemoryTotpSecretStore } from "./nodefony/src/totp/MemoryTotpSecretStore.js";
131
+ export type { TotpStoreSnapshot } from "./nodefony/src/totp/MemoryTotpSecretStore.js";
132
+ export { registerTotpStore, getTotpStoreFactory, listTotpStores, } from "./nodefony/src/totp/totpSecretStoreRegistry.js";
133
+ export type { TotpStoreFactory, ITotpStoreFactoryContext, } from "./nodefony/src/totp/totpSecretStoreRegistry.js";
134
+ export type { ITotpSecret } from "./nodefony/contracts/ITotpSecret.js";
135
+ export type { ITotpSecretStore, ITotpEnrollmentSummary, ITotpListQuery, TotpSecretUpdate, } from "./nodefony/contracts/ITotpSecretStore.js";
136
+ export type { ITotpEnrollment, ITotpActivation, ITotpStatus, ITotpLoginResult, } from "./nodefony/src/totp/totpOperations.js";
137
+ export { beginTotpEnrollment, confirmTotpEnrollment, verifyTotpLogin, disableTotp, totpStatus, } from "./nodefony/src/totp/totpOperations.js";
138
+ export type { ITotpDeps } from "./nodefony/src/totp/totpOperations.js";
139
+ export { totpCode, base32Decode } from "./nodefony/src/totp/totpCrypto.js";
140
+ export { deriveTotpKey } from "./nodefony/src/totp/totpCipher.js";
141
+ export { MemoryWebhookStore } from "./nodefony/src/webhook/MemoryWebhookStore.js";
142
+ export { WEBHOOK_SORTABLE_FIELDS, WEBHOOK_DEFAULT_ORDER, } from "./nodefony/src/webhook/webhookSort.js";
143
+ export { registerWebhookStore, getWebhookStoreFactory, listWebhookStores, } from "./nodefony/src/webhook/webhookStoreRegistry.js";
144
+ export type { WebhookStoreFactory, IWebhookStoreFactoryContext, } from "./nodefony/src/webhook/webhookStoreRegistry.js";
145
+ export type { IWebhookEndpoint, WebhookEndpointUpdate, WebhookEndpointSummary, } from "./nodefony/contracts/IWebhookEndpoint.js";
146
+ export type { IWebhookListQuery, IWebhookStore, } from "./nodefony/contracts/IWebhookStore.js";
147
+ export { assertPublicUrl, isBlockedAddress, } from "./nodefony/src/net/ssrfGuard.js";
148
+ export type { IAssertPublicUrlOptions, IPublicUrlResult, DnsResolver, } from "./nodefony/src/net/ssrfGuard.js";
149
+ export { deriveKey, encryptSecret, decryptSecret, generateEphemeralKey, } from "./nodefony/src/crypto/secretCipher.js";
150
+ export type { IKeyDerivation } from "./nodefony/src/crypto/secretCipher.js";
151
+ export { defineSecurityConfig, securityConfigJsonSchema, } from "./nodefony/config/defineModuleConfig.js";
152
+ export type { ISecurityConfig, ISecurityConfigInput, ISecurityAreaConfig, } from "./nodefony/config/defineModuleConfig.js";
153
+ export { LoginThrottler } from "./nodefony/src/throttle/LoginThrottler.js";
154
+ export type { ILoginThrottleOptions } from "./nodefony/src/throttle/LoginThrottler.js";
155
+ export { AuthenticationError, AccessDeniedError, ThrottledError, UnverifiableTokenError, InvalidTargetError, CsrfError, SsrfError, } from "./nodefony/errors/index.js";
156
+ export { VoterVote } from "./nodefony/contracts/index.js";
157
+ export type { IToken, IAuthenticator, ISecuredArea, IFirewall, IAccessVoter, IAuthorizationService, IAccessTokenRecord, ITokenStore, ITokenListQuery, ITokenUsage, IResourcePermission, TokenRevokeReason, TokenStatus, IJwtKeystore, IJwtSigningKey, } from "./nodefony/contracts/index.js";
@@ -0,0 +1,24 @@
1
+ import { CliKernel, Command } from "nodefony";
2
+ /**
3
+ * `nodefony security:secrets` — génère les clés de chiffrement attendues par le
4
+ * module security (TOTP, webhooks, CSRF) au bon format (32 octets aléatoires,
5
+ * base64) et guide le câblage en 3 FICHIERS (.env → env.ts → nodefony.config.ts).
6
+ * Réponse directe aux warnings « clé ÉPHÉMÈRE générée » du boot.
7
+ *
8
+ * DX anti-confusion (vécu : blocs collés dans le shell → parse error zsh) :
9
+ * - chaque étape nomme son FICHIER et rappelle que rien ne se tape au terminal ;
10
+ * - les étapes déjà faites sont DÉTECTÉES (grep des fichiers du projet) et
11
+ * affichées `✓` au lieu de redemander un collage ;
12
+ * - `--write` écrit le `.env` (fichier local gitignoré) : ajoute uniquement les
13
+ * clés ABSENTES, ne remplace jamais une valeur existante (rotation = manuelle).
14
+ * `env.ts` et `nodefony.config.ts` ne sont JAMAIS modifiés (code de l'app).
15
+ */
16
+ declare class SecuritySecrets extends Command {
17
+ #private;
18
+ constructor(cli: CliKernel);
19
+ generate(opts: {
20
+ json?: boolean;
21
+ write?: boolean;
22
+ }): Promise<this>;
23
+ }
24
+ export default SecuritySecrets;
@@ -0,0 +1,44 @@
1
+ import { CliKernel, Command } from "nodefony";
2
+ /**
3
+ * Traduit `--ttl` en secondes, ou rend l'erreur à afficher.
4
+ *
5
+ * Exportée pour être ÉPROUVÉE : c'est une fonction pure dont chaque verdict est
6
+ * binaire, et dont l'échec — une durée acceptée alors qu'elle est aberrante —
7
+ * ne se verrait qu'au moment où un jeton refuse de mourir.
8
+ *
9
+ * @param raw - la valeur telle que tapée, ou rien
10
+ * @returns les secondes, `undefined` si rien n'est demandé, une `Error` sinon
11
+ */
12
+ export declare function ttlSeconds(raw: string | undefined): number | undefined | Error;
13
+ /**
14
+ * `nodefony security:token` — émet un jeton d'accès pour une porte de cette
15
+ * application (la porte MCP par défaut).
16
+ *
17
+ * **Pourquoi une commande, et pas un `curl`.** Le jeton s'obtenait par un appel
18
+ * au grant : trouver l'URL, composer un JSON, y mettre un mot de passe en clair
19
+ * dans l'historique du shell, et surtout AVOIR un serveur en marche. Personne ne
20
+ * fait ça deux fois. Ici l'application SIGNE elle-même — elle possède la clé —
21
+ * donc : pas de serveur, pas de mot de passe, pas de réseau.
22
+ *
23
+ * **L'audience est celle de la porte, et ce n'est pas un détail** (RFC 8707) :
24
+ * un jeton d'audience différente est refusé, à juste titre — c'est toute la
25
+ * raison d'être de la liaison d'audience. La commande la vise d'elle-même,
26
+ * `--resource` ne sert qu'à en viser une autre.
27
+ *
28
+ * Suit `security:secrets` : `--write` pose la valeur là où l'AGENT la lit
29
+ * (gitignoré), jamais dans le `.env` commité, et ne remplace jamais une valeur
30
+ * existante — une rotation est un geste explicite.
31
+ */
32
+ declare class SecurityToken extends Command {
33
+ #private;
34
+ constructor(cli: CliKernel);
35
+ generate(identifierArg: string | undefined, opts: {
36
+ scope?: string;
37
+ ttl?: string;
38
+ agent?: string;
39
+ resource?: string;
40
+ write?: boolean;
41
+ json?: boolean;
42
+ }): Promise<this>;
43
+ }
44
+ export default SecurityToken;
@@ -0,0 +1,28 @@
1
+ import { CliKernel, Command } from "nodefony";
2
+ /**
3
+ * `nodefony security:user:add [identifier]` — crée un compte utilisateur via le
4
+ * service applicatif `users` (hash Argon2id fait par `UserService.createUser`,
5
+ * jamais de mot de passe stocké en clair).
6
+ *
7
+ * Identifiant : en argument, ou DEMANDÉ quand un terminal est là — la commande
8
+ * est proposée au menu, où personne ne peut taper d'argument. Hors terminal
9
+ * (CI, script), l'absence reste une erreur, avec la ligne exacte à taper.
10
+ *
11
+ * Mot de passe : `--password` (visible dans l'historique shell — accepté pour
12
+ * les scripts) ou PROMPT MASQUÉ en TTY (recommandé). Rôles : `--roles a,b,c`
13
+ * (CSV) ou `--admin` (raccourci `ROLE_ADMIN + ROLE_NODEFONY_ADMIN` → accès
14
+ * console Studio). Défaut : `ROLE_USER`.
15
+ *
16
+ * Le service `users` est posé par l'APPLICATION (cf `provisionUsers` du
17
+ * template d'app) — absent = message actionnable, pas de stack.
18
+ */
19
+ declare class SecurityUserAdd extends Command {
20
+ #private;
21
+ constructor(cli: CliKernel);
22
+ generate(identifierArg: string | undefined, opts: {
23
+ password?: string;
24
+ roles?: string;
25
+ admin?: boolean;
26
+ }): Promise<this>;
27
+ }
28
+ export default SecurityUserAdd;
@@ -0,0 +1,25 @@
1
+ import { CliKernel, Command } from "nodefony";
2
+ /**
3
+ * `nodefony security:user:delete <identifiant>` — retire un compte.
4
+ *
5
+ * **Un geste destructeur ne se fait pas sur une frappe.** La commande MONTRE ce
6
+ * qu'elle va supprimer (identifiant, rôles, identifiant interne), puis demande
7
+ * une confirmation. Hors terminal, elle exige `--yes` : un script qui supprime
8
+ * doit le dire dans sa ligne, jamais l'obtenir d'un prompt sauté.
9
+ *
10
+ * **Garde-fou anti-lockout** : le DERNIER administrateur actif ne se supprime
11
+ * pas. Sans lui, plus personne n'accède à la console d'administration, et le
12
+ * seul recours est une écriture directe en base. Le même garde-fou existe côté
13
+ * data plane (`UserAdminApi`) — ici il est constaté, pas supposé.
14
+ *
15
+ * La révocation des sessions et jetons du compte suit d'elle-même : la
16
+ * suppression émet `onUserRevoked`, auquel `@nodefony/security` réagit en
17
+ * éjectant sessions et jetons. Rien à faire de plus ici.
18
+ */
19
+ declare class SecurityUserDelete extends Command {
20
+ constructor(cli: CliKernel);
21
+ generate(identifierArg: string | undefined, opts: {
22
+ yes?: boolean;
23
+ }): Promise<this>;
24
+ }
25
+ export default SecurityUserDelete;
@@ -0,0 +1,28 @@
1
+ import { CliKernel, Command } from "nodefony";
2
+ /**
3
+ * `nodefony security:user:list` — qui a un compte dans cette application.
4
+ *
5
+ * **Lister n'est pas une fuite, et c'était la question.** Cette commande
6
+ * s'exécute sur la machine qui possède déjà la base : elle ne divulgue rien que
7
+ * son utilisateur ne puisse lire avec un client SQL. Ce qui serait dangereux est
8
+ * ailleurs, et gardé ici : **jamais le hachage du mot de passe**, jamais les
9
+ * secrets de second facteur, jamais les jetons d'un fournisseur social. La
10
+ * sortie est composée champ par champ — un `console.table(user)` livrerait le
11
+ * credential, parce que le repository, lui, le voit.
12
+ *
13
+ * Le contraire — ne pas pouvoir lister — a un coût réel : on crée un compte, on
14
+ * ne voit pas ce qu'on a créé, et on recommence en doublon.
15
+ *
16
+ * La pagination est NATIVE (`users.listPage`) : jamais un `find()` complet
17
+ * ramené en mémoire, indolore sur trois comptes et fatal sur cent mille.
18
+ */
19
+ declare class SecurityUserList extends Command {
20
+ constructor(cli: CliKernel);
21
+ generate(opts: {
22
+ query?: string;
23
+ role?: string;
24
+ limit?: string;
25
+ json?: boolean;
26
+ }): Promise<this>;
27
+ }
28
+ export default SecurityUserList;