@nodefony/security 10.0.0-beta.3 → 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.
- package/dist/_virtual/{_@oxc-project_runtime@0.153.0 → _@oxc-project_runtime@0.155.0}/helpers/esm/decorate.js +1 -2
- package/dist/index.js +6 -8
- package/dist/nodefony/command/{security-token.js → security-mcp-token.js} +18 -11
- package/dist/nodefony/command/security-oauth-doctor.js +5 -3
- package/dist/nodefony/command/security-secrets.js +10 -4
- package/dist/nodefony/command/security-user-add.js +9 -3
- package/dist/nodefony/command/security-user-delete.js +5 -3
- package/dist/nodefony/command/security-user-list.js +5 -3
- package/dist/nodefony/command/security-user-password.js +5 -3
- package/dist/nodefony/config/config.js +20 -4
- package/dist/nodefony/config/defineModuleConfig.js +1 -2
- package/dist/nodefony/contracts/IAccessVoter.js +1 -2
- package/dist/nodefony/errors/AccessDeniedError.js +1 -2
- package/dist/nodefony/errors/ApiKeyError.js +3 -2
- package/dist/nodefony/errors/AuthenticationError.js +1 -2
- package/dist/nodefony/errors/CsrfError.js +1 -2
- package/dist/nodefony/errors/InsecureTransportError.js +1 -2
- package/dist/nodefony/errors/InvalidTargetError.js +1 -2
- package/dist/nodefony/errors/SsrfError.js +1 -2
- package/dist/nodefony/errors/ThrottledError.js +1 -2
- package/dist/nodefony/errors/UnverifiableTokenError.js +1 -2
- package/dist/nodefony/errors/WebAuthnError.js +1 -2
- package/dist/nodefony/keycloak/keycloakAdmin.js +1 -2
- package/dist/nodefony/keycloak/keycloakRealm.js +1 -2
- package/dist/nodefony/keycloak/keycloakRealmInput.js +1 -2
- package/dist/nodefony/keycloak/scaffold.js +3 -3
- package/dist/nodefony/keycloak/security-keycloak-realm.js +5 -3
- package/dist/nodefony/scaffold/contribute.js +1 -2
- package/dist/nodefony/service/accessTokenVerifier.js +101 -34
- package/dist/nodefony/service/apiKeys.js +45 -5
- package/dist/nodefony/service/auditService.js +4 -5
- package/dist/nodefony/service/authFlow.js +10 -8
- package/dist/nodefony/service/authorization.js +1 -2
- package/dist/nodefony/service/cors.js +1 -2
- package/dist/nodefony/service/csrf.js +1 -2
- package/dist/nodefony/service/firewall.js +5 -4
- package/dist/nodefony/service/keycloak.js +1 -2
- package/dist/nodefony/service/oauth2.js +29 -7
- package/dist/nodefony/service/securityHeaders.js +1 -2
- package/dist/nodefony/service/tokenService.js +38 -22
- package/dist/nodefony/service/totp.js +4 -5
- package/dist/nodefony/service/webAuthn.js +4 -5
- package/dist/nodefony/service/webhooks.js +4 -5
- package/dist/nodefony/src/RoleHierarchyWalker.js +1 -2
- package/dist/nodefony/src/SecuredArea.js +1 -2
- package/dist/nodefony/src/accountBinding.js +96 -0
- package/dist/nodefony/src/admin/SecurityAdminApi.js +1 -2
- package/dist/nodefony/src/admin/WebhookAdminApi.js +23 -7
- package/dist/nodefony/src/admin/adminAudit.js +1 -2
- package/dist/nodefony/src/admin/userRevocationCascade.js +79 -15
- package/dist/nodefony/src/apikey/apiKeyFormat.js +1 -2
- package/dist/nodefony/src/audit/MemoryAuditStore.js +1 -2
- package/dist/nodefony/src/audit/auditBridge.js +1 -2
- package/dist/nodefony/src/audit/auditFilters.js +1 -2
- package/dist/nodefony/src/audit/auditStoreRegistry.js +1 -2
- package/dist/nodefony/src/audit/readAuditContext.js +1 -2
- package/dist/nodefony/src/audit/recordAudit.js +1 -2
- package/dist/nodefony/src/authenticator/AnonymousAuthenticator.js +1 -2
- package/dist/nodefony/src/authenticator/ApiKeyAuthenticator.js +3 -2
- package/dist/nodefony/src/authenticator/ExternalJwtAuthenticator.js +8 -113
- package/dist/nodefony/src/authenticator/ExternalSubjectResolver.js +180 -0
- package/dist/nodefony/src/authenticator/FirewallRealtimeAuthenticator.js +42 -4
- package/dist/nodefony/src/authenticator/JwtAuthenticator.js +91 -16
- package/dist/nodefony/src/authenticator/SessionAuthenticator.js +9 -5
- package/dist/nodefony/src/authenticator/UserPasswordAuthenticator.js +1 -2
- package/dist/nodefony/src/authenticator/authenticatorRegistry.js +3 -17
- package/dist/nodefony/src/authenticator/externalSubject.js +1 -2
- package/dist/nodefony/src/authenticator/peekIssuer.js +1 -2
- package/dist/nodefony/src/boot/publicRoutesNotice.js +1 -2
- package/dist/nodefony/src/crypto/secretCipher.js +1 -2
- package/dist/nodefony/src/csp.js +1 -2
- package/dist/nodefony/src/csrfToken.js +1 -2
- package/dist/nodefony/src/entryPoint.js +1 -2
- package/dist/nodefony/src/net/ssrfGuard.js +1 -2
- package/dist/nodefony/src/net/transportFailure.js +57 -0
- package/dist/nodefony/src/oauth/clientCredentials.js +1 -2
- package/dist/nodefony/src/oauth/httpJson.js +3 -3
- package/dist/nodefony/src/oauth/metadata.js +5 -16
- package/dist/nodefony/src/oauth/oauth2Client.js +1 -2
- package/dist/nodefony/src/oauth/oauthProviderRegistry.js +1 -2
- package/dist/nodefony/src/oauth/providerDiagnosis.js +5 -6
- package/dist/nodefony/src/oauth/providerRoles.js +1 -2
- package/dist/nodefony/src/oauth/providers/github.js +16 -6
- package/dist/nodefony/src/oauth/providers/oidc.js +9 -6
- package/dist/nodefony/src/openToSignup.js +1 -2
- package/dist/nodefony/src/realtime/UserRealtimeToken.js +17 -3
- package/dist/nodefony/src/realtime/frameAuthorizer.js +1 -2
- package/dist/nodefony/src/rolesWithin.js +57 -0
- package/dist/nodefony/src/sessionAuthentication.js +1 -2
- package/dist/nodefony/src/sessionIdentity.js +29 -3
- package/dist/nodefony/src/throttle/LoginThrottler.js +1 -2
- package/dist/nodefony/src/token/AnonymousToken.js +1 -2
- package/dist/nodefony/src/token/JwtKeystore.js +14 -5
- package/dist/nodefony/src/token/MemoryTokenStore.js +1 -2
- package/dist/nodefony/src/token/RemoteJwtVerifier.js +6 -3
- package/dist/nodefony/src/token/UserToken.js +1 -2
- package/dist/nodefony/src/token/jwtRuntime.js +1 -2
- package/dist/nodefony/src/token/scopeGrants.js +109 -0
- package/dist/nodefony/src/token/secretFile.js +1 -2
- package/dist/nodefony/src/token/tokenCriteria.js +1 -2
- package/dist/nodefony/src/token/tokenFilters.js +1 -2
- package/dist/nodefony/src/token/tokenSort.js +1 -2
- package/dist/nodefony/src/token/tokenStatus.js +1 -2
- package/dist/nodefony/src/token/tokenStoreRegistry.js +1 -2
- package/dist/nodefony/src/totp/MemoryTotpSecretStore.js +1 -2
- package/dist/nodefony/src/totp/totpCipher.js +1 -2
- package/dist/nodefony/src/totp/totpCrypto.js +1 -2
- package/dist/nodefony/src/totp/totpOperations.js +1 -2
- package/dist/nodefony/src/totp/totpSecretStoreRegistry.js +1 -2
- package/dist/nodefony/src/transport/CredentialTransportPolicy.js +1 -2
- package/dist/nodefony/src/voter/RoleVoter.js +1 -2
- package/dist/nodefony/src/voter/ScopeVoter.js +1 -2
- package/dist/nodefony/src/voter/voterRegistry.js +1 -2
- package/dist/nodefony/src/webauthn/MemoryWebAuthnCredentialStore.js +1 -2
- package/dist/nodefony/src/webauthn/webAuthnCredentialStoreRegistry.js +1 -2
- package/dist/nodefony/src/webhook/MemoryWebhookStore.js +1 -2
- package/dist/nodefony/src/webhook/WebhookDispatcher.js +1 -2
- package/dist/nodefony/src/webhook/webhookCipher.js +1 -2
- package/dist/nodefony/src/webhook/webhookDelivery.js +1 -2
- package/dist/nodefony/src/webhook/webhookFilters.js +1 -2
- package/dist/nodefony/src/webhook/webhookSignature.js +1 -2
- package/dist/nodefony/src/webhook/webhookSort.js +1 -2
- package/dist/nodefony/src/webhook/webhookStoreRegistry.js +1 -2
- package/dist/types/nodefony/command/{security-token.d.ts → security-mcp-token.d.ts} +7 -6
- package/dist/types/nodefony/config/config.d.ts +1 -0
- package/dist/types/nodefony/errors/ApiKeyError.d.ts +3 -1
- package/dist/types/nodefony/service/accessTokenVerifier.d.ts +35 -14
- package/dist/types/nodefony/service/apiKeys.d.ts +13 -2
- package/dist/types/nodefony/service/oauth2.d.ts +20 -0
- package/dist/types/nodefony/src/accountBinding.d.ts +82 -0
- package/dist/types/nodefony/src/admin/userRevocationCascade.d.ts +23 -5
- package/dist/types/nodefony/src/authenticator/ExternalJwtAuthenticator.d.ts +2 -44
- package/dist/types/nodefony/src/authenticator/ExternalSubjectResolver.d.ts +99 -0
- package/dist/types/nodefony/src/authenticator/FirewallRealtimeAuthenticator.d.ts +4 -1
- package/dist/types/nodefony/src/authenticator/JwtAuthenticator.d.ts +37 -1
- package/dist/types/nodefony/src/net/transportFailure.d.ts +22 -0
- package/dist/types/nodefony/src/oauth/httpJson.d.ts +0 -8
- package/dist/types/nodefony/src/realtime/UserRealtimeToken.d.ts +7 -1
- package/dist/types/nodefony/src/realtime/realtimeContracts.d.ts +6 -0
- package/dist/types/nodefony/src/rolesWithin.d.ts +27 -0
- package/dist/types/nodefony/src/sessionIdentity.d.ts +22 -0
- package/dist/types/nodefony/src/token/scopeGrants.d.ts +77 -0
- package/docs/api-keys.md +181 -174
- package/docs/audit.md +140 -140
- package/docs/authenticators.md +101 -101
- package/docs/authorization.md +116 -69
- package/docs/cors.md +64 -64
- package/docs/csrf.md +74 -71
- package/docs/external-jwt.md +22 -12
- package/docs/firewall.md +85 -82
- package/docs/headers.md +81 -81
- package/docs/index.md +4 -4
- package/docs/keycloak.md +95 -78
- package/docs/login-page.md +23 -23
- package/docs/oauth2.md +157 -146
- package/docs/obtenir-un-jeton.md +116 -26
- package/docs/tokens.md +128 -122
- package/docs/totp.md +136 -136
- package/docs/webauthn.md +153 -153
- package/docs/webhooks.md +127 -124
- package/package.json +13 -13
- package/dist/_virtual/_@oxc-project_runtime@0.153.0/helpers/esm/decorateMetadata.js +0 -6
|
@@ -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
|
/**
|
|
@@ -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. */
|
|
@@ -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;
|
|
@@ -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
|
|
@@ -31,8 +31,9 @@ export declare class UserRealtimeToken implements IRealtimeToken {
|
|
|
31
31
|
* @param revalidate - preuve de vie de l'identité, adaptée à son mode.
|
|
32
32
|
* @param type - mode d'authentification réel (défaut `"session"`).
|
|
33
33
|
* @param scopes - scopes délégués du jeton d'origine (défaut aucun).
|
|
34
|
+
* @param confirmAccount - relecture du compte, payée sur `api.request` seul.
|
|
34
35
|
*/
|
|
35
|
-
constructor(user: IUser, revalidate?: ((nowMs?: number) => Promise<boolean>) | null, type?: string, scopes?: readonly string[]);
|
|
36
|
+
constructor(user: IUser, revalidate?: ((nowMs?: number) => Promise<boolean>) | null, type?: string, scopes?: readonly string[], confirmAccount?: (() => Promise<boolean>) | null);
|
|
36
37
|
/**
|
|
37
38
|
* Zero Trust : l'identité qui a ouvert cette socket est-elle TOUJOURS vivante ?
|
|
38
39
|
*
|
|
@@ -46,6 +47,11 @@ export declare class UserRealtimeToken implements IRealtimeToken {
|
|
|
46
47
|
* sans dépendre de l'horloge de la machine.
|
|
47
48
|
*/
|
|
48
49
|
isValid(nowMs?: number): Promise<boolean>;
|
|
50
|
+
/**
|
|
51
|
+
* Le compte qui a ouvert la socket existe-t-il toujours, actif, et sous le
|
|
52
|
+
* même `id` immuable ? `true` si aucune confirmation n'est câblée.
|
|
53
|
+
*/
|
|
54
|
+
confirmAccount(): Promise<boolean>;
|
|
49
55
|
getUserIdentifier(): string;
|
|
50
56
|
isAuthenticated(): boolean;
|
|
51
57
|
getRoles(): string[];
|
|
@@ -32,6 +32,12 @@ export interface IRealtimeToken {
|
|
|
32
32
|
* (absent = valide). Appelé par le pont `api.request` avant l'action data plane.
|
|
33
33
|
*/
|
|
34
34
|
isValid?(nowMs?: number): boolean | Promise<boolean>;
|
|
35
|
+
/**
|
|
36
|
+
* Le compte de la socket existe-t-il toujours, actif, sous le même `id` ?
|
|
37
|
+
* (miroir de `IRealtimeToken.confirmAccount` realtime). Optionnel (absent =
|
|
38
|
+
* confirmé). Relit l'annuaire : payé sur `api.request` seul, jamais au tick.
|
|
39
|
+
*/
|
|
40
|
+
confirmAccount?(): Promise<boolean>;
|
|
35
41
|
}
|
|
36
42
|
/**
|
|
37
43
|
* Données de handshake WS (miroir de `IRealtimeHandshake`) — DTO neutre passé à
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { IRolesWithin } from "nodefony";
|
|
2
|
+
import type { IUser } from "@nodefony/user";
|
|
3
|
+
import type { RoleHierarchyWalker } from "./RoleHierarchyWalker.js";
|
|
4
|
+
/**
|
|
5
|
+
* Vue de l'annuaire PLUS permissive que `IUserProvider` (qui promet un
|
|
6
|
+
* `IUser`) : un fournisseur d'application peut rendre `null` ou `undefined`
|
|
7
|
+
* au lieu de lever — sans garde, zéro rôle vaudrait accord. Tout
|
|
8
|
+
* `IUserProvider` la satisfait.
|
|
9
|
+
*/
|
|
10
|
+
export interface IAccountDirectory {
|
|
11
|
+
loadUserByIdentifier(identifier: string): Promise<IUser | null | undefined>;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* La règle « jouer un autre compte » : les rôles de `target`, hiérarchie
|
|
15
|
+
* comprise, sont-ils TOUS atteints par ceux de `owner` ? Jouer un compte ne
|
|
16
|
+
* monte jamais en droits.
|
|
17
|
+
*
|
|
18
|
+
* Fermée en cas de doute : un annuaire absent, un compte introuvable — y
|
|
19
|
+
* compris un sujet externe éphémère, qui n'a pas de compte local — rendent un
|
|
20
|
+
* refus, jamais un accord. L'annuaire est résolu à l'appel : le service `users`
|
|
21
|
+
* peut être posé après le pare-feu.
|
|
22
|
+
*
|
|
23
|
+
* @param resolveUsers - rend l'annuaire des comptes (`users`), ou `null`
|
|
24
|
+
* @param hierarchy - la hiérarchie de rôles du pare-feu
|
|
25
|
+
* @returns la règle, à poser au conteneur sous `ROLES_WITHIN`
|
|
26
|
+
*/
|
|
27
|
+
export declare function rolesWithin(resolveUsers: () => IAccountDirectory | null, hierarchy: RoleHierarchyWalker): IRolesWithin;
|
|
@@ -18,3 +18,25 @@ import type { IUser, IUserProvider } from "@nodefony/user";
|
|
|
18
18
|
* compte verrouillé ou désactivé.
|
|
19
19
|
*/
|
|
20
20
|
export declare function resolveSessionIdentity(provider: IUserProvider, identifier: string): Promise<IUser>;
|
|
21
|
+
/**
|
|
22
|
+
* Résout l'identité d'une session DÉJÀ ouverte, en exigeant qu'elle ait été
|
|
23
|
+
* ouverte pour CE compte — {@link resolveSessionIdentity} plus la liaison à
|
|
24
|
+
* l'identifiant immuable (`isBoundToAccount`).
|
|
25
|
+
*
|
|
26
|
+
* La session range son principal sous l'`identifier`, qu'un compte recréé
|
|
27
|
+
* (suppression hors framework, restauration, import) peut reprendre : sans la
|
|
28
|
+
* liaison, la session de l'ancien titulaire ouvrirait le compte du nouveau.
|
|
29
|
+
* Une session sans liaison est refusée au même titre.
|
|
30
|
+
*
|
|
31
|
+
* @param provider - source d'identité (`UserService` via le container).
|
|
32
|
+
* @param identifier - identifiant fonctionnel stocké en session.
|
|
33
|
+
* @param boundAccountId - identifiant immuable inscrit à l'ouverture
|
|
34
|
+
* (`metaBag`, clé `ACCOUNT_ID_KEY`).
|
|
35
|
+
* @param boundCredentialsVersion - version des identifiants inscrite à
|
|
36
|
+
* l'ouverture (`metaBag`, clé `CREDENTIALS_VERSION_KEY`).
|
|
37
|
+
* @returns l'utilisateur actif, titulaire de la session.
|
|
38
|
+
* @throws AuthenticationError (401, message uniforme) — identifiant inconnu,
|
|
39
|
+
* compte verrouillé ou désactivé, session ouverte pour un autre compte ou
|
|
40
|
+
* avant le dernier changement de mot de passe.
|
|
41
|
+
*/
|
|
42
|
+
export declare function resolveBoundSessionIdentity(provider: IUserProvider, identifier: string, boundAccountId: unknown, boundCredentialsVersion: unknown): Promise<IUser>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { RoleHierarchyWalker } from "../RoleHierarchyWalker.js";
|
|
2
|
+
/**
|
|
3
|
+
* Qui peut OBTENIR un scope d'application : clé = un scope exact ou une
|
|
4
|
+
* famille `préfixe:*`, valeur = les rôles dont UN suffit.
|
|
5
|
+
*/
|
|
6
|
+
export type IScopeGrants = Readonly<Record<string, readonly string[]>>;
|
|
7
|
+
/**
|
|
8
|
+
* Un scope demandé et refusé, avec la règle qui l'a refusé.
|
|
9
|
+
*
|
|
10
|
+
* `admin` = réservé au plan d'administration du framework
|
|
11
|
+
* ({@link refusedAdminScopes}) ; `application` = déclaré dans
|
|
12
|
+
* `grantableScopes` pour des rôles que le porteur n'a pas.
|
|
13
|
+
*/
|
|
14
|
+
export interface IScopeRefusal {
|
|
15
|
+
scope: string;
|
|
16
|
+
rule: "admin" | "application";
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Clés que `grantableScopes` refuse : le plan d'administration est jugé par le
|
|
20
|
+
* framework, et une déclaration ici serait soit sans effet, soit un
|
|
21
|
+
* assouplissement — deux pièges que rien ne signalerait.
|
|
22
|
+
*/
|
|
23
|
+
export declare const RESERVED_SCOPE_GRANT_KEYS: readonly string[];
|
|
24
|
+
/**
|
|
25
|
+
* Dit pourquoi une clé de `grantableScopes` est invalide, ou `null` si elle
|
|
26
|
+
* est acceptable.
|
|
27
|
+
*
|
|
28
|
+
* Une clé est un `scope-token` (RFC 6749 §3.3) ; un `*` n'y est admis qu'en
|
|
29
|
+
* dernière position, juste après `:` (une famille `préfixe:*`).
|
|
30
|
+
*
|
|
31
|
+
* @param key - la clé déclarée.
|
|
32
|
+
* @returns le motif du refus, ou `null`.
|
|
33
|
+
*/
|
|
34
|
+
export declare function scopeGrantKeyProblem(key: string): string | null;
|
|
35
|
+
/**
|
|
36
|
+
* Politique d'attribution des scopes d'application, compilée une fois au boot.
|
|
37
|
+
*
|
|
38
|
+
* Résolution : une clé EXACTE l'emporte sur une famille, et la famille la plus
|
|
39
|
+
* longue sur une plus courte (`shop:admin:*` > `shop:*`). Le résultat ne dépend
|
|
40
|
+
* donc pas de l'ordre des clés. Un scope qu'aucune clé ne couvre reste accordé
|
|
41
|
+
* à tout porteur (comportement historique).
|
|
42
|
+
*/
|
|
43
|
+
export declare class ScopeGrantPolicy {
|
|
44
|
+
#private;
|
|
45
|
+
/**
|
|
46
|
+
* @param grants - la section `grantableScopes`, déjà validée par le schéma.
|
|
47
|
+
*/
|
|
48
|
+
constructor(grants?: IScopeGrants);
|
|
49
|
+
/** `true` si aucune clé n'est déclarée — tout scope d'application est accordé. */
|
|
50
|
+
get isEmpty(): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Les rôles dont UN suffit pour obtenir ce scope.
|
|
53
|
+
*
|
|
54
|
+
* @param scope - un scope demandé.
|
|
55
|
+
* @returns les rôles de la clé la plus spécifique, ou `null` si aucune clé ne
|
|
56
|
+
* le couvre (scope libre).
|
|
57
|
+
*/
|
|
58
|
+
rolesFor(scope: string): readonly string[] | null;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Les scopes demandés que ce porteur ne peut pas obtenir, avec la règle qui
|
|
62
|
+
* refuse chacun.
|
|
63
|
+
*
|
|
64
|
+
* Seul juge des DEUX familles, appelé par chaque émetteur de l'application
|
|
65
|
+
* (jeton d'accès, clé API) : la règle d'administration du framework
|
|
66
|
+
* ({@link refusedAdminScopes}, jugée sur les rôles PLATS du compte, comme le
|
|
67
|
+
* plan d'administration) et la politique de l'application
|
|
68
|
+
* ({@link ScopeGrantPolicy}, jugée hiérarchie de rôles comprise, comme un
|
|
69
|
+
* `@IsGranted`).
|
|
70
|
+
*
|
|
71
|
+
* @param requested - scopes demandés (RFC 6749 §3.3).
|
|
72
|
+
* @param roles - rôles du porteur, tels que son compte les porte.
|
|
73
|
+
* @param policy - politique compilée de `grantableScopes`.
|
|
74
|
+
* @param hierarchy - hiérarchie de rôles ; absente, seuls les rôles plats comptent.
|
|
75
|
+
* @returns les refus, dans l'ordre de la demande — vide si tout est accordé.
|
|
76
|
+
*/
|
|
77
|
+
export declare function refusedScopes(requested: readonly string[], roles: readonly string[], policy: ScopeGrantPolicy, hierarchy: RoleHierarchyWalker | null): IScopeRefusal[];
|