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