@nodefony/security 10.0.0-beta.2 → 10.0.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (249) hide show
  1. package/dist/_virtual/{_@oxc-project_runtime@0.152.0 → _@oxc-project_runtime@0.155.0}/helpers/esm/decorate.js +1 -2
  2. package/dist/index.js +48 -16
  3. package/dist/nodefony/command/{security-token.js → security-mcp-token.js} +62 -24
  4. package/dist/nodefony/command/security-oauth-doctor.js +68 -0
  5. package/dist/nodefony/command/security-secrets.js +52 -29
  6. package/dist/nodefony/command/security-user-add.js +9 -3
  7. package/dist/nodefony/command/security-user-delete.js +10 -7
  8. package/dist/nodefony/command/security-user-list.js +5 -3
  9. package/dist/nodefony/command/security-user-password.js +9 -14
  10. package/dist/nodefony/config/config.js +99 -8
  11. package/dist/nodefony/config/defineModuleConfig.js +1 -2
  12. package/dist/nodefony/contracts/IAccessVoter.js +1 -2
  13. package/dist/nodefony/contracts/index.js +1 -2
  14. package/dist/nodefony/errors/AccessDeniedError.js +1 -2
  15. package/dist/nodefony/errors/ApiKeyError.js +3 -2
  16. package/dist/nodefony/errors/AuthenticationError.js +1 -2
  17. package/dist/nodefony/errors/CsrfError.js +1 -2
  18. package/dist/nodefony/errors/InsecureTransportError.js +23 -0
  19. package/dist/nodefony/errors/InvalidTargetError.js +1 -2
  20. package/dist/nodefony/errors/SsrfError.js +1 -2
  21. package/dist/nodefony/errors/ThrottledError.js +1 -2
  22. package/dist/nodefony/errors/UnverifiableTokenError.js +1 -2
  23. package/dist/nodefony/errors/WebAuthnError.js +1 -2
  24. package/dist/nodefony/errors/index.js +2 -1
  25. package/dist/nodefony/keycloak/keycloakAdmin.js +254 -0
  26. package/dist/nodefony/keycloak/keycloakRealm.js +259 -0
  27. package/dist/nodefony/keycloak/keycloakRealmInput.js +123 -0
  28. package/dist/nodefony/keycloak/scaffold.js +231 -0
  29. package/dist/nodefony/keycloak/security-keycloak-realm.js +130 -0
  30. package/dist/nodefony/scaffold/contribute.js +19 -0
  31. package/dist/nodefony/service/accessTokenVerifier.js +101 -34
  32. package/dist/nodefony/service/apiKeys.js +45 -5
  33. package/dist/nodefony/service/auditService.js +4 -5
  34. package/dist/nodefony/service/authFlow.js +118 -16
  35. package/dist/nodefony/service/authorization.js +1 -2
  36. package/dist/nodefony/service/cors.js +1 -2
  37. package/dist/nodefony/service/csrf.js +1 -2
  38. package/dist/nodefony/service/firewall.js +116 -25
  39. package/dist/nodefony/service/keycloak.js +208 -0
  40. package/dist/nodefony/service/oauth2.js +403 -28
  41. package/dist/nodefony/service/securityHeaders.js +1 -2
  42. package/dist/nodefony/service/tokenService.js +94 -27
  43. package/dist/nodefony/service/totp.js +55 -11
  44. package/dist/nodefony/service/webAuthn.js +24 -7
  45. package/dist/nodefony/service/webhooks.js +4 -5
  46. package/dist/nodefony/src/RoleHierarchyWalker.js +1 -2
  47. package/dist/nodefony/src/SecuredArea.js +15 -5
  48. package/dist/nodefony/src/accountBinding.js +96 -0
  49. package/dist/nodefony/src/admin/SecurityAdminApi.js +71 -42
  50. package/dist/nodefony/src/admin/WebhookAdminApi.js +23 -7
  51. package/dist/nodefony/src/admin/adminAudit.js +1 -3
  52. package/dist/nodefony/src/admin/userRevocationCascade.js +148 -20
  53. package/dist/nodefony/src/apikey/apiKeyFormat.js +1 -2
  54. package/dist/nodefony/src/audit/MemoryAuditStore.js +1 -2
  55. package/dist/nodefony/src/audit/auditBridge.js +1 -2
  56. package/dist/nodefony/src/audit/auditFilters.js +1 -2
  57. package/dist/nodefony/src/audit/auditStoreRegistry.js +1 -2
  58. package/dist/nodefony/src/audit/readAuditContext.js +1 -2
  59. package/dist/nodefony/src/audit/recordAudit.js +1 -2
  60. package/dist/nodefony/src/authenticator/AnonymousAuthenticator.js +1 -2
  61. package/dist/nodefony/src/authenticator/ApiKeyAuthenticator.js +3 -2
  62. package/dist/nodefony/src/authenticator/ExternalJwtAuthenticator.js +14 -59
  63. package/dist/nodefony/src/authenticator/ExternalSubjectResolver.js +180 -0
  64. package/dist/nodefony/src/authenticator/FirewallRealtimeAuthenticator.js +80 -19
  65. package/dist/nodefony/src/authenticator/JwtAuthenticator.js +92 -16
  66. package/dist/nodefony/src/authenticator/SessionAuthenticator.js +9 -5
  67. package/dist/nodefony/src/authenticator/UserPasswordAuthenticator.js +14 -5
  68. package/dist/nodefony/src/authenticator/authenticatorRegistry.js +5 -12
  69. package/dist/nodefony/src/authenticator/bearer.js +1 -2
  70. package/dist/nodefony/src/authenticator/externalSubject.js +1 -2
  71. package/dist/nodefony/src/authenticator/peekIssuer.js +1 -2
  72. package/dist/nodefony/src/boot/publicRoutesNotice.js +26 -0
  73. package/dist/nodefony/src/crypto/secretCipher.js +1 -2
  74. package/dist/nodefony/src/csp.js +1 -2
  75. package/dist/nodefony/src/csrfToken.js +1 -2
  76. package/dist/nodefony/src/entryPoint.js +38 -0
  77. package/dist/nodefony/src/net/ssrfGuard.js +1 -2
  78. package/dist/nodefony/src/net/transportFailure.js +57 -0
  79. package/dist/nodefony/src/oauth/clientCredentials.js +39 -0
  80. package/dist/nodefony/src/oauth/httpJson.js +3 -3
  81. package/dist/nodefony/src/oauth/metadata.js +28 -12
  82. package/dist/nodefony/src/oauth/oauth2Client.js +61 -35
  83. package/dist/nodefony/src/oauth/oauthProviderRegistry.js +40 -8
  84. package/dist/nodefony/src/oauth/providerDiagnosis.js +282 -0
  85. package/dist/nodefony/src/oauth/providerRoles.js +78 -0
  86. package/dist/nodefony/src/oauth/providers/github.js +48 -12
  87. package/dist/nodefony/src/oauth/providers/oidc.js +90 -8
  88. package/dist/nodefony/src/openToSignup.js +46 -0
  89. package/dist/nodefony/src/realtime/UserRealtimeToken.js +17 -3
  90. package/dist/nodefony/src/realtime/frameAuthorizer.js +1 -2
  91. package/dist/nodefony/src/rolesWithin.js +57 -0
  92. package/dist/nodefony/src/sessionAuthentication.js +25 -0
  93. package/dist/nodefony/src/sessionIdentity.js +29 -3
  94. package/dist/nodefony/src/throttle/LoginThrottler.js +1 -2
  95. package/dist/nodefony/src/token/AnonymousToken.js +1 -2
  96. package/dist/nodefony/src/token/JwtKeystore.js +89 -48
  97. package/dist/nodefony/src/token/MemoryTokenStore.js +1 -2
  98. package/dist/nodefony/src/token/RemoteJwtVerifier.js +46 -16
  99. package/dist/nodefony/src/token/UserToken.js +1 -2
  100. package/dist/nodefony/src/token/jwtRuntime.js +1 -2
  101. package/dist/nodefony/src/token/scopeGrants.js +109 -0
  102. package/dist/nodefony/src/token/secretFile.js +60 -4
  103. package/dist/nodefony/src/token/tokenCriteria.js +1 -2
  104. package/dist/nodefony/src/token/tokenFilters.js +1 -2
  105. package/dist/nodefony/src/token/tokenSort.js +1 -2
  106. package/dist/nodefony/src/token/tokenStatus.js +1 -2
  107. package/dist/nodefony/src/token/tokenStoreRegistry.js +1 -2
  108. package/dist/nodefony/src/totp/MemoryTotpSecretStore.js +1 -2
  109. package/dist/nodefony/src/totp/totpCipher.js +1 -2
  110. package/dist/nodefony/src/totp/totpCrypto.js +1 -2
  111. package/dist/nodefony/src/totp/totpOperations.js +1 -2
  112. package/dist/nodefony/src/totp/totpSecretStoreRegistry.js +1 -2
  113. package/dist/nodefony/src/transport/CredentialTransportPolicy.js +50 -0
  114. package/dist/nodefony/src/voter/RoleVoter.js +1 -2
  115. package/dist/nodefony/src/voter/ScopeVoter.js +1 -2
  116. package/dist/nodefony/src/voter/voterRegistry.js +1 -2
  117. package/dist/nodefony/src/webauthn/MemoryWebAuthnCredentialStore.js +1 -2
  118. package/dist/nodefony/src/webauthn/webAuthnCredentialStoreRegistry.js +1 -2
  119. package/dist/nodefony/src/webhook/MemoryWebhookStore.js +1 -2
  120. package/dist/nodefony/src/webhook/WebhookDispatcher.js +1 -2
  121. package/dist/nodefony/src/webhook/webhookCipher.js +1 -2
  122. package/dist/nodefony/src/webhook/webhookDelivery.js +1 -2
  123. package/dist/nodefony/src/webhook/webhookFilters.js +1 -2
  124. package/dist/nodefony/src/webhook/webhookSignature.js +1 -2
  125. package/dist/nodefony/src/webhook/webhookSort.js +1 -2
  126. package/dist/nodefony/src/webhook/webhookStoreRegistry.js +1 -2
  127. package/dist/types/index.d.ts +26 -6
  128. package/dist/types/nodefony/command/{security-token.d.ts → security-mcp-token.d.ts} +16 -6
  129. package/dist/types/nodefony/command/security-oauth-doctor.d.ts +23 -0
  130. package/dist/types/nodefony/command/security-secrets.d.ts +2 -0
  131. package/dist/types/nodefony/command/security-user-delete.d.ts +4 -3
  132. package/dist/types/nodefony/command/security-user-password.d.ts +4 -4
  133. package/dist/types/nodefony/config/config.d.ts +64 -1
  134. package/dist/types/nodefony/contracts/IAuthenticator.d.ts +1 -1
  135. package/dist/types/nodefony/contracts/IFirewallDescription.d.ts +42 -1
  136. package/dist/types/nodefony/contracts/IOAuthProvider.d.ts +48 -0
  137. package/dist/types/nodefony/contracts/ISecuredArea.d.ts +22 -0
  138. package/dist/types/nodefony/contracts/ITotpSecret.d.ts +4 -1
  139. package/dist/types/nodefony/contracts/ITotpSecretStore.d.ts +7 -1
  140. package/dist/types/nodefony/contracts/IWebAuthnCredential.d.ts +4 -1
  141. package/dist/types/nodefony/contracts/IWebAuthnCredentialStore.d.ts +6 -0
  142. package/dist/types/nodefony/errors/ApiKeyError.d.ts +3 -1
  143. package/dist/types/nodefony/errors/InsecureTransportError.d.ts +20 -0
  144. package/dist/types/nodefony/errors/index.d.ts +1 -0
  145. package/dist/types/nodefony/keycloak/keycloakAdmin.d.ts +154 -0
  146. package/dist/types/nodefony/keycloak/keycloakRealm.d.ts +138 -0
  147. package/dist/types/nodefony/keycloak/keycloakRealmInput.d.ts +92 -0
  148. package/dist/types/nodefony/keycloak/scaffold.d.ts +98 -0
  149. package/dist/types/nodefony/keycloak/security-keycloak-realm.d.ts +43 -0
  150. package/dist/types/nodefony/scaffold/contribute.d.ts +15 -0
  151. package/dist/types/nodefony/service/accessTokenVerifier.d.ts +35 -14
  152. package/dist/types/nodefony/service/apiKeys.d.ts +13 -2
  153. package/dist/types/nodefony/service/authFlow.d.ts +35 -3
  154. package/dist/types/nodefony/service/firewall.d.ts +17 -3
  155. package/dist/types/nodefony/service/keycloak.d.ts +115 -0
  156. package/dist/types/nodefony/service/oauth2.d.ts +145 -9
  157. package/dist/types/nodefony/service/tokenService.d.ts +7 -1
  158. package/dist/types/nodefony/service/totp.d.ts +28 -4
  159. package/dist/types/nodefony/service/webAuthn.d.ts +23 -1
  160. package/dist/types/nodefony/src/SecuredArea.d.ts +4 -0
  161. package/dist/types/nodefony/src/accountBinding.d.ts +82 -0
  162. package/dist/types/nodefony/src/admin/userRevocationCascade.d.ts +72 -12
  163. package/dist/types/nodefony/src/authenticator/ExternalJwtAuthenticator.d.ts +2 -26
  164. package/dist/types/nodefony/src/authenticator/ExternalSubjectResolver.d.ts +99 -0
  165. package/dist/types/nodefony/src/authenticator/FirewallRealtimeAuthenticator.d.ts +17 -1
  166. package/dist/types/nodefony/src/authenticator/JwtAuthenticator.d.ts +37 -1
  167. package/dist/types/nodefony/src/authenticator/UserPasswordAuthenticator.d.ts +11 -3
  168. package/dist/types/nodefony/src/boot/publicRoutesNotice.d.ts +16 -0
  169. package/dist/types/nodefony/src/entryPoint.d.ts +37 -0
  170. package/dist/types/nodefony/src/net/transportFailure.d.ts +22 -0
  171. package/dist/types/nodefony/src/oauth/clientCredentials.d.ts +54 -0
  172. package/dist/types/nodefony/src/oauth/httpJson.d.ts +0 -8
  173. package/dist/types/nodefony/src/oauth/metadata.d.ts +24 -1
  174. package/dist/types/nodefony/src/oauth/oauth2Client.d.ts +50 -1
  175. package/dist/types/nodefony/src/oauth/oauthProviderRegistry.d.ts +46 -2
  176. package/dist/types/nodefony/src/oauth/providerDiagnosis.d.ts +117 -0
  177. package/dist/types/nodefony/src/oauth/providerRoles.d.ts +58 -0
  178. package/dist/types/nodefony/src/oauth/providers/oidc.d.ts +34 -1
  179. package/dist/types/nodefony/src/openToSignup.d.ts +36 -0
  180. package/dist/types/nodefony/src/realtime/UserRealtimeToken.d.ts +7 -1
  181. package/dist/types/nodefony/src/realtime/realtimeContracts.d.ts +6 -0
  182. package/dist/types/nodefony/src/rolesWithin.d.ts +27 -0
  183. package/dist/types/nodefony/src/sessionAuthentication.d.ts +14 -0
  184. package/dist/types/nodefony/src/sessionIdentity.d.ts +22 -0
  185. package/dist/types/nodefony/src/token/JwtKeystore.d.ts +47 -5
  186. package/dist/types/nodefony/src/token/RemoteJwtVerifier.d.ts +29 -2
  187. package/dist/types/nodefony/src/token/scopeGrants.d.ts +77 -0
  188. package/dist/types/nodefony/src/token/secretFile.d.ts +22 -0
  189. package/dist/types/nodefony/src/transport/CredentialTransportPolicy.d.ts +47 -0
  190. package/docs/api-keys.md +182 -175
  191. package/docs/audit.md +141 -141
  192. package/docs/authenticators.md +129 -96
  193. package/docs/authorization.md +116 -69
  194. package/docs/cors.md +64 -64
  195. package/docs/csrf.md +77 -72
  196. package/docs/external-jwt.md +83 -11
  197. package/docs/firewall.md +119 -83
  198. package/docs/headers.md +82 -82
  199. package/docs/index.md +16 -8
  200. package/docs/keycloak.md +835 -0
  201. package/docs/login-page.md +361 -0
  202. package/docs/oauth2.md +291 -166
  203. package/docs/obtenir-un-jeton.md +131 -34
  204. package/docs/tokens.md +197 -140
  205. package/docs/totp.md +177 -136
  206. package/docs/webauthn.md +157 -154
  207. package/docs/webhooks.md +128 -125
  208. package/keycloak/themes/nodefony/account/resources/css/nodefony.css +66 -0
  209. package/keycloak/themes/nodefony/account/resources/img/favicon.ico +0 -0
  210. package/keycloak/themes/nodefony/account/resources/img/logo.png +0 -0
  211. package/keycloak/themes/nodefony/account/theme.properties +14 -0
  212. package/keycloak/themes/nodefony/admin/resources/css/nodefony.css +66 -0
  213. package/keycloak/themes/nodefony/admin/resources/img/favicon.ico +0 -0
  214. package/keycloak/themes/nodefony/admin/resources/img/logo.png +0 -0
  215. package/keycloak/themes/nodefony/admin/theme.properties +12 -0
  216. package/keycloak/themes/nodefony/email/html/template.ftl +73 -0
  217. package/keycloak/themes/nodefony/email/messages/messages_en.properties +2 -0
  218. package/keycloak/themes/nodefony/email/messages/messages_fr.properties +3 -0
  219. package/keycloak/themes/nodefony/email/resources/img/logo.png +0 -0
  220. package/keycloak/themes/nodefony/email/theme.properties +8 -0
  221. package/keycloak/themes/nodefony/login/messages/messages_en.properties +22 -0
  222. package/keycloak/themes/nodefony/login/messages/messages_fr.properties +25 -0
  223. package/keycloak/themes/nodefony/login/resources/css/login.css +1141 -0
  224. package/keycloak/themes/nodefony/login/resources/css/nodefony.css +688 -0
  225. package/keycloak/themes/nodefony/login/resources/css/skins/blueprint.css +70 -0
  226. package/keycloak/themes/nodefony/login/resources/css/skins/console.css +57 -0
  227. package/keycloak/themes/nodefony/login/resources/css/skins/dots.css +67 -0
  228. package/keycloak/themes/nodefony/login/resources/css/skins/enterprise.css +84 -0
  229. package/keycloak/themes/nodefony/login/resources/css/skins/horizon.css +158 -0
  230. package/keycloak/themes/nodefony/login/resources/css/skins/ledger.css +224 -0
  231. package/keycloak/themes/nodefony/login/resources/css/skins/minimal.css +63 -0
  232. package/keycloak/themes/nodefony/login/resources/css/skins/photo-card.css +72 -0
  233. package/keycloak/themes/nodefony/login/resources/img/favicon.ico +0 -0
  234. package/keycloak/themes/nodefony/login/resources/img/logo.png +0 -0
  235. package/keycloak/themes/nodefony/login/template.ftl +423 -0
  236. package/keycloak/themes/nodefony/login/theme.properties +17 -0
  237. package/keycloak/themes/nodefony/welcome/resources/css/nodefony.css +32 -0
  238. package/keycloak/themes/nodefony/welcome/resources/img/logo.png +0 -0
  239. package/keycloak/themes/nodefony/welcome/theme.properties +11 -0
  240. package/keycloak/themes/nodefony-blueprint/login/theme.properties +12 -0
  241. package/keycloak/themes/nodefony-console/login/theme.properties +12 -0
  242. package/keycloak/themes/nodefony-dots/login/theme.properties +12 -0
  243. package/keycloak/themes/nodefony-enterprise/login/theme.properties +12 -0
  244. package/keycloak/themes/nodefony-horizon/login/theme.properties +12 -0
  245. package/keycloak/themes/nodefony-ledger/login/theme.properties +12 -0
  246. package/keycloak/themes/nodefony-minimal/login/theme.properties +12 -0
  247. package/keycloak/themes/nodefony-photo-card/login/theme.properties +12 -0
  248. package/package.json +19 -17
  249. package/dist/_virtual/_@oxc-project_runtime@0.152.0/helpers/esm/decorateMetadata.js +0 -6
@@ -3,32 +3,8 @@ import type { ContextType } from "@nodefony/http";
3
3
  import type { IAuthenticator } from "../../contracts/IAuthenticator.js";
4
4
  import type { ISecuredArea } from "../../contracts/ISecuredArea.js";
5
5
  import type { IToken } from "../../contracts/IToken.js";
6
- import { type ExternalSubjectMapping } from "./externalSubject.js";
7
- /** Comment le sujet d'un jeton devient un utilisateur de cette application. */
8
- export type ExternalSubjectPolicy = "require" | "ephemeral";
9
- /** Un émetteur reconnu, et la façon dont ses sujets entrent chez nous. */
10
- export interface IExternalIssuerBinding {
11
- /** Émetteur de confiance, sous sa forme canonique. */
12
- issuer: string;
13
- /** Comment le `sub` de CET émetteur devient un identifiant local. */
14
- subjectMapping: ExternalSubjectMapping;
15
- }
16
- /** Ce que la fabrique doit fournir à l'authenticator. */
17
- export interface IExternalJwtAuthenticatorOptions {
18
- /**
19
- * Émetteurs de confiance, et leur politique de sujet.
20
- *
21
- * La liste sert à deux choses, qu'il ne faut pas confondre : reconnaître les
22
- * jetons qui relèvent de cet authenticator (aiguillage — elle n'accorde
23
- * rien, le vérificateur refait le contrôle sur sa propre liste), et savoir
24
- * dans quel espace de noms lire le sujet de chacun.
25
- */
26
- issuers: readonly IExternalIssuerBinding[];
27
- /** Politique de rattachement du sujet à un utilisateur local. */
28
- subjectPolicy: ExternalSubjectPolicy;
29
- /** Rôles accordés en mode `ephemeral`. */
30
- ephemeralRoles: readonly string[];
31
- }
6
+ import { type IExternalJwtAuthenticatorOptions } from "./ExternalSubjectResolver.js";
7
+ export type { ExternalSubjectPolicy, IExternalIssuerBinding, IExternalJwtAuthenticatorOptions, } from "./ExternalSubjectResolver.js";
32
8
  /**
33
9
  * Authentification par **jeton d'accès émis par un serveur d'autorisation
34
10
  * TIERS** (Keycloak, Auth0, Entra, ou l'émetteur d'une flotte d'agents).
@@ -0,0 +1,99 @@
1
+ import { type Container } from "nodefony";
2
+ import { type IUser } from "@nodefony/user";
3
+ import type { ISecuredArea } from "../../contracts/ISecuredArea.js";
4
+ import { type ExternalSubjectMapping } from "./externalSubject.js";
5
+ import type { ISecurityConfig } from "../../config/defineModuleConfig.js";
6
+ import { type IProviderRoleMapping } from "../oauth/providerRoles.js";
7
+ /** Comment le sujet d'un jeton devient un utilisateur de cette application. */
8
+ export type ExternalSubjectPolicy = "require" | "ephemeral";
9
+ /** Un émetteur reconnu, et la façon dont ses sujets entrent chez nous. */
10
+ export interface IExternalIssuerBinding {
11
+ /** Émetteur de confiance, sous sa forme canonique. */
12
+ issuer: string;
13
+ /** Comment le `sub` de CET émetteur devient un identifiant local. */
14
+ subjectMapping: ExternalSubjectMapping;
15
+ }
16
+ /** Ce que la fabrique doit fournir à l'authenticator. */
17
+ export interface IExternalJwtAuthenticatorOptions {
18
+ /**
19
+ * Émetteurs de confiance, et leur politique de sujet.
20
+ *
21
+ * La liste sert à deux choses, qu'il ne faut pas confondre : reconnaître les
22
+ * jetons qui relèvent de cet authenticator (aiguillage — elle n'accorde
23
+ * rien, le vérificateur refait le contrôle sur sa propre liste), et savoir
24
+ * dans quel espace de noms lire le sujet de chacun.
25
+ */
26
+ issuers: readonly IExternalIssuerBinding[];
27
+ /** Politique de rattachement du sujet à un utilisateur local. */
28
+ subjectPolicy: ExternalSubjectPolicy;
29
+ /** Rôles accordés en mode `ephemeral`. */
30
+ ephemeralRoles: readonly string[];
31
+ /**
32
+ * Fournisseurs de CONNEXION (`oauth2.providers`) qui déclarent leur émetteur.
33
+ *
34
+ * Quand l'émetteur d'un jeton est celui d'un fournisseur de connexion, la
35
+ * même personne a pu se connecter par le navigateur : son compte local porte
36
+ * alors le lien `(fournisseur, sub)`. Le chercher d'abord évite qu'un jeton
37
+ * d'API désigne un AUTRE compte que la session de son porteur.
38
+ */
39
+ oauthProviders?: readonly {
40
+ name: string;
41
+ issuer: string;
42
+ /**
43
+ * Table de correspondance des rôles du fournisseur, compilée — absente ou
44
+ * `null` : les rôles du compte lié restent locaux.
45
+ */
46
+ roleMapping?: IProviderRoleMapping | null;
47
+ }[];
48
+ }
49
+ /**
50
+ * Les options de rattachement des sujets externes, lues dans la configuration
51
+ * de sécurité — UNE lecture, pour la zone `external-jwt` comme pour le
52
+ * résolveur de compte d'une ressource protégée.
53
+ *
54
+ * @param config - configuration de sécurité validée
55
+ * @returns émetteurs, politique de sujet, fournisseurs de connexion liables
56
+ */
57
+ export declare function externalSubjectOptions(config: ISecurityConfig): IExternalJwtAuthenticatorOptions;
58
+ /**
59
+ * Établit l'utilisateur applicatif que désigne le sujet d'un jeton TIERS —
60
+ * la décision qui transforme « un annuaire a authentifié ce sujet » en « ce
61
+ * compte agit chez nous ».
62
+ *
63
+ * UNE implémentation, deux consommateurs : la zone `external-jwt` du pare-feu
64
+ * (`ExternalJwtAuthenticator`) et le résolveur de compte d'une ressource
65
+ * protégée (porte MCP). Recopiée, la règle d'espace de noms aurait divergé dès
66
+ * le premier émetteur ajouté — et c'est elle qui empêche un annuaire tiers de
67
+ * réclamer le compte local `admin`.
68
+ */
69
+ export declare class ExternalSubjectResolver {
70
+ #private;
71
+ /**
72
+ * @param container - container DI (résolution lazy de `users`)
73
+ * @param options - émetteurs reconnus et politique de rattachement
74
+ */
75
+ constructor(container: Container, options: IExternalJwtAuthenticatorOptions);
76
+ /**
77
+ * L'émetteur revendiqué est-il l'un des nôtres ? Ne lève JAMAIS : la valeur
78
+ * vient d'un jeton que n'importe qui peut forger.
79
+ *
80
+ * @param issuer - émetteur lu dans le jeton, non vérifié
81
+ */
82
+ recognizes(issuer: string): boolean;
83
+ /**
84
+ * Établit l'utilisateur applicatif à partir du sujet d'un jeton VÉRIFIÉ.
85
+ *
86
+ * Les deux politiques répondent à deux questions différentes : « qui, chez
87
+ * nous, est cette personne ? » et « quel pouvoir accorde-t-on à une machine
88
+ * que l'annuaire a authentifiée ? ».
89
+ *
90
+ * @param issuer - émetteur canonique, tel que le vérificateur l'a établi
91
+ * @param subject - sujet brut (`sub`) chez cet émetteur
92
+ * @param claims - revendications du jeton (rôles d'annuaire)
93
+ * @param area - zone qui SURCHARGE la politique, s'il y en a une
94
+ * @throws AuthenticationError (401) — sujet sans compte local utilisable
95
+ * @throws UnverifiableTokenError (503) — émetteur vérifié mais absent de la
96
+ * table de rattachement : configuration incohérente
97
+ */
98
+ resolve(issuer: string, subject: string, claims: Readonly<Record<string, unknown>> | undefined, area?: ISecuredArea): Promise<IUser>;
99
+ }
@@ -1,3 +1,4 @@
1
+ import type { IUserProvider } from "@nodefony/user";
1
2
  import type { IRealtimeAuthenticator, IRealtimeHandshake, IRealtimeToken } from "../realtime/realtimeContracts.js";
2
3
  /**
3
4
  * Authenticator realtime des identités résolues par le **firewall** — équivalent
@@ -50,8 +51,10 @@ export declare class FirewallRealtimeAuthenticator implements IRealtimeAuthentic
50
51
  * @param resolveStore - fournit le store de révocation des jetons (le firewall
51
52
  * passe une closure sur son container). Omis → mode dégradé documenté :
52
53
  * seules les bornes portées par le jeton lui-même sont vérifiables.
54
+ * @param resolveUsers - fournit l'annuaire des comptes (`users`). Omis → pas
55
+ * de confirmation du compte avant `api.request`.
53
56
  */
54
- constructor(resolveStore?: (() => IRealtimeRevocationStore | null) | null);
57
+ constructor(resolveStore?: (() => IRealtimeRevocationStore | null) | null, resolveUsers?: (() => IUserProvider | null) | null);
55
58
  /** Une identité authentifiée a-t-elle été résolue (par le firewall) au handshake ? */
56
59
  supports(_handshake: IRealtimeHandshake): boolean;
57
60
  /**
@@ -62,6 +65,19 @@ export declare class FirewallRealtimeAuthenticator implements IRealtimeAuthentic
62
65
  * filet défensif fail-closed → le hub ferme la socket en 4001).
63
66
  */
64
67
  authenticate(_handshake: IRealtimeHandshake): Promise<IRealtimeToken>;
68
+ /**
69
+ * Le revalidateur de l'identité que le firewall a posée sur la requête
70
+ * COURANTE (ALS), pour toute connexion longue — socket WebSocket ou flux SSE.
71
+ *
72
+ * Une seule règle décide qu'une identité est morte, quel que soit le
73
+ * transport qui la porte : c'est celle-ci, et le hub comme le noyau HTTP
74
+ * l'appellent au lieu de la recopier.
75
+ *
76
+ * @returns `null` pour un anonyme (rien à révoquer), sinon la fonction qui
77
+ * dit si l'identité est TOUJOURS valable — fail-closed : `false` quand elle
78
+ * ne peut pas le prouver.
79
+ */
80
+ currentRevalidator(): ((nowMs?: number) => Promise<boolean>) | null;
65
81
  }
66
82
  /**
67
83
  * Surface MINIMALE du store de jetons consommée ici : les deux lectures qui
@@ -1,5 +1,6 @@
1
- import type { Container } from "nodefony";
1
+ import type { Container, IAccessPrincipal } from "nodefony";
2
2
  import type { ContextType } from "@nodefony/http";
3
+ import type { IUser } from "@nodefony/user";
3
4
  import type { IAuthenticator } from "../../contracts/IAuthenticator.js";
4
5
  import type { IToken } from "../../contracts/IToken.js";
5
6
  import type { IJwtRuntime } from "../token/jwtRuntime.js";
@@ -59,6 +60,41 @@ export declare class JwtAuthenticator implements IAuthenticator {
59
60
  * firewall puis 401 fail-closed (rien ne fuite au client).
60
61
  */
61
62
  authenticate(token: IToken): Promise<IToken>;
63
+ /**
64
+ * Vérifie un jeton émis par CETTE application pour une ressource protégée
65
+ * nommée par l'appelant — la porte d'une ressource (RFC 8707), pas une zone.
66
+ *
67
+ * Les MÊMES règles que {@link authenticate}, appliquées par la même fonction :
68
+ * signature par le keyset local, `iss`, `typ: at+jwt`, révocation ciblée
69
+ * (`jti`) et en masse (`invalidBefore`), compte encore actif. Seule
70
+ * l'audience diffère : c'est celle de la ressource qui appelle, et elle est
71
+ * exigée. Un jeton révoqué, ou celui d'un compte désactivé, n'ouvre donc pas
72
+ * plus une porte MCP qu'une route — ce que la vérification par le seul jeu de
73
+ * clés publiques laissait passer jusqu'à l'expiration.
74
+ *
75
+ * @param raw - le jeton présenté, brut
76
+ * @param audience - l'URI canonique de la ressource qui le reçoit
77
+ * @returns le principal établi, ou `null` pour tout refus (contrat
78
+ * `IAccessTokenVerifier` : un refus n'est jamais une exception)
79
+ * @throws Error si le câblage manque (keystore, store, annuaire) — la porte
80
+ * le traduit en `503`, jamais en jeton accepté
81
+ */
82
+ verifyForResource(raw: string, audience: string): Promise<IAccessPrincipal | null>;
83
+ /**
84
+ * Recharge le compte que désigne un principal établi par
85
+ * {@link verifyForResource} — pour une ressource protégée qui fait agir un
86
+ * outil au nom du propriétaire du jeton.
87
+ *
88
+ * La MÊME règle que la zone ({@link loadBoundAccount}), rejouée sur les
89
+ * revendications du principal : le vérificateur ne garde pas le compte qu'il
90
+ * a lu, et une seconde lecture de l'annuaire est le coût accepté d'un outil
91
+ * qui la demande — les autres ne la paient pas.
92
+ *
93
+ * @param principal - principal d'un jeton émis par CETTE application
94
+ * @returns le compte, ou `null` s'il n'est pas (ou plus) celui du jeton
95
+ * @throws Error si l'annuaire des comptes n'est pas câblé
96
+ */
97
+ resolveAccount(principal: IAccessPrincipal): Promise<IUser | null>;
62
98
  /** Slot audit (J4b). */
63
99
  onSuccess(_context: ContextType, _token: IToken): Promise<void>;
64
100
  /** Slot audit (J4b) — le 401 + challenge sont posés par le firewall. */
@@ -3,6 +3,7 @@ import type { IPasswordVerifier } from "@nodefony/user";
3
3
  import type { IAuthenticator } from "../../contracts/IAuthenticator.js";
4
4
  import type { IToken } from "../../contracts/IToken.js";
5
5
  import type { LoginThrottler } from "../throttle/LoginThrottler.js";
6
+ import type { CredentialTransportPolicy } from "../transport/CredentialTransportPolicy.js";
6
7
  /**
7
8
  * Authentification par identifiant + mot de passe — schéma **HTTP Basic**
8
9
  * (RFC 7617) : `Authorization: Basic base64(identifiant:motdepasse)`, charset
@@ -25,11 +26,18 @@ export declare class UserPasswordAuthenticator implements IAuthenticator {
25
26
  * @param resolveVerifier - résolution lazy de la source de vérification
26
27
  * (typiquement `container.get("users")`) — appelée au premier login.
27
28
  * @param throttler - limiteur de tentatives (backoff NIST), `null` = désactivé.
29
+ * @param transport - politique du transport des secrets, `null` = aucune.
28
30
  */
29
- constructor(resolveVerifier: () => IPasswordVerifier, throttler?: LoginThrottler | null);
31
+ constructor(resolveVerifier: () => IPasswordVerifier, throttler?: LoginThrottler | null, transport?: CredentialTransportPolicy | null);
30
32
  /** La requête porte-t-elle un en-tête `Authorization: Basic ...` ? */
31
33
  supports(context: ContextType): boolean;
32
- /** Décode l'enveloppe Basic — un contenu malformé donne un credential vide (échec uniforme). */
34
+ /**
35
+ * Décode l'enveloppe Basic — un contenu malformé donne un credential vide
36
+ * (échec uniforme).
37
+ *
38
+ * @throws InsecureTransportError (403) — en production, en-tête reçu hors
39
+ * TLS : refusé avant même d'être décodé.
40
+ */
33
41
  createToken(context: ContextType): Promise<IToken>;
34
42
  /**
35
43
  * Vérifie le credential via le verifier ou lève un 401 au message uniforme.
@@ -47,7 +55,7 @@ export declare class UserPasswordAuthenticator implements IAuthenticator {
47
55
  onSuccess(_context: ContextType, _token: IToken): Promise<void>;
48
56
  /** Slot J3+ (audit events). Le throttling vit dans `authenticate` (clé = identifiant) ; le 401 + challenge sont posés par le firewall. */
49
57
  onFailure(_context: ContextType, _error: Error): Promise<void>;
50
- /** Challenge RFC 7235 posé par le firewall sur les 401 de la zone. */
58
+ /** Challenge RFC 9110 §11 posé par le firewall sur les 401 de la zone. */
51
59
  challenge(): string;
52
60
  }
53
61
  export default UserPasswordAuthenticator;
@@ -0,0 +1,16 @@
1
+ import type { IBootNotice, IFirewallZoneView } from "nodefony";
2
+ /**
3
+ * Le point d'attention « routes métier publiques » du bilan de démarrage.
4
+ *
5
+ * Hors d'une zone du pare-feu, une requête n'est jamais authentifiée : son
6
+ * identité n'est pas résolue, et une route métier qu'on croyait protégée
7
+ * répond à n'importe qui. Les seules zones présentes sont alors les aires du
8
+ * framework (`/nodefony/*`) ; on le DIT au démarrage, avec la recette.
9
+ *
10
+ * La classification « applicative ou framework » n'est pas recopiée : elle
11
+ * vient du cœur (`isFrameworkZone`), qui l'applique aussi au tableau des zones.
12
+ *
13
+ * @param zones - zones montées (`firewall.describe().zones`).
14
+ * @returns le point à déclarer, ou `null` si une zone applicative existe.
15
+ */
16
+ export declare function publicRoutesNotice(zones: readonly IFirewallZoneView[]): IBootNotice | null;
@@ -0,0 +1,37 @@
1
+ /** Ce que la décision lit d'une requête refusée — rien de plus. */
2
+ export interface IEntryPointRequest {
3
+ /** Méthode HTTP (`GET`, `POST`…). */
4
+ readonly method: string | undefined;
5
+ /** En-tête `Accept`. */
6
+ readonly accept: string | undefined;
7
+ /** En-tête `Sec-Fetch-Mode` (Fetch Metadata), absent d'un client ancien. */
8
+ readonly fetchMode: string | undefined;
9
+ /** Chemin et requête demandés (`/compte?onglet=2`), jamais l'hôte. */
10
+ readonly path: string;
11
+ }
12
+ /**
13
+ * Dit si une requête refusée est la NAVIGATION d'un navigateur — le seul cas
14
+ * où conduire à la page de connexion aide, au lieu de casser un client.
15
+ *
16
+ * `GET` ou `HEAD`, `Accept` qui demande du HTML, et `Sec-Fetch-Mode: navigate`
17
+ * quand l'en-tête est présent : un `fetch()` d'une page porte `cors` ou
18
+ * `same-origin` même s'il accepte du HTML, et recevrait une page de connexion
19
+ * à la place de ses données.
20
+ *
21
+ * @param request - la requête refusée
22
+ * @returns `true` pour une navigation
23
+ */
24
+ export declare function isBrowserNavigation(request: IEntryPointRequest): boolean;
25
+ /**
26
+ * L'adresse de la page de connexion pour un visiteur refusé :
27
+ * `<entryPoint>?from=<chemin demandé>`, composée par `loginPagePath` (cœur).
28
+ * Le chemin demandé repasse par `safeRedirectPath` — la seule garde
29
+ * anti-redirection ouverte du framework, que la page de connexion appliquera
30
+ * de nouveau au retour.
31
+ *
32
+ * @param entryPoint - chemin de la page de connexion (vérifié au boot)
33
+ * @param request - la requête refusée
34
+ * @returns l'adresse du `Location`, ou `null` quand ce n'est pas une
35
+ * navigation, ou quand la page demandée EST la page de connexion
36
+ */
37
+ export declare function entryPointLocation(entryPoint: string, request: IEntryPointRequest): string | null;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Rend un échec de transport LISIBLE : `fetch` (undici) ne dit que
3
+ * `fetch failed` et range la vraie raison dans `cause` — certificat refusé,
4
+ * connexion refusée, nom introuvable.
5
+ *
6
+ * @param error - ce que `fetch` (ou une bibliothèque qui l'appelle) a levé
7
+ * @returns `message (CODE : raison)`, ou le message seul faute de cause
8
+ */
9
+ export declare function describeTransportFailure(error: unknown): string;
10
+ /**
11
+ * Le remède d'un certificat signé par une autorité que le processus ne
12
+ * reconnaît pas — `null` pour tout autre échec.
13
+ *
14
+ * En développement, Nodefony reconnaît d'office l'autorité qu'il a générée ; en
15
+ * production jamais, et c'est voulu. Sans ce remède, le journal ne montrait que
16
+ * `SELF_SIGNED_CERT_IN_CHAIN` : la variable qui le règle n'était écrite que dans
17
+ * un commentaire du `.env`, fichier qu'un serveur de production n'a pas.
18
+ *
19
+ * @param error - ce que `fetch` (ou une bibliothèque qui l'appelle) a levé
20
+ * @returns une phrase d'action, ou `null`
21
+ */
22
+ export declare function untrustedAuthorityHint(error: unknown): string | null;
@@ -0,0 +1,54 @@
1
+ import type { OAuth2ClientAuthMethod, OAuth2Tokens } from "./oauth2Client.js";
2
+ /**
3
+ * Ce qu'un programme présente pour obtenir un jeton EN SON PROPRE NOM
4
+ * (RFC 6749 §4.4) — un script, un service partenaire, un agent : aucun
5
+ * utilisateur, aucun navigateur.
6
+ */
7
+ export interface IClientCredentialsRequest {
8
+ /**
9
+ * Identifiant de l'émetteur (claim `iss`), en https. Le point de jeton en est
10
+ * DÉCOUVERT (RFC 8414), jamais écrit à côté : un point de jeton recopié
11
+ * survit au déménagement de l'émetteur et envoie le secret ailleurs.
12
+ */
13
+ readonly issuer: string;
14
+ /** Identifiant du client confidentiel (ex. `<app>-machine` chez Keycloak). */
15
+ readonly clientId: string;
16
+ /** Secret du client — obligatoire : ce flux n'existe que pour un client confidentiel. */
17
+ readonly clientSecret: string;
18
+ /**
19
+ * Comment le client s'authentifie. Défaut `client_secret_basic`, la méthode
20
+ * que la RFC 6749 §2.3.1 rend obligatoire côté serveur. `none` n'existe pas
21
+ * ici : un client public ne peut pas obtenir de jeton en son nom.
22
+ */
23
+ readonly clientAuthMethod?: Exclude<OAuth2ClientAuthMethod, "none">;
24
+ /** Portées demandées ; aucune par défaut (le serveur applique les siennes). */
25
+ readonly scopes?: readonly string[];
26
+ /**
27
+ * Ressource visée (RFC 8707 §2) — l'audience que le jeton doit porter. Un
28
+ * serveur qui ne la connaît pas l'ignore ; Keycloak, lui, inscrit l'audience
29
+ * par un mapper déclaré sur le client.
30
+ */
31
+ readonly resource?: string;
32
+ /** Adresse où lire les métadonnées, si elle diffère de l'émetteur (cf `IDiscoveryOptions`). */
33
+ readonly discoveryUrl?: string | undefined;
34
+ /** Implémentation de `fetch` (banc sans réseau). */
35
+ readonly fetch?: typeof globalThis.fetch;
36
+ /** Délai d'attente de CHAQUE requête (découverte, puis jeton), en millisecondes. */
37
+ readonly timeoutMs?: number;
38
+ }
39
+ /**
40
+ * Obtient un jeton d'accès pour un programme, par le flux `client_credentials`
41
+ * (RFC 6749 §4.4), auprès d'un émetteur découvert (RFC 8414).
42
+ *
43
+ * C'est la voie de l'appelant MACHINE : il s'authentifie avec son propre
44
+ * secret, et le jeton rendu le désigne lui, pas un utilisateur. La même
45
+ * authentification de client que l'échange de code ({@link postTokenRequest}) —
46
+ * une seule implémentation, pour qu'aucune règle ne diverge d'un flux à l'autre.
47
+ *
48
+ * @param request - émetteur, identité du client, portées et ressource.
49
+ * @returns Les jetons rendus ; `accessToken()` est celui à présenter en `Bearer`.
50
+ * @throws Error - secret vide, émetteur invalide ou injoignable, métadonnées discordantes.
51
+ * @throws OAuth2RequestError - le serveur a refusé (`invalid_client` : secret
52
+ * faux ; `unauthorized_client` : le client n'a pas droit à ce flux).
53
+ */
54
+ export declare function requestClientCredentialsToken(request: IClientCredentialsRequest): Promise<OAuth2Tokens>;
@@ -1,11 +1,3 @@
1
- /**
2
- * Lecture BORNÉE d'un corps JSON — la brique de transport commune aux trois
3
- * appels sortants du social login (découverte, point de jeton, API d'un
4
- * fournisseur non-OIDC).
5
- *
6
- * Elle existe parce qu'une borne posée APRÈS `response.text()` ne protège plus
7
- * rien : le corps est déjà entièrement en mémoire quand on mesure sa longueur.
8
- */
9
1
  /**
10
2
  * Lit un corps JSON en refusant de dépasser une taille — la borne est vérifiée
11
3
  * PENDANT la lecture, pas après : un corps déjà entièrement en mémoire ne se
@@ -23,6 +23,19 @@ export interface IDiscoveredAuthorizationServer {
23
23
  * alors pas exiger ce qu'il n'a pas promis.
24
24
  */
25
25
  readonly issParameterSupported: boolean;
26
+ /**
27
+ * Point de déconnexion initiée par l'application (`end_session_endpoint`,
28
+ * OpenID Connect RP-Initiated Logout 1.0 §2.1), ou `null` si le serveur n'en
29
+ * publie aucun : la déconnexion reste alors locale.
30
+ */
31
+ readonly endSessionEndpoint: string | null;
32
+ /**
33
+ * Algorithmes de signature des ID tokens annoncés
34
+ * (`id_token_signing_alg_values_supported`, OpenID Connect Discovery §3), ou
35
+ * `null` si le serveur n'en publie aucun. Ce sont aussi ceux des jetons de
36
+ * déconnexion (OpenID Connect Back-Channel Logout 1.0 §2.6).
37
+ */
38
+ readonly idTokenSigningAlgValuesSupported: string[] | null;
26
39
  }
27
40
  /** Réglages de la découverte — l'injection de `fetch` est la voie pour éprouver sans TLS. */
28
41
  export interface IDiscoveryOptions {
@@ -36,6 +49,16 @@ export interface IDiscoveryOptions {
36
49
  readonly fetch?: typeof globalThis.fetch;
37
50
  /** Délai d'attente par URL candidate, en millisecondes. */
38
51
  readonly timeoutMs?: number;
52
+ /**
53
+ * Adresse où LIRE le document, quand elle diffère de l'émetteur — le service
54
+ * interne d'un cluster (`http://keycloak.sso:8080/realms/x/.well-known/openid-configuration`).
55
+ *
56
+ * @remarks Seul le lieu de lecture change : le document doit toujours se
57
+ * déclarer de l'émetteur attendu (RFC 8414 §3.3). Les points de serveur à
58
+ * serveur (jeton, clés) peuvent alors être en `http` sur l'origine exacte de
59
+ * cette adresse ; ceux vers lesquels le navigateur est envoyé restent en `https`.
60
+ */
61
+ readonly discoveryUrl?: string | undefined;
39
62
  }
40
63
  /**
41
64
  * Interroge un serveur d'autorisation et rend ses points d'entrée.
@@ -47,7 +70,7 @@ export interface IDiscoveryOptions {
47
70
  * d'entrée — la même attaque que le paramètre `iss` couvre au retour (RFC 9207).
48
71
  *
49
72
  * @param rawIssuer - identifiant d'émetteur tel qu'écrit en configuration.
50
- * @param options - transport injectable et délai d'attente.
73
+ * @param options - transport injectable, délai d'attente, adresse de découverte.
51
74
  * @returns Les points d'entrée, prêts pour `OAuth2Client`.
52
75
  * @throws Error - émetteur mal formé, document introuvable, incomplet, ou `issuer` discordant.
53
76
  */
@@ -25,7 +25,16 @@ export declare class OAuth2RequestError extends Error {
25
25
  readonly code: string;
26
26
  /** Description lisible fournie par le serveur, ou `null`. */
27
27
  readonly description: string | null;
28
- constructor(code: string, description: string | null);
28
+ /**
29
+ * Statut HTTP de la réponse, ou `null` s'il n'est pas connu.
30
+ *
31
+ * Le code `error` ne suffit pas toujours à dire QUI est refusé : la RFC 6749
32
+ * §5.2 réserve `401` à l'échec d'authentification du client, et Keycloak
33
+ * répond `unauthorized_client` en `401` sur un secret faux — le même code
34
+ * qu'en `400` pour un flux que le client n'a pas le droit d'employer.
35
+ */
36
+ readonly status: number | null;
37
+ constructor(code: string, description: string | null, status?: number | null);
29
38
  }
30
39
  /**
31
40
  * Jetons rendus par le point de jeton (RFC 6749 §5.1), enveloppés pour qu'un champ
@@ -88,6 +97,16 @@ export interface IAuthorizationRequest {
88
97
  readonly codeVerifier: string | null;
89
98
  /** Portées demandées ; aucune n'est ajoutée d'office. */
90
99
  readonly scopes: readonly string[];
100
+ /**
101
+ * Thème d'affichage que l'application a rendu (clair ou sombre), pour que
102
+ * l'écran du fournisseur le reprenne.
103
+ *
104
+ * Aucun paramètre normalisé ne le porte (OIDC n'a que `ui_locales`, pour la
105
+ * langue) : seul un fournisseur qui sait le lire le traduit — Keycloak, en
106
+ * `nf_theme` que lit le thème livré. Les autres l'ignorent, et ne le
107
+ * reçoivent jamais : un paramètre inconnu peut être refusé.
108
+ */
109
+ readonly theme?: "light" | "dark";
91
110
  /**
92
111
  * Paramètres versés TELS QUELS dans la requête d'autorisation.
93
112
  *
@@ -172,3 +191,33 @@ export declare class OAuth2Client {
172
191
  */
173
192
  validateAuthorizationCode(request: ITokenRequest): Promise<OAuth2Tokens>;
174
193
  }
194
+ /** Ce qu'il faut savoir du client pour parler au point de jeton. */
195
+ export interface ITokenEndpointClient {
196
+ /** Point de jeton (RFC 6749 §3.2). */
197
+ readonly tokenEndpoint: string;
198
+ /** Identifiant client délivré par le serveur d'autorisation. */
199
+ readonly clientId: string;
200
+ /** Secret client — vide seulement pour `none`. */
201
+ readonly clientSecret: string;
202
+ /** Comment le client s'authentifie (RFC 6749 §2.3). */
203
+ readonly clientAuthMethod: OAuth2ClientAuthMethod;
204
+ /** Implémentation de `fetch` (banc sans réseau). */
205
+ readonly fetch?: typeof globalThis.fetch | undefined;
206
+ /** Délai d'attente, en millisecondes. */
207
+ readonly timeoutMs?: number | undefined;
208
+ }
209
+ /**
210
+ * Présente une requête au point de jeton et rend les jetons (RFC 6749 §5.1).
211
+ *
212
+ * SEULE implémentation de l'authentification du client et de la lecture de la
213
+ * réponse : l'échange de code et le flux `client_credentials` y passent tous
214
+ * deux, pour qu'une règle (méthode déclarée, redirection refusée, réponse
215
+ * bornée, refus lu sur `error`) ne diverge jamais entre deux flux.
216
+ *
217
+ * @param client - point de jeton et identité du client.
218
+ * @param body - corps de la requête, `grant_type` compris ; l'identité du
219
+ * client y est ajoutée selon la méthode déclarée.
220
+ * @throws OAuth2RequestError - le serveur a refusé, en nommant la cause (RFC 6749 §5.2).
221
+ * @throws Error - réponse inintelligible, hors gabarit, ou serveur injoignable.
222
+ */
223
+ export declare function postTokenRequest(client: ITokenEndpointClient, body: URLSearchParams): Promise<OAuth2Tokens>;
@@ -14,7 +14,11 @@ import type { OAuth2ClientAuthMethod } from "./oauth2Client.js";
14
14
  * en une ligne dans l'application :
15
15
  *
16
16
  * ```ts
17
- * registerOAuthProvider("azure", (ctx) => createDiscoveredOidcProvider("azure", ctx));
17
+ * registerOAuthProvider(
18
+ * "azure",
19
+ * (ctx) => createDiscoveredOidcProvider("azure", ctx),
20
+ * { requiresIssuer: true }, // un émetteur oublié refuse le démarrage
21
+ * );
18
22
  * ```
19
23
  */
20
24
  /** Contexte de construction d'un fournisseur (secrets et URL issus de la config). */
@@ -42,6 +46,11 @@ export interface IOAuthProviderContext {
42
46
  * l'émetteur est connu d'avance (Google) ou qui n'en publient pas (GitHub).
43
47
  */
44
48
  readonly issuer?: string | undefined;
49
+ /**
50
+ * Adresse où lire les métadonnées de l'émetteur quand elle diffère de lui
51
+ * (service interne d'un cluster) — cf `IDiscoveryOptions.discoveryUrl`.
52
+ */
53
+ readonly discoveryUrl?: string | undefined;
45
54
  }
46
55
  /**
47
56
  * Fabrique d'un fournisseur OAuth pour un nom donné.
@@ -51,12 +60,47 @@ export interface IOAuthProviderContext {
51
60
  * (le service mémoïse le fournisseur résolu).
52
61
  */
53
62
  export type OAuthProviderFactory = (ctx: IOAuthProviderContext) => IOAuthProvider | Promise<IOAuthProvider>;
63
+ /**
64
+ * Ce qu'une fabrique EXIGE de la configuration — lu au démarrage, avant que le
65
+ * moindre utilisateur ne clique.
66
+ *
67
+ * @remarks La fabrique est une fonction opaque : rien, à sa seule lecture, ne
68
+ * dit qu'elle lèvera faute d'émetteur. Sans déclaration, l'oubli ne se
69
+ * découvrait qu'au premier login.
70
+ */
71
+ export interface IOAuthProviderRegistration {
72
+ /**
73
+ * `true` si le fournisseur ne peut se construire sans `issuer` dans sa
74
+ * configuration — typiquement un serveur OpenID Connect découvert par son
75
+ * émetteur. Absent : l'émetteur est facultatif (connu d'avance, ou inutile).
76
+ */
77
+ readonly requiresIssuer?: boolean;
78
+ }
54
79
  /**
55
80
  * Enregistre (ou remplace) la fabrique d'un fournisseur OAuth. Appelée par les
56
81
  * builtins au chargement, et par une application pour ses fournisseurs.
82
+ *
83
+ * @param name - nom sous lequel le fournisseur se configure (`oauth2.providers.<name>`).
84
+ * @param factory - fabrique du fournisseur.
85
+ * @param registration - exigences de configuration, vérifiées au démarrage.
57
86
  */
58
- export declare function registerOAuthProvider(name: string, factory: OAuthProviderFactory): void;
87
+ export declare function registerOAuthProvider(name: string, factory: OAuthProviderFactory, registration?: IOAuthProviderRegistration): void;
59
88
  /** Fabrique d'un fournisseur par nom, ou `undefined` si inconnu. */
60
89
  export declare function getOAuthProviderFactory(name: string): OAuthProviderFactory | undefined;
90
+ /** `true` si le fournisseur nommé a déclaré exiger un émetteur en configuration. */
91
+ export declare function oauthProviderRequiresIssuer(name: string): boolean;
61
92
  /** Noms enregistrés (validation boot, introspection Studio, tests). */
62
93
  export declare function listOAuthProviders(): string[];
94
+ /**
95
+ * Paramètre d'autorisation que lit le thème Keycloak livré (`template.ftl`)
96
+ * pour afficher l'écran dans le thème de l'application (`light` | `dark`).
97
+ */
98
+ export declare const KEYCLOAK_THEME_PARAMETER = "nf_theme";
99
+ /**
100
+ * Ajoute le thème demandé ({@link IAuthorizationRequest.theme}) à l'URL
101
+ * d'autorisation d'un fournisseur Keycloak, sous {@link KEYCLOAK_THEME_PARAMETER}.
102
+ *
103
+ * @param provider - fournisseur OIDC découvert, inchangé pour tout le reste
104
+ * @returns le même fournisseur, dont l'URL d'autorisation porte le thème
105
+ */
106
+ export declare function withKeycloakThemeHint(provider: IOAuthProvider): IOAuthProvider;