@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,82 @@
1
+ import type { Container } from "nodefony";
2
+ import type { IAdminApi, IAdminRegistry } from "nodefony";
3
+ import type { ITokenListQuery } from "../../contracts/ITokenStore.js";
4
+ import type { IAuditListQuery } from "../../contracts/IAuditStore.js";
5
+ /**
6
+ * Traduit la query string admin en {@link ITokenListQuery} bornée (`limit` par
7
+ * défaut 50, cap 200 ; pagination, tri et filtres de {@link TOKEN_FILTERS}).
8
+ *
9
+ * **UN SEUL traducteur par dimension, jamais deux.** Pour la page et le tri,
10
+ * `parsePageQuery` ; pour les filtres, `parseFilters`. En appeler un second sans
11
+ * son allowlist ferait refuser en 400 ce que le premier venait d'accepter, et
12
+ * aucun test unitaire ne le verrait (chaque appel est correct isolément).
13
+ *
14
+ * Un filtre inconnu ou mal formé est désormais **refusé** (400) au lieu d'être
15
+ * ignoré : `?status=revoqué` rendait la liste ENTIÈRE, que la console affichait
16
+ * comme le résultat du filtre demandé.
17
+ *
18
+ * @param query - `request.query` du broker admin.
19
+ * @param sortable - champs que le backend branché sait trier ; un `?order=`
20
+ * portant autre chose est refusé en 400 par le traducteur. Liste vide (store à
21
+ * curseur, store absent) ⇒ tout tri est refusé, ce qui est la vérité du backend.
22
+ */
23
+ export declare function parseTokenListQuery(query: Readonly<Record<string, string | string[]>>, sortable: readonly string[]): ITokenListQuery;
24
+ /**
25
+ * Traduit la query string admin en {@link IAuditListQuery} typée — pagination
26
+ * par curseur, plus les filtres de {@link AUDIT_FILTERS}.
27
+ *
28
+ * Un filtre inconnu ou mal formé est **refusé** (400). Il était auparavant
29
+ * ignoré, au nom de la robustesse de la console : mais un journal d'audit rendu
30
+ * ENTIER à qui demandait `?outcome=deneid` n'est pas robuste — c'est la pire
31
+ * réponse possible à un auditeur, qui lit l'absence de refus comme l'absence
32
+ * d'incident. Le typage suit la même source : les valeurs viennent de la spec,
33
+ * il n'y a plus de `as AuditCategory` à écrire ici.
34
+ *
35
+ * Le `limit` est **toujours posé** (défaut {@link AUDIT_DEFAULT_LIMIT}, cap du
36
+ * traducteur) : le contrat de page n'admet pas « tout » ; le store applique en
37
+ * plus son propre plafond, l'appelant ne peut donc pas s'en servir pour tirer un
38
+ * journal entier.
39
+ *
40
+ * @param query - `request.query` du broker admin.
41
+ * @returns filtre prêt pour `auditService.listPage`.
42
+ * @throws `PageQueryError` (400) sur un filtre inconnu ou mal formé.
43
+ */
44
+ export declare function parseAuditQuery(query: Readonly<Record<string, string | string[]>>): IAuditListQuery;
45
+ /**
46
+ * Producteur admin (`IAdminApi`) du module sécurité — data plane consommé par
47
+ * Studio (section Sécurité, P6.15) :
48
+ *
49
+ * - `GET /nodefony/security/api/audit/events` — page filtrée du journal d'audit
50
+ * (P6.14 Lot 3 ; `?category&outcome&actor&action&requestId&since&until&limit&cursor`),
51
+ * du plus récent au plus ancien, pagination par curseur (`cursor` / `nextCursor`).
52
+ * - `GET /nodefony/security/api/firewall` — introspection du firewall (zones,
53
+ * authenticators montés, défenses) — état RUNTIME, secrets exclus.
54
+ * - `GET /nodefony/security/api/roleHierarchy` — hiérarchie de rôles + résolution.
55
+ * - `GET /nodefony/security/api/apikeys` — toutes les clés API (gouvernance),
56
+ * `GET …/apikeys/status` — backend du token store (« où on écrit »),
57
+ * `POST …/apikeys/{id}/revoke` — révocation par id (réponse à incident).
58
+ * - `GET …/users/{id}/passkeys` + `DELETE …/users/{id}/passkeys/{credentialId}`
59
+ * — passkeys d'un utilisateur (vue admin sans clé publique) + révocation
60
+ * owner-scopée (reset facteur fort, audité).
61
+ * - `GET …/users/{id}/totp` + `POST …/users/{id}/totp/disable` — état du 2FA
62
+ * TOTP + désactivation (reset facteur fort, audité). Pas d'enrôlement
63
+ * cross-user (le secret se scanne sur l'appareil de l'utilisateur).
64
+ *
65
+ * **RBAC `ROLE_NODEFONY_ADMIN`** (appliqué par le broker, 403 sinon) — la console
66
+ * sécurité ne se consulte qu'en administrateur. Handlers **lazy** : ils résolvent
67
+ * `auditService`/`firewall` à la requête (service désactivable → 503), jamais au
68
+ * montage. Le namespace `"security"` est distinct des routes classiques
69
+ * `/nodefony/security/api/keys` (P6.12) — paths disjoints, zéro collision.
70
+ *
71
+ * @param container - container du kernel (résolution lazy des services).
72
+ */
73
+ export declare function createSecurityAdminApi(container: Container): IAdminApi;
74
+ /**
75
+ * Enregistre le producteur admin sécurité sur le broker — **idempotent** (no-op
76
+ * si déjà monté). À appeler au `onKernelBoot` du module (avant le montage des
77
+ * routes par framework à `onKernelReady`). Calque `registerOrmAdminApi`.
78
+ *
79
+ * @param registry - broker admin (`container.get("adminBroker")`).
80
+ * @param container - container du kernel (capturé par le handler lazy).
81
+ */
82
+ export declare function registerSecurityAdminApi(registry: IAdminRegistry, container: Container): void;
@@ -0,0 +1,30 @@
1
+ import type { Container, IAdminEndpoint } from "nodefony";
2
+ import type { IWebhookListQuery } from "../../contracts/IWebhookStore.js";
3
+ /**
4
+ * Traduit la query string admin en {@link IWebhookListQuery} **bornée**
5
+ * (`limit` défaut 50, cap 200 ; pagination, tri, et les filtres de
6
+ * {@link WEBHOOK_FILTERS}). Pagination **offset uniquement** : les trois stores
7
+ * webhook (memory/drizzle/mongoose) sont offset-pur — aucun curseur ici.
8
+ *
9
+ * **UN SEUL traducteur par dimension, jamais deux** : `parsePageQuery` pour la
10
+ * page et le tri, `parseFilters` pour les filtres. En appeler un second sans son
11
+ * allowlist ferait refuser en 400 ce que le premier venait d'accepter, et aucun
12
+ * test unitaire ne le verrait.
13
+ *
14
+ * Un filtre inconnu ou mal formé est **refusé** (400) et non plus ignoré :
15
+ * `?enabled=oui` rendait la liste entière, lue comme « aucun endpoint désactivé ».
16
+ *
17
+ * @param query - `request.query` du broker admin.
18
+ * @param sortable - champs que le backend branché sait trier ; un `?order=`
19
+ * portant autre chose est refusé en 400 par le traducteur.
20
+ */
21
+ export declare function parseWebhookListQuery(query: Readonly<Record<string, string | string[]>>, sortable: readonly string[]): IWebhookListQuery;
22
+ /**
23
+ * Construit les endpoints admin webhook, à **spreader** dans les
24
+ * `adminEndpoints()` du producteur `security`. Les handlers résolvent le service
25
+ * `webhooks` **lazy** (à la requête) → un service désactivé/absent rend 503 (ou
26
+ * un état honnête en lecture), jamais une erreur au montage.
27
+ *
28
+ * @param container - container du kernel (capturé par les handlers lazy).
29
+ */
30
+ export declare function webhookAdminEndpoints(container: Container): IAdminEndpoint[];
@@ -0,0 +1,27 @@
1
+ import type { Container } from "nodefony";
2
+ import type { IAuditEventDraft } from "../../contracts/IAuditEvent.js";
3
+ /**
4
+ * Helpers d'audit PARTAGÉS par les producteurs admin du module sécurité
5
+ * (`SecurityAdminApi`, `WebhookAdminApi`…). Extraits dans leur propre fichier
6
+ * pour être consommés par plusieurs producteurs SANS créer de cycle d'import
7
+ * (un producteur composé ne ré-importe pas le producteur qui le compose).
8
+ */
9
+ /**
10
+ * Identité de l'admin appelant (label d'audit) — duck-typing prudent sur
11
+ * l'`IUser` projeté dans `IAdminRequest.user` (ALS du firewall). Repli
12
+ * `"admin"` (libellé d'audit, jamais une décision d'autorisation).
13
+ *
14
+ * @param user - `request.user` du broker admin.
15
+ * @returns un libellé d'identité stable, jamais un secret.
16
+ */
17
+ export declare function adminActor(user: unknown): string;
18
+ /**
19
+ * Émet un événement d'audit pour une mutation admin (best-effort,
20
+ * fire-and-forget) — l'audit ne doit jamais bloquer ni faire échouer l'action.
21
+ * No-op si le service `auditService` est absent. Couplage structurel : `record`
22
+ * lu défensivement (jamais d'import de la classe concrète).
23
+ *
24
+ * @param container - container du kernel.
25
+ * @param draft - événement (sans `id`/`ts`, posés par le service).
26
+ */
27
+ export declare function auditAdmin(container: Container, draft: IAuditEventDraft): void;
@@ -0,0 +1,31 @@
1
+ import type { Container } from "nodefony";
2
+ import { type IUserRevokedEvent } from "@nodefony/user";
3
+ /** Bus d'événements minimal (kernel) capable d'abonner un handler. */
4
+ interface KernelListenerLike {
5
+ on(event: string, handler: (...args: unknown[]) => void): unknown;
6
+ }
7
+ /**
8
+ * Cascade de révocation déclenchée par {@link USER_REVOKED_EVENT} : éjecte
9
+ * **immédiatement** les artefacts d'accès du porteur — ses **sessions** (http,
10
+ * `destroyByUser`) et ses **jetons/PAT** (`tokenStore.revokeAllForSubject`,
11
+ * seuil `invalidBefore`). Best-effort par brique (une indispo n'empêche pas
12
+ * l'autre) — l'accès était DÉJÀ neutralisé par le re-fetch des authenticators,
13
+ * cette cascade est de la **propreté + défense en profondeur**, jamais l'unique
14
+ * rempart. `tenantId` du payload est réservé (scoping non câblé en mono-tenant).
15
+ *
16
+ * @param container - container du kernel (résolution lazy de `sessions`/`tokenStore`).
17
+ * @param event - charge utile de l'événement (porteur + raison).
18
+ * @param now - horloge (epoch ms) injectable pour les tests.
19
+ */
20
+ export declare function cascadeUserRevocation(container: Container, event: IUserRevokedEvent, now?: number): Promise<void>;
21
+ /**
22
+ * Abonne la cascade au bus kernel. À appeler au `onKernelBoot` d'un module
23
+ * bootable (ici `@nodefony/security`). **Extensible** : tout autre module
24
+ * (webhooks…) peut s'abonner au MÊME `USER_REVOKED_EVENT` pour ses propres
25
+ * artefacts, sans toucher à ce fichier.
26
+ *
27
+ * @param kernel - bus d'événements (kernel) exposant `on`.
28
+ * @param container - container capturé par le handler.
29
+ */
30
+ export declare function registerUserRevocationCascade(kernel: KernelListenerLike, container: Container): void;
31
+ export {};
@@ -0,0 +1,43 @@
1
+ /** Hash au repos d'une clé présentée (token entier) — `sha256` hex. */
2
+ export declare function hashApiKey(token: string): string;
3
+ /** Résultat d'une génération de clé — le `token` n'est disponible qu'ICI (1×). */
4
+ export interface IGeneratedApiKey {
5
+ /** Token complet EN CLAIR — à afficher une seule fois, jamais re-dérivable. */
6
+ token: string;
7
+ /** Identifiant public (8 car.) — composant de {@link IGeneratedApiKey.publicPrefix}. */
8
+ pubid: string;
9
+ /** Préfixe public affichable (`<prefix>_<pubid>`) → `record.prefix`. */
10
+ publicPrefix: string;
11
+ /** Hash au repos (`sha256` hex) → `record.secretHash`. */
12
+ secretHash: string;
13
+ }
14
+ /**
15
+ * Génère une nouvelle clé API cryptographiquement aléatoire.
16
+ *
17
+ * @param prefix - marque applicative (`apiKeys.prefix`).
18
+ * @returns le token clair + ses dérivés publics/persistants.
19
+ */
20
+ export declare function generateApiKey(prefix: string): IGeneratedApiKey;
21
+ /** Décomposition validée d'une clé présentée. */
22
+ export interface IParsedApiKey {
23
+ pubid: string;
24
+ publicPrefix: string;
25
+ /** Hash de lookup (`findByHash`) — `sha256` du token entier présenté. */
26
+ secretHash: string;
27
+ }
28
+ /**
29
+ * Test **bon marché** (préfixe seul) — discrimine un PAT d'un JWT à l'`supports()`
30
+ * de l'authenticator, sans calculer le checksum.
31
+ */
32
+ export declare function looksLikeApiKey(token: string, prefix: string): boolean;
33
+ /**
34
+ * Valide la **forme** d'une clé présentée et en dérive le hash de lookup —
35
+ * **sans aucun accès au store**. Rejette (→ `null`) un préfixe absent, une
36
+ * longueur incorrecte, un charset non base64url ou un **CRC invalide** : autant
37
+ * de requêtes qui n'atteignent jamais la base (anti-DoS).
38
+ *
39
+ * @param token - valeur brute présentée (after `Bearer `).
40
+ * @param prefix - marque applicative attendue.
41
+ * @returns la décomposition + le `secretHash` de lookup, ou `null` si malformée.
42
+ */
43
+ export declare function parseApiKey(token: string, prefix: string): IParsedApiKey | null;
@@ -0,0 +1,33 @@
1
+ import type { IPage } from "nodefony";
2
+ import type { IAuditEvent } from "../../contracts/IAuditEvent.js";
3
+ import type { IAuditListQuery, IAuditStore } from "../../contracts/IAuditStore.js";
4
+ /** Instantané sérialisable du journal mémoire (persistance fichier + tests). */
5
+ export interface AuditStoreSnapshot {
6
+ events: IAuditEvent[];
7
+ }
8
+ /**
9
+ * Journal d'audit **en mémoire** — implémentation de référence d'{@link IAuditStore}.
10
+ *
11
+ * 0 dépendance, idéal pour le dev mono-process et les **tests**. **Volatile**
12
+ * (perdu au redémarrage) et **non partagé** (per-pod) → en prod multi-process,
13
+ * brancher un backend ORM/Redis. Le volume est **borné** (`maxEntries`, FIFO : au
14
+ * delà, le plus ancien tombe) pour ne JAMAIS fuir, doublé d'une purge par âge
15
+ * ({@link MemoryAuditStore.gc}, rétention). Append-only : aucune mutation d'un
16
+ * événement déjà journalisé.
17
+ *
18
+ * Horloge injectable (`now`) pour des tests déterministes (pattern `MemoryTokenStore`).
19
+ */
20
+ export declare class MemoryAuditStore implements IAuditStore {
21
+ #private;
22
+ constructor(now?: () => number, retentionMs?: number, // 365 jours
23
+ maxEntries?: number);
24
+ append(event: IAuditEvent): Promise<void>;
25
+ listPage(query: IAuditListQuery): Promise<IPage<IAuditEvent>>;
26
+ gc(now?: number): Promise<number>;
27
+ /** Nombre d'événements actuellement retenus (introspection / tests). */
28
+ get size(): number;
29
+ /** Instantané sérialisable de l'état courant. */
30
+ snapshot(): AuditStoreSnapshot;
31
+ /** Remplace l'état par celui d'un instantané. */
32
+ restore(snapshot: AuditStoreSnapshot): void;
33
+ }
@@ -0,0 +1,49 @@
1
+ import type { IAuditEvent } from "../../contracts/IAuditEvent.js";
2
+ /**
3
+ * Canal WS du flux live d'audit (P6.14 lot 4). Le préfixe `security:` le place
4
+ * sous le plancher `SECURITY_CHANNEL_POLICY` (ROLE_NODEFONY_ADMIN) du verrou de
5
+ * frame — un user lambda ne peut pas s'y abonner (refus audité `frame.denied`).
6
+ */
7
+ export declare const SECURITY_AUDIT_CHANNEL: "nodefony:audit";
8
+ /** Source d'événements live — sous-ensemble de `IAuditSink` (slot `subscribe`). */
9
+ export interface IAuditEventSource {
10
+ subscribe(listener: (event: IAuditEvent) => void): () => void;
11
+ }
12
+ /** Charge poussée sur le canal `nodefony:audit` — batch coalescé + omis. */
13
+ export interface IAuditBatch {
14
+ events: IAuditEvent[];
15
+ dropped: number;
16
+ }
17
+ /** Options de coalescing du pont d'audit. */
18
+ export interface AuditBridgeOptions {
19
+ /** Fenêtre d'agrégation : 1 frame WS au plus toutes les `flushMs`. Défaut 250. */
20
+ flushMs?: number;
21
+ /** Cap d'un batch (ring buffer) : au-delà, on garde les + récents et on compte
22
+ * les omis. Borne la mémoire ET le nb d'événements envoyés au front. Défaut 200. */
23
+ maxBatch?: number;
24
+ }
25
+ /**
26
+ * Pont journal d'audit → canal `nodefony:audit`, **coalescé** (P6.14 lot 4).
27
+ *
28
+ * Calque {@link createSyslogBridge} (studio) : au lieu de 1 frame WS par
29
+ * événement (un pic d'`auth.failure` sous brute-force noierait la console
30
+ * auditeur), on accumule dans un **ring buffer borné** et on flush **1 frame
31
+ * agrégée toutes les `flushMs`** : `{ events, dropped }`. Sous surcharge, le ring
32
+ * écrase les plus vieux et `dropped` indique combien ont été omis → la console
33
+ * affiche un récap au lieu de se figer (budget borné, dégradable — règle
34
+ * observabilité « superviser ≠ tomber la prod »).
35
+ *
36
+ * **Lazy par construction** (créé par le hub au 1ᵉʳ abonné, `dispose` au dernier) :
37
+ * tant qu'aucun auditeur n'écoute `nodefony:audit`, ce pont N'EXISTE PAS — aucun
38
+ * listener sur l'`AuditService`, aucun timer. Au repos avec auditeur connecté mais
39
+ * sans événement : ring `null`, 0 timer (armé au 1ᵉʳ événement, `unref`).
40
+ *
41
+ * @param source - l'`AuditService` (slot `subscribe`).
42
+ * @param publish - publication hub (le canal est fourni par la factory).
43
+ * @param channel - canal de publication (`nodefony:audit`).
44
+ * @returns dispose() — détache le listener `AuditService` ET désarme le timer.
45
+ * OBLIGATOIRE (aucun listener/timer sans cleanup, sinon fuite à chaque
46
+ * dernier désabonnement).
47
+ */
48
+ export declare function createAuditBridge(source: IAuditEventSource, publish: (channel: string, payload: unknown) => void, channel: string, opts?: AuditBridgeOptions): () => void;
49
+ export default createAuditBridge;
@@ -0,0 +1,56 @@
1
+ import type { AuditCategory, AuditOutcome } from "../../contracts/IAuditEvent.js";
2
+ /**
3
+ * **Le vocabulaire de filtre du journal d'audit**, en noms PUBLICS — ceux qu'un
4
+ * auditeur écrit dans l'URL (`?category=authz&outcome=denied&since=…`).
5
+ *
6
+ * Les deux énumérations y sont écrites en toutes lettres, et
7
+ * {@link AUDIT_FILTER_VOCABULARY_IS_COMPLETE} vérifie **à la compilation**
8
+ * qu'elles couvrent exactement `AuditCategory` et `AuditOutcome`.
9
+ *
10
+ * Ce contrôle n'est pas décoratif : la liste qu'il remplace avait DÉJÀ dérivé.
11
+ * Un `Set` recopié à la main dans le data plane portait dix catégories quand le
12
+ * type en déclarait onze — `?category=config` tombait donc hors de l'allowlist,
13
+ * était ignoré en silence, et l'auditeur recevait le journal ENTIER en croyant
14
+ * lire les seules mutations de configuration. Une liste recopiée ne diverge
15
+ * jamais bruyamment.
16
+ *
17
+ * `actor`, `action` et `requestId` restent des chaînes libres : ce sont des
18
+ * identifiants produits à l'exécution, aucune allowlist ne peut les connaître.
19
+ * `since`/`until` sont des horodatages en millisecondes (bornes incluses).
20
+ */
21
+ export declare const AUDIT_FILTERS: {
22
+ /** Famille d'événement — la liste EST le type `AuditCategory`. */
23
+ readonly category: readonly ["auth", "authz", "token", "session", "oauth", "webauthn", "csrf", "cors", "ws", "webhook", "config"];
24
+ /** Issue — `denied` est le signal d'accès non autorisé. */
25
+ readonly outcome: readonly ["success", "failure", "denied"];
26
+ /** Identité de l'acteur (égalité stricte). */
27
+ readonly actor: "string";
28
+ /** Nom de l'action auditée (égalité stricte). */
29
+ readonly action: "string";
30
+ /** Corrèle toutes les traces d'une même requête. */
31
+ readonly requestId: "string";
32
+ /** Borne basse, horodatage en millisecondes. */
33
+ readonly since: "int";
34
+ /** Borne haute, horodatage en millisecondes. */
35
+ readonly until: "int";
36
+ };
37
+ /**
38
+ * Vrai si les deux ensembles sont EXACTEMENT les mêmes, `never` sinon — donc
39
+ * inassignable depuis `true`, donc erreur de compilation.
40
+ *
41
+ * Les deux sens comptent, et pour des raisons différentes : une valeur en trop
42
+ * dans la liste ouvrirait un filtre qu'aucun store ne sait honorer ; une valeur
43
+ * manquante ferait refuser en 400 une catégorie parfaitement légitime — le
44
+ * contraire du silence d'origine, mais tout aussi faux.
45
+ */
46
+ type SameValues<A, B> = [A] extends [B] ? [B] extends [A] ? true : never : never;
47
+ /**
48
+ * Preuve **à la compilation** que le vocabulaire ci-dessus est exactement celui
49
+ * des types du contrat. Elle remplace la discipline humaine « penser à mettre
50
+ * les deux à jour », qui avait échoué en silence.
51
+ */
52
+ export declare const AUDIT_FILTER_VOCABULARY_IS_COMPLETE: [
53
+ SameValues<(typeof AUDIT_FILTERS.category)[number], AuditCategory>,
54
+ SameValues<(typeof AUDIT_FILTERS.outcome)[number], AuditOutcome>
55
+ ];
56
+ export {};
@@ -0,0 +1,37 @@
1
+ import type { Container } from "nodefony";
2
+ import type { ISecurityConfig } from "../../config/defineModuleConfig.js";
3
+ import type { IAuditStore } from "../../contracts/IAuditStore.js";
4
+ /**
5
+ * Registre de **fabriques de stores d'audit** — résout le nom configuré
6
+ * (`security.audit.store`) vers une instance d'{@link IAuditStore}, SANS coupler
7
+ * le cœur à un backend en dur.
8
+ *
9
+ * Pourquoi : le journal d'audit est pluggable par contrat (mémoire/ORM/Redis/Loki) ;
10
+ * un `if (name === "drizzle")` dans le service trahirait cette promesse. Le builtin
11
+ * sans dépendance (`memory`) s'enregistre au chargement de ce module ; les adapters
12
+ * lourds (`drizzle`, `mongoose`, `redis`) s'enregistrent depuis LEUR module
13
+ * (inversion de dépendance : ils importent `import type { IAuditStore }`, effacé à
14
+ * la compilation → 0 cycle). Convention-frère : `tokenStoreRegistry`,
15
+ * `webAuthnCredentialStoreRegistry`, `ormRegistry`.
16
+ */
17
+ /**
18
+ * Contexte passé à une fabrique de store d'audit : de quoi se construire
19
+ * (résolutions coûteuses en lazy à l'intérieur de l'instance).
20
+ */
21
+ export interface IAuditStoreFactoryContext {
22
+ /** Container DI — résolution de services (ORM, redis...). */
23
+ readonly container: Container;
24
+ /** Config sécurité validée + gelée. */
25
+ readonly config: ISecurityConfig;
26
+ }
27
+ /** Fabrique d'un store d'audit pour un nom donné. */
28
+ export type AuditStoreFactory = (ctx: IAuditStoreFactoryContext) => IAuditStore;
29
+ /**
30
+ * Enregistre (ou remplace) la fabrique d'un store d'audit. Appelée par le builtin
31
+ * `memory` au chargement, et par les adapters (drizzle/mongoose/redis) pour les leurs.
32
+ */
33
+ export declare function registerAuditStore(name: string, factory: AuditStoreFactory): void;
34
+ /** Fabrique d'un store par nom, ou `undefined` si inconnu. */
35
+ export declare function getAuditStoreFactory(name: string): AuditStoreFactory | undefined;
36
+ /** Noms enregistrés (validation boot, introspection Studio, tests). */
37
+ export declare function listAuditStores(): string[];
@@ -0,0 +1,17 @@
1
+ import type { IAuditEventFlags } from "../../contracts/IAuditEvent.js";
2
+ /** Métadonnées de provenance extraites d'un contexte, prêtes à enrichir un événement. */
3
+ export interface AuditContextInfo {
4
+ ip: string | null;
5
+ userAgent: string | null;
6
+ requestId: string | null;
7
+ flags: IAuditEventFlags;
8
+ }
9
+ /**
10
+ * Extrait IP / User-Agent / requestId + drapeaux de présence (jamais la valeur)
11
+ * d'un contexte de requête — pour enrichir un événement d'audit avec la
12
+ * provenance (« d'où vient cette tentative »). Calque {@link JsonAuditLogger}.
13
+ *
14
+ * @param context - contexte HTTP/WS courant (typé `unknown` à la frontière).
15
+ * @returns provenance normalisée ; champs `null` si l'info est absente.
16
+ */
17
+ export declare function readAuditContext(context: unknown): AuditContextInfo;
@@ -0,0 +1,13 @@
1
+ import type { Container } from "nodefony";
2
+ import type { IAuditEventDraft } from "../../contracts/IAuditEvent.js";
3
+ /**
4
+ * Émet un événement d'audit **si** le service est présent — no-op sinon. La
5
+ * résolution se fait par le container (`auditService`) sur le **cold-path**
6
+ * (login, refus, révocation) : le coût d'un `Map.get` y est négligeable, et le
7
+ * journal reste **découplé** (module audit absent ou désactivé → aucun effet,
8
+ * jamais d'exception qui remonterait dans le flux métier).
9
+ *
10
+ * @param container - container du service émetteur (`this.container`).
11
+ * @param event - brouillon d'événement (l'`AuditService` pose `id` + `ts`).
12
+ */
13
+ export declare function recordAudit(container: Container | null | undefined, event: IAuditEventDraft): void;
@@ -0,0 +1,26 @@
1
+ import type { ContextType } from "@nodefony/http";
2
+ import type { IAuthenticator } from "../../contracts/IAuthenticator.js";
3
+ import type { IToken } from "../../contracts/IToken.js";
4
+ /**
5
+ * Acceptation EXPLICITE de l'anonymat dans une zone — le seul authenticator
6
+ * autorisé à produire un token non authentifié sans déclencher le Zero Trust.
7
+ *
8
+ * Ne le lister que volontairement : une zone `authenticators: ["jwt", "anonymous"]`
9
+ * (mode `first`) signifie « identifié si preuve présente, sinon visiteur anonyme
10
+ * accepté ». Sans lui, zone protégée + aucune preuve → 401. En mode `all` il
11
+ * reste utile en DERNIER : « le canal doit être prouvé (ex. mtls), l'identité
12
+ * utilisateur est optionnelle ».
13
+ *
14
+ * Zéro coût : `supports()` accepte tout, le token porte le singleton gelé
15
+ * `anonymousUser` (aucune allocation d'utilisateur).
16
+ */
17
+ export declare class AnonymousAuthenticator implements IAuthenticator {
18
+ readonly name = "anonymous";
19
+ supports(): boolean;
20
+ createToken(): Promise<IToken>;
21
+ /** Toujours un succès — accepter l'anonymat ne vérifie rien. */
22
+ authenticate(token: IToken): Promise<IToken>;
23
+ onSuccess(_context: ContextType, _token: IToken): Promise<void>;
24
+ onFailure(_context: ContextType, _error: Error): Promise<void>;
25
+ }
26
+ export default AnonymousAuthenticator;
@@ -0,0 +1,74 @@
1
+ import type { Container } from "nodefony";
2
+ import type { ContextType } from "@nodefony/http";
3
+ import type { IAuthenticator } from "../../contracts/IAuthenticator.js";
4
+ import type { IToken } from "../../contracts/IToken.js";
5
+ /** Paramètres effectifs d'un {@link ApiKeyAuthenticator} (dérivés de la config). */
6
+ export interface IApiKeyAuthenticatorRuntime {
7
+ /** Marque des clés (`apiKeys.prefix`) — discrimine un PAT d'un JWT. */
8
+ prefix: string;
9
+ /** Coalescence d'écriture `lastUsedAt` (s) — 0 = écrit à chaque usage. */
10
+ lastUsedThrottleS: number;
11
+ }
12
+ /**
13
+ * Authentification par **clé API personnelle (PAT, P6.12)** présentée en
14
+ * `Authorization: Bearer <prefix>_…` (RFC 6750). Réservée API/CI/scripts — le web
15
+ * utilise la session BFF.
16
+ *
17
+ * Un PAT est un **bearer opaque** (≠ JWT auto-porté) : sa vérité vit côté serveur
18
+ * (`ITokenStore`), donc il est **révocable immédiatement**. Discrimination du
19
+ * JWT : le PAT porte le préfixe `<prefix>_` (le JWT a la structure compacte
20
+ * `a.b.c`) → les deux authenticators cohabitent dans une même zone.
21
+ *
22
+ * Défenses :
23
+ * - **forme + CRC validés AVANT tout accès au store** ({@link parseApiKey}) →
24
+ * une valeur malformée n'atteint jamais la base (anti-DoS) ;
25
+ * - lookup par **hash** (`sha256`) — le secret n'existe nulle part au repos ;
26
+ * - **révocation** immédiate (`revokedAt`) + **expiration** (`expiresAt`) +
27
+ * **ban en masse** du porteur (`invalidBefore` vs `createdAt`) ;
28
+ * - **sujet revérifié** à chaque requête (`loadUserByIdentifier` → disparu/
29
+ * inactif/verrouillé = rejet) — rôles **frais** (révocation effective) ;
30
+ * - message d'échec **uniforme** (anti-énumération).
31
+ *
32
+ * Dépendances (store, userProvider) résolues **paresseusement** du container.
33
+ */
34
+ export declare class ApiKeyAuthenticator implements IAuthenticator {
35
+ #private;
36
+ readonly name = "apikey";
37
+ /**
38
+ * @param container - container DI (résolution lazy de `tokenStore`/`users`).
39
+ * @param runtime - préfixe + throttle effectifs (dérivés de `config.apiKeys`).
40
+ */
41
+ constructor(container: Container, runtime: IApiKeyAuthenticatorRuntime);
42
+ /** La requête porte-t-elle un `Authorization: Bearer <prefix>_…` ? (test bon marché) */
43
+ supports(context: ContextType): boolean;
44
+ /** Extrait la valeur brute (non vérifiée) → portée par un `UserToken` type `"apikey"`. */
45
+ createToken(context: ContextType): Promise<IToken>;
46
+ /**
47
+ * Valide la clé (forme+CRC, puis store) et résout le sujet — ou lève un 401 au
48
+ * message uniforme.
49
+ *
50
+ * @throws AuthenticationError (401) — clé malformée/inconnue/révoquée/expirée,
51
+ * ou sujet disparu/banni.
52
+ * @throws Error (câblage : store/users absents) — loggée ERROR par le firewall
53
+ * puis 401 fail-closed (rien ne fuite au client).
54
+ */
55
+ authenticate(token: IToken): Promise<IToken>;
56
+ /**
57
+ * Inscrit la trace d'usage de la clé — horodatage, IP et agent.
58
+ *
59
+ * C'est ici, et pas dans `authenticate()`, parce que c'est ici qu'on reçoit
60
+ * le contexte. La provenance se lit par les ACCESSEURS proxy-aware des
61
+ * contextes concrets (`getRemoteAddress()` dépouille `X-Forwarded-For` selon
62
+ * `trustProxy`), absents du type de base — duck-typing optionnel, même
63
+ * approche que `AuthFlow.#openSession()`.
64
+ *
65
+ * Rien n'est écrit si `authenticate()` n'a pas posé le marqueur : la fenêtre
66
+ * de throttle n'était pas dépassée, et le hot path reste sans écriture.
67
+ */
68
+ onSuccess(context: ContextType, token: IToken): Promise<void>;
69
+ /** Slot audit (P6.14) — le 401 + challenge sont posés par le firewall. */
70
+ onFailure(_context: ContextType, _error: Error): Promise<void>;
71
+ /** Challenge RFC 6750/7235 posé par le firewall sur les 401 de la zone. */
72
+ challenge(): string;
73
+ }
74
+ export default ApiKeyAuthenticator;