Référence d'API — @nodefony/security — contracts
stable
Documentation › Référence d’API › @nodefony/security › contracts
Générée depuis les déclarations de types publiées de
@nodefony/security10.0.0-beta.5 : exactement ce que reçoit une application qui l’installe. Cette page n’existe dans aucun dépôt — pour la corriger, on corrige le TSDoc du code.
@nodefony/security#
import { … } from "@nodefony/security" — 47 symboles.
Interfaces (39)#
IAccessTokenRecord#
interface IAccessTokenRecord
Enregistrement d’un jeton persistant — un PAT (clé API style GitHub) ou un refresh token. Le secret n’est JAMAIS stocké en clair : seul secretHash vit ici (le secret high-entropy est affiché une seule fois à la création).
id: string— Identifiant unique : claimjti(refresh) / id public (PAT).kind: "pat" | "refresh"— Nature : clé API personnelle (pat) ou refresh token (refresh).name: string— Libellé humain (« CI deploy », « app mobile ») — affiché dans la console.prefix: string | null— Préfixe public affichable (« nf_a1b2c3… ») pour l’UI/lookup ;nullpour un refresh opaque.subjectId: string— Identité du porteur (id d’un utilisateur OU d’un service account).subjectType: "user" | "service"— Discriminant du porteur (humain vs machine).tenantId: string | null— Organisation/tenant (multi-tenant, façon orgs GitHub) — axe distinct du porteur ;null= global.scopes: string[]— Capacités accordées à ce jeton (⊆ droits du porteur — downscoping).audience: string[]— Audiences liées (claimaud, RFC 8707/9700) ;[]= audience de l’app.resources: IResourcePermission[] | null— Permissions fine-grained (slot GitHub) ;null= portée par les seulsscopes.secretHash: string— Hash du secret présenté (jamais le secret) — clé deITokenStore.findByHash.hashAlg: string— Algorithme de hachage du secret (agilité crypto, migration future).clientId: string | null— Client OAuth émetteur (slot OAuth2) ;null= non-OAuth.cnf: string | null— Confirmation sender-constrained :jkt(DPoP RFC 9449) /x5t#S256(mTLS RFC 8705) ;null= bearer simple.family: string | null— Famille de rotation : un refresh tourné conserve la famille (reuse detection) ;nullpour un PAT.replacedBy: string | null—iddu successeur après rotation (chaîne d’audit) ;null= feuille active.createdAt: number— Création (epoch ms).expiresAt: number | null— Expiration (epoch ms) ;null= sans expiration (PAT longue durée).lastUsedAt: number | null— Dernier usage (epoch ms) ounull.lastUsedIp: string | null— IP du dernier usage (audit « last used from ») ; slot.lastUsedUserAgent: string | null— User-Agent du dernier usage (audit) ; slot.revokedAt: number | null— Instant de révocation (epoch ms) ounullsi actif.revokedReason: TokenRevokeReason | null— Raison de révocation (audit) ounull.metadata: Record<string, unknown>— Extras applicatifs libres (anti-migration),{}par défaut.
IAccessVoter#
interface IAccessVoter
Voter contextuel (niveau C de l’autorisation, P6.8) — décide d’un accès en fonction du token ET du sujet (ex. « cet user édite-t-il SON projet ? »).
supports(attribute: string, subject?: unknown): boolean— Ce voter sait-il décider de cet attribut/sujet ?vote(token: IToken, attribute: string, subject?: unknown): Promise<VoterVote>— Vote pour l’attribut donné sur le sujet donné.
IApiKeyCapabilities#
interface IApiKeyCapabilities
Capacités/contraintes d’émission des clés API — exposées à la console (« Mes clés ») pour un formulaire de création honnête : plafond par porteur, catalogue de scopes, préfixe public, durée par défaut. Aucune valeur sensible (config publique : le préfixe figure déjà dans chaque clé).
enabled: boolean— Émission de clés activée en config (apiKeys.enabled).prefix: string— Marqueur public des clés (<prefix>_…, ex.defaultExpiryDays: number | null— Expiration par défaut en jours (null= sans expiration).maxPerSubject: number— Plafond de clés ACTIVES par porteur (création au-delà → 409).allowedScopes: string[] | null— Catalogue de scopes autorisés (null= libre : tout scope non vide accepté).
IApiKeyCreated#
interface IApiKeyCreated extends IApiKeyView
Résultat d’une création de clé — la vue publique + le token en clair. Le token n’est disponible qu’ICI, une seule fois : il n’est jamais stocké (seul son sha256 l’est) ni re-dérivable. La console l’affiche puis l’oublie.
token: string— Secret EN CLAIR — affiché UNE seule fois (RFC : « shown once »).
IApiKeyView#
interface IApiKeyView
Vue publique d’une clé API (PAT) — projection de IAccessTokenRecord sans le secret ni son hash (jamais exposés après création). C’est la forme renvoyée par la console (« mes clés ») et le data plane Studio.
id: string— Identifiant public de la clé (révocation, affichage).prefix: string | null— Préfixe public affichable (<prefix>_<pubid>, ex.name: string— Libellé humain (« CI deploy », « app mobile »).scopes: string[]— Capacités accordées (axe distinct des rôles RBAC).subjectId: string— Porteur (id utilisateur ou service account).subjectType: "user" | "service"— Discriminant du porteur.tenantId: string | null— Organisation/tenant (null= global).createdAt: number— Création (epoch ms).expiresAt: number | null— Expiration (epoch ms) ounull(sans expiration).lastUsedAt: number | null— Dernier usage (epoch ms) ounull(jamais utilisée).lastUsedIp: string | null— Adresse du dernier usage, ounull.lastUsedUserAgent: string | null— Agent du dernier usage, ounull(même usage : reconnaître l’inattendu).revokedAt: number | null— Révocation (epoch ms) ounull(active).
IAuditEvent#
interface IAuditEvent
Un événement de sécurité journalisé. id/ts sont posés par l’AuditService (l’émetteur ne fournit qu’un IAuditEventDraft) → un seul appel système d’horodatage, centralisé hors des points d’émission.
id: string— Identifiant unique dans le process (corrélation + curseur de pagination).ts: number— Horodatage (epoch ms,Date.now()), posé par le service.category: AuditCategory— Sous-système concerné — axe de filtrage principal.action: string— Action conventionnée<sujet>.<verbe>(ex.outcome: AuditOutcome— Issue de l’action.actor: string | null— Acteur : identité (token.getUserIdentifier()) ounullsi anonyme/non identifié (tentative pré-authentification).resource?: string | null— Ressource visée (zone firewall, route, canal WS, attribut d’autorisation) — descripteur léger ; jamais le corps ni les en-têtes de la requête.reason?: string | null— Raison machine stable et filtrable (ex.ip?: string | null— IP source si disponible (audit « from where »).userAgent?: string | null— User-Agent source si disponible.requestId?: string | null— Id de requête (corrélation log ↔ audit via l’ALS, P1.4) ;nullhors requête.flags?: IAuditEventFlags— Présence (jamais la valeur) de matériel sensible au moment de l’événement — calqueAuditLogEntry.hasAuthorization.metadata?: Record<string, unknown>— Extras applicatifs libres (anti-migration) ; absents par défaut.
IAuditEventFlags#
interface IAuditEventFlags
Drapeaux de présence de matériel sensible (jamais la valeur).
hasAuthorization?: boolean— Un en-têteAuthorizationétait présent.hasCookie?: boolean— Un cookie était présent.
IAuditListQuery#
interface IAuditListQuery extends IPageQuery
Filtre de lecture du journal d’audit (console Studio P6.15, data plane P6.14 Lot 3) — IPageQuery + les critères propres au journal. Tous les critères sont AND ; absents = non filtrant.
category?: AuditCategory— Restreint à un sous-système.outcome?: AuditOutcome— Restreint à une issue (success/failure/denied).actor?: string— Restreint à un acteur (égalité exacte de l’identifiant).action?: string— Restreint à une action conventionnée ("login.success"…).requestId?: string— Restreint aux événements d’une requête (corrélation trace ↔ audit, P6.15).since?: number— Borne basse d’horodatage (epoch ms, inclus).until?: number— Borne haute d’horodatage (epoch ms, inclus).
IAuditSink#
interface IAuditSink
Face émission du journal, vue par les points sensibles (firewall, authenticators, controllers OAuth/WebAuthn). record est synchrone et fire-and-forget : l’audit ne doit JAMAIS bloquer ni faire échouer le flux métier (un store en panne se solde par un log d’erreur, pas par un login KO). No-op à coût nul si l’audit est désactivé.
record(event: IAuditEventDraft): void— Journalise un événement : poseid+ts, persiste (best-effort) et notifie les abonnés live.subscribe(listener: (event: IAuditEvent) => void): () => void— S’abonne au flux live des événements (bridge WS, P6.14 Lot 4).
IAuditStore#
interface IAuditStore
Journal d’audit append-only (tamper-evident) — état serveur des événements de sécurité. Pluggable par backend (mémoire/fichier/ORM/Loki) comme ITokenStore. La référence mémoire borne le volume (anti-fuite) ; un backend partagé (ORM/Redis) sert le multi-pod et la rétention longue.
append(event: IAuditEvent): Promise<void>— Ajoute un événement (immuable, jamais modifié ensuite).listPage(query: IAuditListQuery): Promise<IPage<IAuditEvent>>— Lit une page d’événements filtrés, du plus récent au plus ancien — l’implémentation ne matérialise jamais plus quelimitévénements.gc(now?: number): Promise<number>— Purge les événements au-delà de la fenêtre de rétention.
IAuthenticator#
interface IAuthenticator
Stratégie d’authentification — pattern « authenticator » : une classe = une façon de prouver qui on est. Pas de Bridge/Factory (héritage Passport/YAML abandonné).
readonly name: string— Nom logique (référencé dansarea.authenticators).supports(context: ContextType): boolean— Peut-on extraire un credential de cette requête ?createToken(context: ContextType): Promise<IToken>— Construit un token NON authentifié depuis la requête (extraction credential).authenticate(token: IToken): Promise<IToken>— Valide le token (vérifie le credential) outhrowune AuthenticationError.onSuccess(context: ContextType, token: IToken): Promise<void>— Hook succès — pose le token (cookie JWT, log audit S1…).onFailure(context: ContextType, error: Error): Promise<void>— Hook échec — log audit, throttling (J2)… Le 401 + challenge sont posés par le firewall.challenge?(area?: ISecuredArea): string— ChallengeWWW-Authenticate(RFC 9110 §11 : tout 401 DOIT en porter un).validateArea?(area: ISecuredArea): void— Vérifie AU BOOT que la zone qui référence cet authenticator lui donne ce dont il a besoin — sinonthrow, et la configuration n’existe pas.
IAuthorizationService#
interface IAuthorizationService
Service d’autorisation (niveau C de la sécurité, P6) — décide si un token a le droit d’effectuer une action (attribute) sur un sujet (subject).
decide(token: IToken, attribute: string, subject?: unknown): Promise<boolean>— Le token a-t-il le droit demandé ?
ICreateApiKeyOptions#
interface ICreateApiKeyOptions
Options de création d’une clé API.
name: string— Libellé humain (obligatoire, non vide).scopes?: string[]— Capacités demandées (⊆apiKeys.allowedScopessi un catalogue est défini).expiresInDays?: number | null— Durée de vie en jours ;null= sans expiration ; omis = défaut config.tenantId?: string | null— Tenant/organisation (multi-tenant) ;null= global.
IFirewall#
interface IFirewall extends IFirewallGate
Orchestrateur de sécurité — branché dans le pipeline HTTP/WS de @nodefony/http.
isSecure(context: ContextType): boolean— Match rapide de zone (posecontext.security).handleSecurity(context: ContextType): Promise<ContextType>— Pipeline complet d’authentification de la zone.enforceCsrf(context: ContextType): void— Défense CSRF (Fetch Metadata + repli Origin) sur les méthodes state-changing.handleCors(context: ContextType): number— Politique CORS : pose les en-têtesAccess-Control-*.applySecurityHeaders(context: ContextType): void— Pose les en-têtes de sécurité applicatifs (CSP, Referrer-Policy, COOP/COEP/ CORP, Permissions-Policy) sur la réponse.registerCspOrigins(moduleName: string, fragment: CspFragment): void— Déclare des directives CSP additionnelles pour un module (ex.unregisterCspOrigins(moduleName: string): void— Retire les directives CSP d’un module (ex.registerAuthenticator(authenticator: IAuthenticator): void— Enregistre un authenticator (appelé par chaque*Authenticatorau boot).getArea(name: string): ISecuredArea— Zone par nom, ouundefined.describe(): IFirewallDescription— Projection LECTURE SEULE de l’état RUNTIME (zones/authenticators/défenses) pour le data plane Studio — secrets exclus (présence, jamais valeur).describeRoleHierarchy(): IRoleHierarchyDescription— Hiérarchie de rôles déclarée + résolution transitive (data plane Studio).publishedProtectedResources(): readonly IProtectedResourceInput[]— Ressources protégées à publier en RFC 9728 — une pararea.resourcedéclarée, avec les émetteurs de confiance comme serveurs d’autorisation.
IIssuerPolicy#
interface IIssuerPolicy
Ce qu’on exige du paramètre iss renvoyé par le serveur d’autorisation (RFC 9207).
readonly issuer: string— Émetteur attendu, sous sa forme canonique.readonly requireIssParameter: boolean—truesi le serveur ANNONCE émettreiss(authorization_response_iss_parameter_supported) : son absence est alors une promesse non tenue, donc un refus.
IJwtKeystore#
interface IJwtKeystore
Source du matériel cryptographique de signature des JWT.
getSigningKey(): Promise<IJwtSigningKey>— Clé active de signature.getPublicJWKS(): Promise<JSONWebKeySet>— JWKS public (toutes les clés du keyset, paramètres publics uniquement) — passé àcreateLocalJWKSetpour résoudre la clé de vérification parkid.
IJwtSigningKey#
interface IJwtSigningKey
Clé active de signature résolue par le keystore — la clé privée Ed25519 plus son kid (à poser dans l’en-tête JWT) et l’algorithme JWA.
readonly key: CryptoKey— Clé privée Ed25519 (WebCryptoCryptoKey) qui signe les jetons.readonly kid: string— Identifiant de clé = thumbprint RFC 7638 → en-tête JWTkid(sélection au verify).readonly alg: "EdDSA"— Algorithme JWA — toujours"EdDSA"pour une clé Ed25519 (RFC 8037).
ILogoutRequest#
interface ILogoutRequest
Ce que porte une déconnexion initiée par l’application (OpenID Connect RP-Initiated Logout 1.0 §2).
readonly idTokenHint: string— ID token reçu au login (id_token_hint) : il dit au fournisseur QUELLE session fermer, et lui évite de demander une confirmation à l’utilisateur.readonly postLogoutRedirectUri: string— Adresse de retour, enregistrée chez le fournisseur (post_logout_redirect_uri).
ILogoutTokenClaims#
interface ILogoutTokenClaims
Ce qu’un jeton de déconnexion VÉRIFIÉ désigne (OpenID Connect Back-Channel Logout 1.0 §2.4) : au moins l’un de subject et sid.
readonly issuer: string— Émetteur canonique — celui de la liste fermée qui a servi à vérifier.readonly subject: string | null—sub: l’utilisateur chez le fournisseur, ounull.readonly sid: string | null—sid: LA session du fournisseur à fermer, ounull(= toutes celles desubject).readonly tokenId: string—jti: identifiant unique du jeton, la clé de l’anti-rejeu.readonly expiresAt: number—exp: fin de validité, en secondes depuis l’époque.
IOAuthProvider#
interface IOAuthProvider
Adaptateur d’un fournisseur OAuth/OIDC, façade UNIFORME au-dessus d’un client OAuth 2.0 — masque les divergences entre fournisseurs derrière un contrat stable consommé par OAuth2Service :
readonly usesPkce: boolean—truesi le fournisseur exige PKCE (RFC 7636) — le service génère alors uncode_verifieret le transmet aux deux méthodes ci-dessous.readonly issuerPolicy: IIssuerPolicy | null— Politique de vérification du paramètreiss(anti-mix-up, RFC 9207), ounullpour un fournisseur qui ne relève pas de cette défense (GitHub, non-OIDC).readonly defaultScopes: string[]— Scopes appliqués quand la configuration n’en précise aucun.createAuthorizationURL(request: IAuthorizationRequest): URL— Construit l’URL d’autorisation (étape 1).validateAuthorizationCode(request: ITokenRequest): Promise<OAuth2Tokens>— Échange lecoded’autorisation contre des jetons (étape 2, canal serveur).fetchProfile(tokens: OAuth2Tokens): Promise<IOAuthProfile>— Récupère et normalise le profil de l’utilisateur à partir des jetons.createLogoutURL?(request: ILogoutRequest): URL— Construit l’adresse de déconnexion chez le fournisseur (RP-Initiated Logout).verifyLogoutToken?(logoutToken: string): Promise<ILogoutTokenClaims | null>— Vérifie un jeton de déconnexion reçu sur le canal arrière (OpenID Connect Back-Channel Logout 1.0 §2.6) et rend ce qu’il désigne.
IResourcePermission#
interface IResourcePermission
Permission fine-grained sur une ressource (modèle GitHub fine-grained PAT).
type: string— Type de ressource (ex.ids?: string[]— Identifiants ciblés (omis = toutes les ressources de ce type).perms: Array<"read" | "write">— Permissions accordées sur ces ressources.
ISecuredArea#
interface ISecuredArea extends ISecurityZone
Zone sécurisée — un pattern d’URL + la liste des authenticators à exécuter.
readonly name: string— Nom de la zone ("main_api","admin"…).readonly pattern: RegExp— Pattern d’URL compilé.readonly security: boolean— Zone protégée (Zero Trust) ;false= zone publique explicite.readonly stateless: boolean— Stratégie d’identité AU-DESSUS du protocole.readonly mode: "first" | "all"— Sémantique de la chaîne :"first"= le premier authenticator qui reconnaît la requête authentifie ;"all"= tous doivent passer (MFA — le DERNIER porte l’identité).readonly authenticators: readonly string[]— Noms des authenticators à exécuter (sémantique selonmode).readonly host?: string— Domaine/vhost de la zone (ex.readonly resource?: string— URI canonique de la ressource protégée par cette zone — l’audience qu’un jeton doit porter pour y être accepté (RFC 8707 §2).readonly subjectPolicy?: "require" | "ephemeral"— Politique de rattachement du sujet d’un jeton TIERS propre à cette zone, ouundefinedpour suivreresourceServer.subjectPolicy.readonly ephemeralRoles?: readonly string[]— Rôles accordés par cette zone à un appelantephemeral— définis dès que la zone déclaresubjectPolicy: "ephemeral"(vide si elle n’en écrit aucun : un pouvoir local ne s’hérite jamais de la politi …readonly entryPoint?: string— Page de connexion vers laquelle le pare-feu conduit un visiteur anonyme qui NAVIGUE (302vers<entryPoint>?from=<chemin demandé>), au lieu du401.readonly realtime: boolean— Zone valable AUSSI pour le WebSocket (framesapi.request+subscribe), pas seulement HTTP.readonly roles: readonly string[] | null— Rôles exigés PAR DÉFAUT dans la zone — en OU, hiérarchie comprise —, ounullquand la zone n’exige qu’une identité.match(context: ContextType): boolean— La requête tombe-t-elle dans cette zone ?
IToken#
interface IToken
Jeton de sécurité — porte l’identité (authentifiée ou non) d’une requête.
readonly type: string— Type du token ("anonymous","userpassword","jwt","oauth2","mtls").getUser(): IUser— Utilisateur porté — jamaisnull(AnonymousUser si non authentifié).getUserIdentifier(): string— Identifiant logique de l’utilisateur ("anonymous","admin"…) — partagé avecIRealtimeToken(le token WS, sous-ensemble transport-neutre, n’a PASgetUser()).isAuthenticated(): boolean—truesi l’authentification a réussi (≠ anonyme).getRoles(): string[]— Rôles plats de l’utilisateur (sans hiérarchie résolue).getCredentials(): unknown— Credential brut avant validation (vidé au succès).getScopes(): string[]— Scopes accordés (clé API / OAuth) — axe distinct des rôles RBAC.getAttribute<T = unknown>(key: string): T— Lecture d’un attribut arbitraire posé par l’authenticator (claims JWT, scopes…).setAttribute(key: string, value: unknown): void— Pose un attribut arbitraire (claims, scopes, providerId…).
ITokenListQuery#
interface ITokenListQuery extends IPageQuery
Requête de listing paginé de jetons (data plane admin) — le contrat de page standard du core (IPageQuery) enrichi des filtres propres aux jetons. Tous portables au Criteria (subjectId indexé, colonnes simples) → pas de SQL natif, le helper paginate() d’orm-core suffit côté ORM.
subjectId?: string— Restreint à un porteur (indexé).kind?: "pat" | "refresh"— Restreint à une nature de jeton.status?: TokenStatus— Restreint à un état de vie du jeton.
ITokenStore#
interface ITokenStore extends ISortableSource
Contrat du store de jetons — implémentations enregistrées via registerTokenStore, sélectionnées par config. Toutes les opérations sont asynchrones (un backend ORM/Redis fait des I/O ; la référence mémoire résout immédiatement).
put(record: IAccessTokenRecord): Promise<void>— Enregistre (ou remplace) un jeton persistant.findById(id: string): Promise<IAccessTokenRecord | null>— Recherche par identifiant (jti/id public).findByHash(secretHash: string): Promise<IAccessTokenRecord | null>— Recherche par hash de secret présenté.findBySubject(subjectId: string): Promise<IAccessTokenRecord[]>— Tous les jetons d’un porteur (console « mes jetons », révocation ciblée).listAll(): Promise<IAccessTokenRecord[]>— Tous les jetons du store, tous porteurs confondus — vue d’ADMINISTRATION cross-porteur (gouvernance / réponse à incident).listPage(query: ITokenListQuery): Promise<IPage<IAccessTokenRecord>>— Liste paginée de jetons pour le data plane admin — ne matérialise jamais plus d’une page (contrairement àITokenStore.listAll, réservé au dump d’incident).countTokens(query: ITokenListQuery): Promise<number>— Compte les jetons correspondant aux filtres — base dutotalde la page (SQL/MongoCOUNT, mémoirelength).markUsed(id: string, usage: ITokenUsage): Promise<void>— Met à jourlastUsedAt/IP/UA (no-op si l’id est inconnu).revoke(id: string, reason: TokenRevokeReason): Promise<void>— Révoque un jeton (poserevokedAt+revokedReason) — idempotent.revokeFamily(family: string, reason: TokenRevokeReason): Promise<void>— Révoque TOUTE la famille de rotation (détection de rejeu, RFC 9700).denyJti(jti: string, expiresAt: number): Promise<void>— Inscrit unjtid’access sur la denylist jusqu’àexpiresAt(epoch ms) — révocation immédiate avant l’expdu JWT.isJtiDenied(jti: string): Promise<boolean>—truesi lejtiest denylisté et non encore expiré.revokeAllForSubject(subjectId: string, invalidBefore: number): Promise<void>— Pose le seuilinvalidBefore(epoch ms) d’un porteur : tout access auto-porté dontiat < invalidBeforeest rejeté.getInvalidBefore(subjectId: string): Promise<number | null>— SeuilinvalidBefored’un porteur (epoch ms) ounullsi aucun.gc(now?: number): Promise<number>— Purge les entrées devenues inutiles : denylistjtiexpirée, records arrivés àexp, PAT révoqués sans expiration au-delà de la fenêtre de rétention.
ITokenUsage#
interface ITokenUsage
Contexte d’un usage de jeton (audit « last used »).
at: number— Instant d’usage (epoch ms).ip?: string | null— IP source, si disponible.userAgent?: string | null— User-Agent source, si disponible.
ITotpEnrollmentSummary#
interface ITotpEnrollmentSummary
Vue d’un enrôlement 2FA pour l’INTROSPECTION admin (« qui a activé le 2FA, qui est resté en attente de confirmation »).
readonly userId: string— Utilisateur propriétaire.readonly algorithm: string— Fonction HMAC du code (RFC 6238 §1.2).readonly digits: number— Nombre de chiffres du code.readonly period: number— Période d’un code en secondes.readonly confirmedAt: number | null— Confirmation de l’enrôlement (epoch ms), ounull= en attente.readonly createdAt: number— Création de l’enrôlement (epoch ms).readonly lastUsedAt: number | null— Dernier usage réussi (epoch ms), ounull= jamais servi.readonly recoveryCodesLeft: number— Nombre de codes de récupération NON consommés (jamais les condensats).
ITotpListQuery#
interface ITotpListQuery extends IPageQuery
Requête de listing des enrôlements 2FA — IPageQuery + les filtres qui ont un sens ici. q (hérité) = préfixe d’userId (retrouver un id partiel collé depuis la console).
confirmed?: boolean—true= enrôlements confirmés seulement,false= en attente seulement (comptes à relancer : un secret jamais confirmé ne protège personne), omis = les deux.
ITotpSecret#
interface ITotpSecret
Secret TOTP d’un utilisateur (2FA) — un seul par utilisateur (clé = userId).
readonly userId: string— Propriétaire =IUser.identifier(clé naturelle), jamaisIUser.id— cfITotpSecretStore.readonly secretEnc: string— Secret partagéKchiffré (blob opaque :iv.tag.ciphertextbase64url).readonly algorithm: TotpAlgorithm— Fonction HMAC du code (RFC 6238 §1.2).readonly digits: number— Nombre de chiffres du code.readonly period: number— Période d’un code en secondes.recoveryCodes: string[]— Condensatssha256des codes de récupération non encore consommés.confirmedAt: number | null— Horodatage de confirmation de l’enrôlement (epoch ms), ounulltant que l’utilisateur n’a pas prouvé qu’il lit bien les codes (anti-lock-out).lastUsedStep: number | null— Dernière tranche temporelleTayant validé un code (RFC 6238 §5.2) — un code déjà consommé dans sa fenêtre ne doit pas resservir (anti-rejeu).readonly createdAt: number— Horodatage de création (epoch ms).lastUsedAt: number | null— Horodatage du dernier usage réussi (epoch ms), ounull.
ITotpSecretStore#
interface ITotpSecretStore
Store pluggable du secret TOTP par utilisateur — découple le cœur du backend de persistance (mémoire / fichier / ORM / Redis). Convention-frère d’IWebAuthnCredentialStore / ITokenStore : le builtin memory est sans dépendance, les adapters lourds s’enregistrent depuis leur propre module.
findByUser(userId: string): Promise<ITotpSecret | null>— Secret TOTP de l’utilisateur, ounullsi non enrôlé.save(secret: ITotpSecret): Promise<void>— Crée ou remplace le secret de l’utilisateur (upsert — ré-enrôlement).update(userId: string, patch: TotpSecretUpdate): Promise<void>— Applique un patch partiel (confirmation, anti-rejeu, consommation de codes).delete(userId: string): Promise<void>— Supprime le secret de l’utilisateur (désactivation du 2FA).listPage(query: ITotpListQuery): Promise<IPage<ITotpEnrollmentSummary>>— Page d’enrôlements 2FA pour le data plane admin — ne matérialise jamais plus d’une page, filtres appliqués au store, secrets exclus (cfITotpEnrollmentSummary).countEnrollments(query: ITotpListQuery): Promise<number>— Nombre d’enrôlements correspondant aux filtres (COUNTnatif) — le KPI « couverture 2FA » sans énumérer.
IWebAuthnCredential#
interface IWebAuthnCredential
Enregistrement d’un credential WebAuthn / passkey côté serveur — le credentialRecord de WebAuthn L3 §7.1 (produit par la cérémonie d’enregistrement, relu et mis à jour à chaque authentification §7.2).
readonly id: string— Identifiant du credential (base64url) — unique, fourni par l’authenticator.readonly userId: string— Propriétaire =IUser.identifier(sub / userHandle applicatif), jamaisIUser.id— cfIWebAuthnCredentialStore.readonly publicKey: string— Clé publique COSE encodée base64url — vérifie la signature des assertions.signCount: number— Compteur de signatures (WebAuthn §6.1.1) — anti-clone : SHOULD croître à chaque authentification.readonly transports: readonly string[]— Transports annoncés (usb|nfc|ble|internal|hybrid).readonly backupEligible: boolean— BE flag (Backup Eligibility, §6.1.3) — credential multi-appareils (synchronisable).backupState: boolean— BS flag (Backup State) — actuellement sauvegardé.uvInitialized: boolean— La cérémonie a-t-elle réalisé une vérification d’utilisateur (biométrie/ PIN) au moins une fois (uvInitialized, §7.2) — base du step-up MFA.nickname?: string— Surnom optionnel de la passkey (« MacBook de Chris »).readonly createdAt: number— Création (epoch ms).lastUsedAt: number | null— Dernière authentification réussie (epoch ms), ounullsi jamais utilisé.
IWebAuthnCredentialStore#
interface IWebAuthnCredentialStore
Store pluggable des credentials WebAuthn — découple le cœur du backend de persistance (mémoire / ORM / Redis). Convention-frère d’ITokenStore : le builtin memory est sans dépendance, les adapters lourds s’enregistrent depuis leur propre module (inversion de dépendance).
findById(credentialId: string): Promise<IWebAuthnCredential | null>— Credential par son id (base64url), ounull— résolution à l’authentification.findByUser(userId: string): Promise<IWebAuthnCredential[]>— Tous les credentials d’un utilisateur — pourallowCredentials(authentification ciblée) et l’UX « mes appareils ».countByUser(userId: string): Promise<number>— Nombre de credentials d’un utilisateur — natif par backend (COUNT,countDocuments,SCARD), jamais unfindByUser().length.save(credential: IWebAuthnCredential): Promise<void>— Persiste un nouveau credential (fin de la cérémonie d’enregistrement).update(credentialId: string, patch: WebAuthnAuthUpdate): Promise<void>— Met à jour l’état post-authentification (compteur, sauvegarde, UV, usage).delete(credentialId: string): Promise<void>— Révoque un credential (l’utilisateur retire un appareil).listPage(query: IWebAuthnListQuery): Promise<IPage<IWebAuthnCredentialSummary>>— Page de passkeys pour le data plane admin — ne matérialise jamais plus d’une page, filtres appliqués au store,publicKeyexclue (cfIWebAuthnCredentialSummary).countCredentials(query: IWebAuthnListQuery): Promise<number>— Nombre de passkeys correspondant aux filtres (COUNTnatif), ou-1si le backend ne sait pas compter à coût raisonnable (Redis : un total exigerait unSCANcomplet O(N) sur un chemin froid — …
IWebAuthnCredentialSummary#
interface IWebAuthnCredentialSummary
Vue d’une passkey pour l’INTROSPECTION admin (« quels appareils portent des passkeys, lesquelles meurent avec leur appareil »).
readonly id: string— Identifiant du credential (base64url) — la clé naturelle.readonly userId: string— Utilisateur propriétaire.readonly transports: readonly string[]— Canaux de l’authenticator (internal,hybrid,usb…).readonly backupEligible: boolean— Le credential PEUT être sauvegardé/synchronisé (BE flag).readonly backupState: boolean— Le credential EST sauvegardé (BS flag) — sinon il meurt avec l’appareil.readonly uvInitialized: boolean— Une vérification utilisateur (biométrie/PIN) a déjà eu lieu.readonly signCount: number— Compteur de signatures (anti-clone WebAuthn §6.1.1).readonly nickname?: string— Nom donné à l’appareil par l’utilisateur, si renseigné.readonly createdAt: number— Enrôlement (epoch ms).readonly lastUsedAt: number | null— Dernière authentification réussie (epoch ms), ounull= jamais servie.
IWebAuthnListQuery#
interface IWebAuthnListQuery extends IPageQuery
Requête de listing des passkeys — IPageQuery + les filtres qui ont un sens ici. q (hérité) = préfixe d’userId.
userId?: string— Restreindre à un porteur (sa liste d’appareils).backedUp?: boolean—true= passkeys sauvegardées/synchronisées seulement,false= celles liées à un seul appareil (les porteurs à risque de verrouillage), omis = les deux.
IWebhookEndpoint#
interface IWebhookEndpoint
Endpoint webhook sortant — une destination que Nodefony notifie quand un événement souscrit survient (modèle façon GitHub/Stripe).
readonly id: string— Identifiant public stable (wh_<random>).readonly url: string— URL de destination (validée anti-SSRF à l’enregistrement).readonly secretEnc: string— Secret de signature chiffré au repos (blobgcm1.…).readonly events: readonly string[]— Actions d’audit souscrites (ex.readonly enabled: boolean— Endpoint actif ?readonly description: string | null— Libellé humain optionnel (console admin).readonly tenantId: string | null— Slot multi-tenant (réservé P17) —null= global.readonly createdBy: string | null— Identité de l’admin créateur (soft ref, traçabilité).readonly createdAt: number— Création (epoch ms).readonly updatedAt: number— Dernière modification (epoch ms).readonly lastDeliveryAt: number | null— Dernière tentative de livraison (epoch ms) ounull.readonly lastDeliveryStatus: number | null— Code HTTP de la dernière livraison, ounull(jamais livré / erreur réseau).readonly lastDeliveryError: string | null— Message d’erreur de la dernière livraison, ounull.readonly failureCount: number— Échecs consécutifs (auto-désactivation au-delà d’un seuil, façon GitHub).readonly metadata: Record<string, unknown>— Métadonnées extensibles (jamais de secret).
IWebhookListQuery#
interface IWebhookListQuery extends IPageQuery
Requête de listing paginé d’endpoints webhook (data plane admin) — le contrat de page standard du core (IPageQuery) enrichi des filtres propres aux endpoints.
enabled?: boolean—true= actifs seulement,false= désactivés seulement, omis = les deux.event?: string— Ne garder que les endpoints abonnés à cet événement (appartenance au tableauevents).failing?: boolean—true= seulement les endpoints en échec (au moins un échec consécutif courant,failureCount > 0),false= seulement ceux qui vont bien, omis = les deux.
IWebhookStore#
interface IWebhookStore extends ISortableSource
Persistance des endpoints webhook (configuration durable, pas un cache). Backend interchangeable (Memory dev/test · Drizzle SQL · Mongoose) via le registre ../src/webhook/webhookStoreRegistry. Redis n’est PAS un store d’endpoints (config durable ≠ éphémère) — il servira la queue de livraison cross-pod (slice cluster), pas ce contrat.
save(endpoint: IWebhookEndpoint): Promise<void>— Insère un nouvel endpoint.findById(id: string): Promise<IWebhookEndpoint | null>— Charge un endpoint par id, ounull.update(id: string, patch: WebhookEndpointUpdate): Promise<void>— Applique un patch partiel (champs mutables) ; no-op si id absent.delete(id: string): Promise<void>— Supprime un endpoint ; no-op si id absent.listAll(): Promise<IWebhookEndpoint[]>— Tous les endpoints — réservé au snapshot du dispatcher (le service garde en mémoire la table complète des abonnements pour router un événement sans I/O, et la recharge au boot puis après chaqu …listPage(query: IWebhookListQuery): Promise<IPage<IWebhookEndpoint>>— Liste paginée d’endpoints pour le data plane admin — ne matérialise jamais plus d’une page, filtresIWebhookListQueryappliqués au store (jamais après un chargement complet).countEndpoints(query: IWebhookListQuery): Promise<number>— Nombre d’endpoints correspondant aux filtres (COUNTnatif) — base dutotald’une page et des compteurs de la console.
TotpSecretUpdate#
interface TotpSecretUpdate
Champs mutables d’un secret TOTP (patch partiel).
confirmedAt?: number | null— Confirmation de l’enrôlement (epoch ms) — passe le secret en « actif ».recoveryCodes?: string[]— Stock résiduel de codes de récupération hachés (après consommation).lastUsedStep?: number— Dernière trancheTvalidée (anti-rejeu RFC 6238 §5.2).lastUsedAt?: number— Horodatage du dernier usage réussi (epoch ms).
WebAuthnAuthUpdate#
interface WebAuthnAuthUpdate
État mis à jour après une authentification réussie (WebAuthn §7.2).
signCount: number— Nouveau compteur de signatures (anti-clone).backupState: boolean— Nouvel état de sauvegarde (BS flag).uvInitialized: boolean— UV réalisée durant cette cérémonie.lastUsedAt: number— Horodatage de l’usage (epoch ms).
Types (7)#
AuditCategory#
type AuditCategory = "auth" | "authz" | "token" | "session" | "oauth" | "webauthn" | "csrf" | "cors" | "ws" | "webhook" | "config"
Sous-système de sécurité concerné — axe de filtrage principal de la console d’audit (Studio P6.15). Fermé : la liste des sous-systèmes est stable.
AuditOutcome#
type AuditOutcome = "success" | "failure" | "denied"
Issue d’une action de sécurité. La distinction failure/denied est utile à l’auditeur : failure = l’acteur a échoué une preuve (brute-force, signature invalide) ; denied = une politique a refusé un acteur même bien formé (rôle manquant, origine interdite, plancher WS) — signal d’accès non autorisé.
IAuditEventDraft#
type IAuditEventDraft = Omit<IAuditEvent, "id" | "ts">
Champs fournis par l’émetteur d’un événement — l’AuditService y ajoute id + ts. Garde les points d’émission concis et sans appel système d’horloge.
TokenRevokeReason#
type TokenRevokeReason = "logout" | "rotated" | "reuse_detected" | "manual" | "compromised" | "subject_disabled" | "expired_cleanup"
Raison de révocation d’un jeton — tracée pour l’audit de sécurité.
TokenStatus#
type TokenStatus = "active" | "expired" | "revoked"
État de vie d’un jeton — partition exhaustive, évaluée révoqué → expiré → actif.
WebhookEndpointSummary#
type WebhookEndpointSummary = Omit<IWebhookEndpoint, "secretEnc">
Vue publique d’un endpoint (DTO console admin) — sans le secret chiffré. Le secret en clair n’est renvoyé qu’une fois, à la création/rotation.
WebhookEndpointUpdate#
type WebhookEndpointUpdate = Partial<Pick<IWebhookEndpoint, "url" | "secretEnc" | "events" | "enabled" | "description" | "updatedAt" | "lastDeliveryAt" | "lastDeliveryStatus" | "lastDeliveryError" | "failureCount" | "metadata">>
Champs mutables d’un endpoint (PATCH). id/createdAt/createdBy/tenantId sont immuables après création.
Énumérations (1)#
VoterVote#
enum VoterVote { GRANT = "GRANT", DENY = "DENY", ABSTAIN = "ABSTAIN" }
Vote d’un IAccessVoter. Stratégie firewall = affirmative + DENY veto : un seul DENY bloque ; sinon un GRANT suffit ; tous ABSTAIN → DENY (Zero Trust).