@nodefony/framework 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 (76) hide show
  1. package/README.md +4 -0
  2. package/dist/_virtual/{_@oxc-project_runtime@0.152.0 → _@oxc-project_runtime@0.155.0}/helpers/esm/decorate.js +1 -2
  3. package/dist/index.js +15 -20
  4. package/dist/nodefony/config/config.js +1 -2
  5. package/dist/nodefony/config/defineModuleConfig.js +1 -2
  6. package/dist/nodefony/controller/AdminApiController.js +45 -11
  7. package/dist/nodefony/controller/ApiKeyController.js +18 -10
  8. package/dist/nodefony/controller/BenchController.js +1 -2
  9. package/dist/nodefony/controller/IssuerMetadataController.js +1 -2
  10. package/dist/nodefony/controller/LoginPageController.js +117 -0
  11. package/dist/nodefony/controller/OAuth2Controller.js +133 -17
  12. package/dist/nodefony/controller/ProtectedResourceMetadataController.js +1 -2
  13. package/dist/nodefony/controller/SessionAuthController.js +50 -13
  14. package/dist/nodefony/controller/TokenAuthController.js +14 -8
  15. package/dist/nodefony/controller/TotpController.js +7 -8
  16. package/dist/nodefony/controller/WebAuthnController.js +28 -12
  17. package/dist/nodefony/controller/oauthAuthority.js +1 -2
  18. package/dist/nodefony/decorators/routerDecorators.js +268 -65
  19. package/dist/nodefony/service/AdminBroker.js +4 -6
  20. package/dist/nodefony/service/Eta.js +1 -2
  21. package/dist/nodefony/service/IdempotencyStore.js +4 -6
  22. package/dist/nodefony/service/router.js +97 -34
  23. package/dist/nodefony/src/Controller.js +71 -43
  24. package/dist/nodefony/src/FrameworkAdminApi.js +33 -11
  25. package/dist/nodefony/src/KernelAdminApi.js +82 -18
  26. package/dist/nodefony/src/PlaygroundAdminApi.js +72 -3
  27. package/dist/nodefony/src/RedisIdempotencyStore.js +46 -4
  28. package/dist/nodefony/src/Resolver.js +105 -36
  29. package/dist/nodefony/src/ResourceController.js +2 -3
  30. package/dist/nodefony/src/Route.js +54 -8
  31. package/dist/nodefony/src/SecurityApiController.js +23 -0
  32. package/dist/nodefony/src/SyslogAdminApi.js +1 -2
  33. package/dist/nodefony/src/Template.js +1 -2
  34. package/dist/nodefony/src/configMutation.js +1 -2
  35. package/dist/nodefony/src/docsReader.js +39 -23
  36. package/dist/nodefony/src/idempotency.js +14 -22
  37. package/dist/nodefony/src/idempotencyGc.js +1 -2
  38. package/dist/nodefony/src/idempotencyStoreRegistry.js +1 -2
  39. package/dist/nodefony/src/loginPage.js +430 -0
  40. package/dist/nodefony/src/requireAuthentication.js +35 -0
  41. package/dist/nodefony/src/routeSchema.js +81 -0
  42. package/dist/nodefony/src/scopeCatalog.js +1 -2
  43. package/dist/nodefony/src/singletonGuard.js +1 -2
  44. package/dist/nodefony/src/syslogFilters.js +1 -2
  45. package/dist/types/index.d.ts +8 -12
  46. package/dist/types/nodefony/controller/AdminApiController.d.ts +12 -0
  47. package/dist/types/nodefony/controller/ApiKeyController.d.ts +2 -2
  48. package/dist/types/nodefony/controller/LoginPageController.d.ts +32 -0
  49. package/dist/types/nodefony/controller/OAuth2Controller.d.ts +67 -8
  50. package/dist/types/nodefony/controller/SessionAuthController.d.ts +46 -8
  51. package/dist/types/nodefony/controller/TokenAuthController.d.ts +6 -4
  52. package/dist/types/nodefony/controller/TotpController.d.ts +2 -2
  53. package/dist/types/nodefony/controller/WebAuthnController.d.ts +12 -4
  54. package/dist/types/nodefony/decorators/routerDecorators.d.ts +221 -35
  55. package/dist/types/nodefony/interfaces/IController.d.ts +2 -3
  56. package/dist/types/nodefony/interfaces/IRoute.d.ts +1 -2
  57. package/dist/types/nodefony/service/router.d.ts +40 -1
  58. package/dist/types/nodefony/src/Controller.d.ts +54 -17
  59. package/dist/types/nodefony/src/PlaygroundAdminApi.d.ts +12 -0
  60. package/dist/types/nodefony/src/Resolver.d.ts +44 -1
  61. package/dist/types/nodefony/src/ResourceController.d.ts +1 -1
  62. package/dist/types/nodefony/src/Route.d.ts +21 -1
  63. package/dist/types/nodefony/src/SecurityApiController.d.ts +17 -0
  64. package/dist/types/nodefony/src/docsReader.d.ts +10 -6
  65. package/dist/types/nodefony/src/idempotency.d.ts +12 -10
  66. package/dist/types/nodefony/src/loginPage.d.ts +144 -0
  67. package/dist/types/nodefony/src/requireAuthentication.d.ts +29 -0
  68. package/dist/types/nodefony/src/routeSchema.d.ts +112 -0
  69. package/docs/admin.md +83 -78
  70. package/docs/controller.md +112 -111
  71. package/docs/decorateurs.md +212 -163
  72. package/docs/idempotence.md +137 -135
  73. package/docs/routing.md +96 -95
  74. package/docs/templates.md +55 -55
  75. package/package.json +10 -13
  76. package/dist/_virtual/_@oxc-project_runtime@0.152.0/helpers/esm/decorateMetadata.js +0 -6
@@ -1,5 +1,17 @@
1
1
  import type { ContextType } from "@nodefony/http";
2
2
  import Controller from "../src/Controller.js";
3
+ type AdminQuery = Record<string, string | string[]>;
4
+ /**
5
+ * Ramène les paramètres d'URL au contrat {@link IAdminRequest.query} : seules
6
+ * les chaînes et les tableaux de chaînes passent. `qs` produit des objets pour
7
+ * `a[b]=1` ; les laisser passer sous un type qui les nie tromperait chaque
8
+ * producteur du plan d'administration.
9
+ *
10
+ * @param source - paramètres d'URL de la requête (`Controller.query`)
11
+ * @returns `source` lui-même quand il respecte déjà le contrat (aucune
12
+ * allocation), sinon une copie sans les valeurs imbriquées
13
+ */
14
+ export declare function toAdminQuery(source: Record<string, unknown> | undefined): AdminQuery;
3
15
  /**
4
16
  * Controller pont unique du data plane admin (Studio).
5
17
  *
@@ -1,6 +1,6 @@
1
1
  import type { Module } from "nodefony";
2
2
  import type { ContextType } from "@nodefony/http";
3
- import Controller from "../src/Controller.js";
3
+ import SecurityApiController from "../src/SecurityApiController.js";
4
4
  /**
5
5
  * Endpoints HTTP de **gestion des clés API personnelles (PAT, P6.12)** — console
6
6
  * « mes clés » façon GitHub, adaptateurs MINCES au-dessus du service `apiKeys`
@@ -22,7 +22,7 @@ import Controller from "../src/Controller.js";
22
22
  * Erreurs mappées par DUCK-TYPING sur `code` (400/409/503) — framework ne peut pas
23
23
  * importer les classes d'erreur de security.
24
24
  */
25
- declare class ApiKeyController extends Controller {
25
+ declare class ApiKeyController extends SecurityApiController {
26
26
  #private;
27
27
  constructor(context: ContextType);
28
28
  /** Émission : crée une clé pour le porteur courant → token clair (1×). */
@@ -0,0 +1,32 @@
1
+ import { type Module } from "nodefony";
2
+ import type { ContextType } from "@nodefony/http";
3
+ import Controller from "../src/Controller.js";
4
+ /**
5
+ * Page de connexion par défaut (ADR-0015) : gabarit F03 rendu côté serveur,
6
+ * feuille `nodefony/login.css`, script `nodefony/login.js`. Aucun front, aucun
7
+ * build côté application.
8
+ *
9
+ * Joignable sans session ; jamais mise en cache (`no-store`) ni affichable
10
+ * dans un cadre (`frame-ancestors 'none'`).
11
+ */
12
+ declare class LoginPageController extends Controller {
13
+ constructor(context: ContextType);
14
+ /**
15
+ * Rend la page de connexion.
16
+ *
17
+ * @returns la page HTML, ou 404 quand security ne la décrit plus
18
+ */
19
+ page(): Promise<import("@nodefony/http").HttpResponse | import("@nodefony/http").WebsocketResponse>;
20
+ }
21
+ /**
22
+ * Monte la page de connexion par défaut quand security la décrit : la route
23
+ * sur `loginPage.path`, et ses fichiers sous `LOGIN_PAGE_ASSETS_BASE`.
24
+ *
25
+ * La page est le chemin vers la session : elle échappe au pare-feu, comme les
26
+ * routes de connexion — sinon une application qui protège `^/` exigerait
27
+ * d'être connecté pour se connecter.
28
+ *
29
+ * @param frameworkModule - module framework, qui porte les contrôleurs
30
+ */
31
+ export declare function mountLoginPage(frameworkModule: Module): void;
32
+ export default LoginPageController;
@@ -1,6 +1,6 @@
1
- import type { Module } from "nodefony";
1
+ import { type Module } from "nodefony";
2
2
  import type { ContextType } from "@nodefony/http";
3
- import Controller from "../src/Controller.js";
3
+ import SecurityApiController from "../src/SecurityApiController.js";
4
4
  /**
5
5
  * Vue MINIMALE du service `oauth2` (`@nodefony/security`) — couplage par NOM
6
6
  * (framework ne dépend pas de security). Contrat structurel imposé par
@@ -24,13 +24,31 @@ export interface IOAuth2Service {
24
24
  success: string;
25
25
  failure: string;
26
26
  };
27
- createAuthorization(provider: string): Promise<{
27
+ /** `options` : une version de security qui ne le lit pas l'ignore. */
28
+ createAuthorization(provider: string, options?: {
29
+ theme?: "light" | "dark";
30
+ }): Promise<{
28
31
  url: string;
29
32
  state: string;
30
33
  codeVerifier: string | null;
31
34
  }>;
32
35
  exchangeAndProvision(provider: string, code: string, codeVerifier: string | null, returnedIss: string | null): Promise<{
33
36
  identifier: string;
37
+ logoutHint?: unknown;
38
+ }>;
39
+ /**
40
+ * Retient en session de quoi fermer plus tard la session du fournisseur
41
+ * (RP-Initiated Logout). Optionnel : la clé et la forme appartiennent au
42
+ * service, ce contrôleur ne fait que transmettre.
43
+ */
44
+ rememberLogout?(session: IOAuth2Session, hint: unknown): void;
45
+ /**
46
+ * Déconnexion demandée par le fournisseur sur le canal arrière (OpenID
47
+ * Connect Back-Channel Logout). Optionnel, même raison que
48
+ * {@link listDisplayProviders} : absent, la route répond 501.
49
+ */
50
+ backchannelLogout?(provider: string, logoutToken: string): Promise<{
51
+ outcome: "unsupported" | "refused" | "done";
34
52
  }>;
35
53
  }
36
54
  /** Vue minimale d'une session — porte l'état du flux OAuth (anti-CSRF/anti-replay). */
@@ -50,16 +68,45 @@ export interface IOAuth2BffFlow {
50
68
  /** Garantit une session (anonyme) pour porter `state`/`code_verifier`. */
51
69
  ensureSession(context: ContextType): Promise<IOAuth2Session | null>;
52
70
  }
71
+ /**
72
+ * Thème que la page appelante a rendu (`?theme=`), à transmettre à l'écran du
73
+ * fournisseur. Liste FERMÉE : la chaîne vient de l'URL, toute autre valeur est
74
+ * ignorée sans un mot — rien d'arbitraire n'atteint la requête d'autorisation.
75
+ *
76
+ * @param value - paramètre `theme` de la requête, ou `null`
77
+ * @returns l'option à passer à `createAuthorization`, vide hors liste
78
+ */
79
+ export declare function providerThemeOption(value: string | null): {
80
+ theme?: "light" | "dark";
81
+ };
82
+ /**
83
+ * Compose l'adresse de l'écran d'échec : la raison de l'échec, et la page où
84
+ * aller une fois connecté, pour que l'écran la garde à la tentative suivante.
85
+ *
86
+ * `reason` vaut le code rendu par le fournisseur s'il appartient à la liste
87
+ * normalisée (`access_denied` = l'utilisateur a choisi de revenir), sinon
88
+ * {@link OAUTH2_FAILED_REASON} — jamais la chaîne reçue, qui vient de l'URL.
89
+ * Une `reason` ou un `from` déjà présents sont REMPLACÉS (l'écran qui a lancé
90
+ * le flux peut porter ceux d'une tentative précédente) ; le reste de la
91
+ * requête et le fragment sont conservés, et une adresse relative reste relative.
92
+ *
93
+ * @param failure - l'écran d'échec : `back` revalidé, ou `failureRedirect`
94
+ * @param error - le paramètre `error` reçu au callback, ou `null`
95
+ * @param from - la page où aller une fois connecté (revalidée), ou `null`
96
+ * @returns l'adresse de redirection
97
+ */
98
+ export declare function oauth2FailureTarget(failure: string, error: string | null, from: string | null): string;
53
99
  /**
54
100
  * Endpoints HTTP du **social login OAuth 2.0** (P6 J9) — adaptateurs MINCES
55
101
  * au-dessus du service `oauth2` (`@nodefony/security`) :
56
102
  *
57
- * - `GET /nodefony/security/api/oauth2/{provider}/authorize` — démarre le flux :
58
- * pose `state`+`code_verifier` en session (anonyme), redirige (302) vers le
59
- * fournisseur.
103
+ * - `GET /nodefony/security/api/oauth2/{provider}/authorize?from=&back=` —
104
+ * démarre le flux : pose `state`+`code_verifier` en session (anonyme), avec
105
+ * les retours revalidés en chemins locaux, redirige (302) vers le fournisseur.
60
106
  * - `GET /nodefony/security/api/oauth2/{provider}/callback` — valide le `state`
61
107
  * (anti-CSRF), échange le `code`, provisionne le Shadow User et OUVRE la
62
- * session BFF (302 vers `successRedirect`).
108
+ * session BFF (302 vers `from`, sinon `successRedirect`). Un échec mène à
109
+ * `back`, sinon à `failureRedirect`, avec `reason` et `from`.
63
110
  *
64
111
  * Montés UNIQUEMENT si le service `oauth2` existe (social login activé) — 404
65
112
  * sinon, zéro surface.
@@ -70,7 +117,7 @@ export interface IOAuth2BffFlow {
70
117
  * La session anonyme ne porte que `state`/`verifier` ; `establishSessionFor`
71
118
  * régénère l'ID (anti-fixation) à la promotion.
72
119
  */
73
- declare class OAuth2Controller extends Controller {
120
+ declare class OAuth2Controller extends SecurityApiController {
74
121
  #private;
75
122
  constructor(context: ContextType);
76
123
  /**
@@ -87,6 +134,18 @@ declare class OAuth2Controller extends Controller {
87
134
  authorize(provider: string): Promise<import("@nodefony/http").HttpResponse | import("@nodefony/http").WebsocketResponse | undefined>;
88
135
  /** Valide `state`, échange le `code`, ouvre la session BFF (302). */
89
136
  callback(provider: string): Promise<import("@nodefony/http").HttpResponse | import("@nodefony/http").WebsocketResponse | undefined>;
137
+ /**
138
+ * Canal arrière : le fournisseur demande de fermer les sessions qu'il
139
+ * désigne (OpenID Connect Back-Channel Logout 1.0 §2.5-2.8).
140
+ *
141
+ * Appelé serveur à serveur — ni cookie, ni session, ni jeton CSRF : la
142
+ * SIGNATURE du `logout_token` est la seule authentification. Réponses de la
143
+ * norme (§2.8) : 200 si la déconnexion a eu lieu (ou n'avait plus lieu
144
+ * d'être), 400 sinon — jeton refusé (`invalid_request`) comme déconnexion
145
+ * impossible (la cause part au journal, jamais au fournisseur). Toujours
146
+ * `Cache-Control: no-store`.
147
+ */
148
+ backchannelLogout(provider: string): Promise<import("@nodefony/http").Http2Response | import("@nodefony/http").HttpResponse | import("@nodefony/http").WebsocketResponse>;
90
149
  }
91
150
  /**
92
151
  * Monte les routes du social login OAuth — appelé par le module framework à
@@ -1,6 +1,6 @@
1
- import type { Module } from "nodefony";
1
+ import { type ILoginPageDescription, type Module } from "nodefony";
2
2
  import type { ContextType } from "@nodefony/http";
3
- import Controller from "../src/Controller.js";
3
+ import SecurityApiController from "../src/SecurityApiController.js";
4
4
  /**
5
5
  * Vue MINIMALE du flux de session BFF que consomme ce controller — le service
6
6
  * `authFlow` est posé au container par `@nodefony/security` (P6 J3). Contrat
@@ -14,14 +14,29 @@ type ILoginOutcome = {
14
14
  user: unknown;
15
15
  } | {
16
16
  status: "mfa_required";
17
- methods: string[];
17
+ methods: readonly string[];
18
18
  };
19
19
  export interface ISessionAuthFlow {
20
20
  login(context: ContextType, identifier: unknown, password: unknown): Promise<ILoginOutcome>;
21
21
  completeMfaLogin(context: ContextType, code: unknown): Promise<unknown>;
22
22
  logout(context: ContextType): Promise<boolean>;
23
+ /**
24
+ * Déconnexion qui ferme AUSSI la session du fournisseur d'identité. Optionnelle :
25
+ * une version de `@nodefony/security` qui ne l'expose pas encore dégrade sur
26
+ * {@link logout}.
27
+ */
28
+ logoutWithProvider?(context: ContextType): Promise<{
29
+ destroyed: boolean;
30
+ logoutUrl: string | null;
31
+ }>;
23
32
  /** Identité courante, ou `null` sans session authentifiée. */
24
33
  me(context: ContextType): Promise<unknown>;
34
+ /**
35
+ * Ce que la page de connexion par défaut doit afficher, ou `null` quand
36
+ * elle est désactivée. Optionnelle : une version de `@nodefony/security`
37
+ * qui ne l'expose pas encore laisse la page absente (404).
38
+ */
39
+ describeLoginPage?(): ILoginPageDescription | null;
25
40
  }
26
41
  /**
27
42
  * Endpoints HTTP du flux de session BFF — adaptateurs MINCES au-dessus du
@@ -29,7 +44,8 @@ export interface ISessionAuthFlow {
29
44
  *
30
45
  * - `POST /nodefony/security/api/auth/login` — body JSON `{username, password}`
31
46
  * - `POST /nodefony/security/api/auth/logout` — idempotent
32
- * - `GET /nodefony/security/api/auth/me` — identité de la session courante
47
+ * - `GET /nodefony/security/api/auth/me` — identité de la session courante :
48
+ * TOUJOURS `200`, `{ user }` ou `{ user: null }` sans session
33
49
  *
34
50
  * Montés par le module framework UNIQUEMENT si le service `authFlow` existe
35
51
  * (module security chargé) — sans lui, les routes n'existent pas (404, zéro
@@ -40,14 +56,20 @@ export interface ISessionAuthFlow {
40
56
  * `Retry-After` (RFC 6585) posé par le throttler NIST ; tout le reste remonte
41
57
  * au pipeline d'erreurs standard (500, détail loggé jamais fuité).
42
58
  *
43
- * @remarks Dérogation RFC 7235 §3.1 DÉLIBÉRÉE : les 401 de `login`/`me` ne
59
+ * `me` est une SONDE d'état, pas une ressource protégée : « pas de session »
60
+ * est un état normal du navigateur, pas une faute — d'où `200` + `{ user:
61
+ * null }`, comme les bibliothèques qui livrent aussi le client de connexion
62
+ * (Auth.js, better-auth). Le `401` y écrivait une erreur réseau dans la console
63
+ * de chaque visiteur anonyme, sans rien protéger : la réponse ne révèle rien.
64
+ *
65
+ * @remarks Dérogation RFC 9110 §15.5.2 DÉLIBÉRÉE : les 401 de `login` ne
44
66
  * portent PAS de `WWW-Authenticate` — aucun scheme HTTP ne décrit un cookie de
45
67
  * session, et un challenge `Basic` mensonger déclencherait le popup natif du
46
68
  * navigateur (l'anti-pattern que le BFF élimine). Les zones du firewall, elles,
47
69
  * restent strictement conformes (challenge du premier authenticator qui en
48
70
  * déclare un).
49
71
  */
50
- declare class SessionAuthController extends Controller {
72
+ declare class SessionAuthController extends SecurityApiController {
51
73
  #private;
52
74
  constructor(context: ContextType);
53
75
  /** Login BFF : credential JSON présenté UNE fois → cookie de session opaque. */
@@ -58,9 +80,25 @@ declare class SessionAuthController extends Controller {
58
80
  * absent/invalide → 401 (uniforme) ; trop de tentatives → 429 (Retry-After).
59
81
  */
60
82
  loginTotp(): Promise<import("@nodefony/http").Http2Response | import("@nodefony/http").HttpResponse | import("@nodefony/http").WebsocketResponse>;
61
- /** Détruit la session (storage + cookie). Toujours 200 (idempotent). */
83
+ /**
84
+ * Détruit la session (storage + cookie). Toujours 200 (idempotent). Quand la
85
+ * session venait d'un fournisseur qui publie un point de déconnexion, la
86
+ * réponse porte `logoutUrl` : le client y envoie le navigateur pour fermer
87
+ * aussi la session du fournisseur.
88
+ */
62
89
  logout(): Promise<import("@nodefony/http").Http2Response | import("@nodefony/http").HttpResponse | import("@nodefony/http").WebsocketResponse>;
63
- /** Identité de la session courante, re-résolue (rôles frais), ou 401. */
90
+ /**
91
+ * Identité de la session courante, re-résolue (rôles frais). Toujours `200` :
92
+ * `{ user: null }` quand il n'y a pas de session (ou qu'elle ne mène plus à
93
+ * un compte actif) — l'absence de session est un état, pas une erreur.
94
+ *
95
+ * **Sème aussi le jeton anti-CSRF** (cookie lisible `csrf-token`) : c'est la
96
+ * seule requête sûre que TOUT client connaît d'avance. Sans elle, une mutation
97
+ * `@CsrfProtect` n'a de jeton à rejouer que si son auteur a AUSSI décoré une
98
+ * lecture — protéger les seules écritures, le geste naturel, rendait alors 403
99
+ * sans issue, au navigateur comme aux tests générés. Le jeton est sans état
100
+ * (HMAC) : celui-ci vaut pour toute route protégée.
101
+ */
64
102
  me(): Promise<import("@nodefony/http").Http2Response | import("@nodefony/http").HttpResponse | import("@nodefony/http").WebsocketResponse>;
65
103
  }
66
104
  /**
@@ -1,6 +1,6 @@
1
1
  import type { Module } from "nodefony";
2
2
  import type { ContextType } from "@nodefony/http";
3
- import Controller from "../src/Controller.js";
3
+ import SecurityApiController from "../src/SecurityApiController.js";
4
4
  /**
5
5
  * Vue MINIMALE du service d'émission de jetons (`tokenService`, posé au container
6
6
  * par `@nodefony/security` P6 J4). Contrat structurel local : framework ne dépend
@@ -10,7 +10,9 @@ export interface ITokenIssuer {
10
10
  /** `true` si l'émission JWT est opérationnelle (JWT activé + store prêt). */
11
11
  isEnabled(): boolean;
12
12
  /** Émet access+refresh après vérification d'un credential (grant M2M/CLI). */
13
- issueForCredentials(identifier: unknown, password: unknown, scopes?: string[], resource?: unknown): Promise<unknown>;
13
+ issueForCredentials(identifier: unknown, password: unknown, scopes?: string[], resource?: unknown,
14
+ /** Requête porteuse — soumet le credential au refus du clair en production. */
15
+ context?: ContextType): Promise<unknown>;
14
16
  /** Rotation d'un refresh token (nouveau couple, ancien révoqué). */
15
17
  refresh(rawRefresh: unknown, resource?: unknown): Promise<unknown>;
16
18
  }
@@ -31,11 +33,11 @@ export interface ITokenIssuer {
31
33
  * framework ne peut pas importer les classes d'erreur de security ; un 429
32
34
  * reporte le `Retry-After` (RFC 6585) du throttler NIST.
33
35
  *
34
- * @remarks Dérogation RFC 7235 §3.1 (comme `SessionAuthController`) : ces 401 ne
36
+ * @remarks Dérogation RFC 9110 §15.5.2 (comme `SessionAuthController`) : ces 401 ne
35
37
  * portent pas de `WWW-Authenticate` — l'endpoint d'émission n'est pas une
36
38
  * ressource protégée par Bearer, il DÉLIVRE le Bearer.
37
39
  */
38
- declare class TokenAuthController extends Controller {
40
+ declare class TokenAuthController extends SecurityApiController {
39
41
  #private;
40
42
  constructor(context: ContextType);
41
43
  /** Émission : credential présenté → couple access/refresh. */
@@ -1,6 +1,6 @@
1
1
  import type { Module } from "nodefony";
2
2
  import type { ContextType } from "@nodefony/http";
3
- import Controller from "../src/Controller.js";
3
+ import SecurityApiController from "../src/SecurityApiController.js";
4
4
  /**
5
5
  * Endpoints HTTP **self-service 2FA TOTP (P6.17)** — console « ma sécurité »,
6
6
  * adaptateurs MINCES au-dessus du service `totp` (`@nodefony/security`) :
@@ -19,7 +19,7 @@ import Controller from "../src/Controller.js";
19
19
  * `totp` existe (security chargé) — sans security, 404 et zéro surface ; 2FA
20
20
  * désactivé, les routes répondent 503.
21
21
  */
22
- declare class TotpController extends Controller {
22
+ declare class TotpController extends SecurityApiController {
23
23
  #private;
24
24
  constructor(context: ContextType);
25
25
  /** Démarre l'enrôlement : secret + URI otpauth affichés une seule fois. */
@@ -1,6 +1,6 @@
1
- import type { Module } from "nodefony";
1
+ import { type Module } from "nodefony";
2
2
  import type { ContextType } from "@nodefony/http";
3
- import Controller from "../src/Controller.js";
3
+ import SecurityApiController from "../src/SecurityApiController.js";
4
4
  /**
5
5
  * Options de cérémonie renvoyées au navigateur (forme JSON WebAuthn) — seul le
6
6
  * `challenge` nous intéresse côté serveur (stocké en session, anti-replay) ; le
@@ -20,6 +20,7 @@ export interface IWebAuthnService {
20
20
  id: string;
21
21
  name: string;
22
22
  displayName?: string;
23
+ handle?: string;
23
24
  }): Promise<CeremonyOptions>;
24
25
  verifyRegistration(response: unknown, expectedChallenge: string, userId: string, requestOrigin?: string): Promise<{
25
26
  id: string;
@@ -46,7 +47,8 @@ export interface IWebAuthnSession {
46
47
  /** Vue minimale du flux de session BFF (`authFlow`) consommée ici. */
47
48
  export interface IWebAuthnBffFlow {
48
49
  me(context: ContextType): Promise<{
49
- username: string;
50
+ id: string;
51
+ identifier: string;
50
52
  } | null>;
51
53
  /**
52
54
  * @param reason - facteur d'authentification journalisé par l'audit
@@ -84,9 +86,15 @@ export interface IWebAuthnBffFlow {
84
86
  * firewall (qui, sur l'aire data plane, déclencherait un deadlock identique au
85
87
  * login BFF).
86
88
  */
87
- declare class WebAuthnController extends Controller {
89
+ declare class WebAuthnController extends SecurityApiController {
88
90
  #private;
89
91
  constructor(context: ContextType);
92
+ /**
93
+ * Dit si la connexion par passkey répondra : `{ enabled }`, toujours `200`.
94
+ * Un écran de connexion le lit pour ne pas proposer un bouton dont la
95
+ * cérémonie répondrait `503` (passkeys coupées).
96
+ */
97
+ status(): import("nodefony").MaybePromise<import("@nodefony/http").Http2Response | import("@nodefony/http").HttpResponse | import("@nodefony/http").WebsocketResponse>;
90
98
  /** Défi d'enregistrement — l'utilisateur connecté ajoute un passkey. */
91
99
  registerOptions(): Promise<import("@nodefony/http").Http2Response | import("@nodefony/http").HttpResponse | import("@nodefony/http").WebsocketResponse>;
92
100
  /** Vérifie la réponse d'enregistrement et persiste le credential. */