@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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { RoleHierarchyWalker } from "../RoleHierarchyWalker.js";
|
|
2
2
|
import "../../contracts/IAccessVoter.js";
|
|
3
|
-
|
|
3
|
+
// nodefony/src/voter/RoleVoter.ts
|
|
4
4
|
/**
|
|
5
5
|
* Voter built-in **niveau A** — résout les attributs `ROLE_*` via la hiérarchie
|
|
6
6
|
* de rôles ({@link RoleHierarchyWalker}).
|
|
@@ -28,5 +28,4 @@ var RoleVoter = class {
|
|
|
28
28
|
return Promise.resolve(vote);
|
|
29
29
|
}
|
|
30
30
|
};
|
|
31
|
-
//#endregion
|
|
32
31
|
export { RoleVoter, RoleVoter as default };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import "../../contracts/IAccessVoter.js";
|
|
2
|
-
|
|
2
|
+
// nodefony/src/voter/ScopeVoter.ts
|
|
3
3
|
/**
|
|
4
4
|
* Types de jetons **non scopables** — identité humaine interactive (session BFF
|
|
5
5
|
* web/Studio/WS, login mot de passe) ou visiteur anonyme. Un scope `api:action`
|
|
@@ -48,5 +48,4 @@ var ScopeVoter = class {
|
|
|
48
48
|
return Promise.resolve(token.getScopes().includes(attribute) ? "GRANT" : "ABSTAIN");
|
|
49
49
|
}
|
|
50
50
|
};
|
|
51
|
-
//#endregion
|
|
52
51
|
export { ScopeVoter, ScopeVoter as default };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { RoleVoter } from "./RoleVoter.js";
|
|
2
2
|
import { ScopeVoter } from "./ScopeVoter.js";
|
|
3
|
-
|
|
3
|
+
// nodefony/src/voter/voterRegistry.ts
|
|
4
4
|
const factories = /* @__PURE__ */ new Map();
|
|
5
5
|
/**
|
|
6
6
|
* Enregistre (ou remplace) la fabrique d'un voter. Appelé par les builtins au
|
|
@@ -16,5 +16,4 @@ function listVoterFactories() {
|
|
|
16
16
|
}
|
|
17
17
|
registerVoterFactory("role", ({ container }) => new RoleVoter(container));
|
|
18
18
|
registerVoterFactory("scope", () => new ScopeVoter());
|
|
19
|
-
//#endregion
|
|
20
19
|
export { listVoterFactories, registerVoterFactory };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { assertPageQuery } from "nodefony";
|
|
2
|
-
|
|
2
|
+
// nodefony/src/webauthn/MemoryWebAuthnCredentialStore.ts
|
|
3
3
|
/**
|
|
4
4
|
* Projection contractuelle : credential complet → vue admin **sans `publicKey`**.
|
|
5
5
|
* Partagée par les backends qui matérialisent des credentials en mémoire.
|
|
@@ -135,5 +135,4 @@ var MemoryWebAuthnCredentialStore = class {
|
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
};
|
|
138
|
-
//#endregion
|
|
139
138
|
export { MemoryWebAuthnCredentialStore, MemoryWebAuthnCredentialStore as default, toWebAuthnSummary };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { MemoryWebAuthnCredentialStore } from "./MemoryWebAuthnCredentialStore.js";
|
|
2
|
-
|
|
2
|
+
// nodefony/src/webauthn/webAuthnCredentialStoreRegistry.ts
|
|
3
3
|
const factories = /* @__PURE__ */ new Map();
|
|
4
4
|
/** Enregistre (ou remplace) la fabrique d'un store de credentials WebAuthn. */
|
|
5
5
|
function registerWebAuthnStore(name, factory) {
|
|
@@ -14,5 +14,4 @@ function listWebAuthnStores() {
|
|
|
14
14
|
return [...factories.keys()];
|
|
15
15
|
}
|
|
16
16
|
registerWebAuthnStore("memory", () => new MemoryWebAuthnCredentialStore());
|
|
17
|
-
//#endregion
|
|
18
17
|
export { getWebAuthnStoreFactory, listWebAuthnStores, registerWebAuthnStore };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { WEBHOOK_DEFAULT_ORDER, WEBHOOK_SORTABLE_FIELDS } from "./webhookSort.js";
|
|
2
2
|
import { assertPageQuery, compareByOrder, pickOrder } from "nodefony";
|
|
3
|
-
|
|
3
|
+
// nodefony/src/webhook/MemoryWebhookStore.ts
|
|
4
4
|
/**
|
|
5
5
|
* Applique les filtres d'{@link IWebhookListQuery} à un endpoint — sémantique de
|
|
6
6
|
* RÉFÉRENCE du contrat, partagée par les backends qui filtrent en mémoire.
|
|
@@ -91,5 +91,4 @@ var MemoryWebhookStore = class {
|
|
|
91
91
|
return n;
|
|
92
92
|
}
|
|
93
93
|
};
|
|
94
|
-
//#endregion
|
|
95
94
|
export { MemoryWebhookStore, matchesWebhookQuery };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { webhookSignatureHeaders } from "./webhookSignature.js";
|
|
2
|
-
|
|
2
|
+
// nodefony/src/webhook/WebhookDispatcher.ts
|
|
3
3
|
/**
|
|
4
4
|
* Dispatcher de webhooks sortants — abonné au journal d'audit, il filtre les
|
|
5
5
|
* événements par souscription, signe (Standard Webhooks v1) et livre, **sans
|
|
@@ -205,5 +205,4 @@ var WebhookDispatcher = class {
|
|
|
205
205
|
this.#queue = null;
|
|
206
206
|
}
|
|
207
207
|
};
|
|
208
|
-
//#endregion
|
|
209
208
|
export { WebhookDispatcher, backoffMs, classifyDelivery, matchesSubscription };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { decryptSecret, deriveKey, encryptSecret, generateEphemeralKey } from "../crypto/secretCipher.js";
|
|
2
2
|
import { Buffer } from "node:buffer";
|
|
3
|
-
|
|
3
|
+
// nodefony/src/webhook/webhookCipher.ts
|
|
4
4
|
/**
|
|
5
5
|
* Chiffrement réversible du secret de signature webhook au repos — façade de
|
|
6
6
|
* domaine au-dessus de {@link ../crypto/secretCipher} (calque {@link ../totp/totpCipher}).
|
|
@@ -23,5 +23,4 @@ const WEBHOOK_DERIVATION = {
|
|
|
23
23
|
function deriveWebhookKey(material) {
|
|
24
24
|
return deriveKey(material, WEBHOOK_DERIVATION);
|
|
25
25
|
}
|
|
26
|
-
//#endregion
|
|
27
26
|
export { decryptSecret, deriveWebhookKey, encryptSecret, generateEphemeralKey };
|
|
@@ -2,7 +2,7 @@ import { Buffer } from "node:buffer";
|
|
|
2
2
|
import { isIP } from "node:net";
|
|
3
3
|
import { request } from "node:https";
|
|
4
4
|
import { request as request$1 } from "node:http";
|
|
5
|
-
|
|
5
|
+
// nodefony/src/webhook/webhookDelivery.ts
|
|
6
6
|
/**
|
|
7
7
|
* Émetteur HTTP d'une livraison webhook — `node:http(s)` natif (0 dép). Trois
|
|
8
8
|
* propriétés de sécurité par rapport à `fetch` :
|
|
@@ -98,5 +98,4 @@ function deliverWebhook(url, body, headers, opts) {
|
|
|
98
98
|
req.end();
|
|
99
99
|
});
|
|
100
100
|
}
|
|
101
|
-
//#endregion
|
|
102
101
|
export { deliverWebhook };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// nodefony/src/webhook/webhookFilters.ts
|
|
2
2
|
/**
|
|
3
3
|
* **Le vocabulaire de filtre des endpoints webhook**, en noms PUBLICS — ceux
|
|
4
4
|
* qu'un client écrit dans l'URL (`?enabled=false&event=user.created`).
|
|
@@ -52,5 +52,4 @@ const WEBHOOK_FACETS = {
|
|
|
52
52
|
* Un test verrouille l'accord entre cette liste et {@link WEBHOOK_FACETS}.
|
|
53
53
|
*/
|
|
54
54
|
const WEBHOOK_STATS_FILTERS = { event: "string" };
|
|
55
|
-
//#endregion
|
|
56
55
|
export { WEBHOOK_FACETS, WEBHOOK_FILTERS, WEBHOOK_STATS_FILTERS };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHmac } from "node:crypto";
|
|
2
2
|
import { Buffer } from "node:buffer";
|
|
3
|
-
|
|
3
|
+
// nodefony/src/webhook/webhookSignature.ts
|
|
4
4
|
/**
|
|
5
5
|
* Signature des webhooks sortants — schéma **Standard Webhooks v1**
|
|
6
6
|
* (standardwebhooks.com), choisi plutôt que RFC 9421 (trop lourd pour un webhook,
|
|
@@ -47,5 +47,4 @@ function webhookSignatureHeaders(secret, id, timestampS, body) {
|
|
|
47
47
|
"webhook-signature": signStandardWebhook(secret, id, timestampS, body)
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
|
-
//#endregion
|
|
51
50
|
export { buildSignatureBase, parseWebhookSecret, signStandardWebhook, webhookSignatureHeaders };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// nodefony/src/webhook/webhookSort.ts
|
|
2
2
|
/**
|
|
3
3
|
* **Le vocabulaire de tri des endpoints webhook**, en noms PUBLICS — ceux qu'un
|
|
4
4
|
* client écrit dans l'URL (`?order=failureCount:DESC`), jamais des noms de
|
|
@@ -44,5 +44,4 @@ const WEBHOOK_SORTABLE_FIELDS = [
|
|
|
44
44
|
* des deux ne jamais apparaître.
|
|
45
45
|
*/
|
|
46
46
|
const WEBHOOK_DEFAULT_ORDER = [["createdAt", "DESC"], ["id", "ASC"]];
|
|
47
|
-
//#endregion
|
|
48
47
|
export { WEBHOOK_DEFAULT_ORDER, WEBHOOK_SORTABLE_FIELDS };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { MemoryWebhookStore } from "./MemoryWebhookStore.js";
|
|
2
|
-
|
|
2
|
+
// nodefony/src/webhook/webhookStoreRegistry.ts
|
|
3
3
|
const factories = /* @__PURE__ */ new Map();
|
|
4
4
|
/** Enregistre (ou remplace) la fabrique d'un store d'endpoints webhook. */
|
|
5
5
|
function registerWebhookStore(name, factory) {
|
|
@@ -14,5 +14,4 @@ function listWebhookStores() {
|
|
|
14
14
|
return [...factories.keys()];
|
|
15
15
|
}
|
|
16
16
|
registerWebhookStore("memory", () => new MemoryWebhookStore());
|
|
17
|
-
//#endregion
|
|
18
17
|
export { getWebhookStoreFactory, listWebhookStores, registerWebhookStore };
|
|
@@ -20,7 +20,7 @@ export declare function ttlSeconds(raw: string | undefined): number | undefined
|
|
|
20
20
|
*/
|
|
21
21
|
export declare function tokenTtlSeconds(raw: string | undefined, write: boolean): number | undefined | Error;
|
|
22
22
|
/**
|
|
23
|
-
* `nodefony security:token` — émet un jeton d'accès pour une porte de cette
|
|
23
|
+
* `nodefony security:mcp:token` — émet un jeton d'accès pour une porte de cette
|
|
24
24
|
* application (la porte MCP par défaut).
|
|
25
25
|
*
|
|
26
26
|
* **Pourquoi une commande, et pas un `curl`.** Le jeton s'obtenait par un appel
|
|
@@ -34,11 +34,12 @@ export declare function tokenTtlSeconds(raw: string | undefined, write: boolean)
|
|
|
34
34
|
* raison d'être de la liaison d'audience. La commande la vise d'elle-même,
|
|
35
35
|
* `--resource` ne sert qu'à en viser une autre.
|
|
36
36
|
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
37
|
+
* `--write` pose la valeur là où l'AGENT la lit (gitignoré), jamais dans le
|
|
38
|
+
* `.env` de l'application. Relancée, elle REMPLACE le jeton chez les agents qui
|
|
39
|
+
* le portent déjà — la rotation est le geste le plus fréquent — et n'en câble
|
|
40
|
+
* aucun nouveau sans le proposer.
|
|
40
41
|
*/
|
|
41
|
-
declare class
|
|
42
|
+
declare class SecurityMcpToken extends Command {
|
|
42
43
|
#private;
|
|
43
44
|
constructor(cli: CliKernel);
|
|
44
45
|
generate(identifierArg: string | undefined, opts: {
|
|
@@ -50,4 +51,4 @@ declare class SecurityToken extends Command {
|
|
|
50
51
|
json?: boolean;
|
|
51
52
|
}): Promise<this>;
|
|
52
53
|
}
|
|
53
|
-
export default
|
|
54
|
+
export default SecurityMcpToken;
|
|
@@ -31,6 +31,7 @@ export declare const securityConfigSchema: z.ZodObject<{
|
|
|
31
31
|
rounds: z.ZodDefault<z.ZodNumber>;
|
|
32
32
|
}, z.core.$strict>>>;
|
|
33
33
|
roleHierarchy: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>;
|
|
34
|
+
grantableScopes: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>;
|
|
34
35
|
areas: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
35
36
|
pattern: z.ZodString;
|
|
36
37
|
security: z.ZodDefault<z.ZodBoolean>;
|
|
@@ -5,6 +5,8 @@ import { nodefonyError } from "nodefony";
|
|
|
5
5
|
* classes de `@nodefony/security`).
|
|
6
6
|
*
|
|
7
7
|
* - `400` — entrée invalide (nom vide, scope hors catalogue) ;
|
|
8
|
+
* - `403` — scope demandé que le porteur ne peut pas obtenir (`grantableScopes`,
|
|
9
|
+
* ou `admin:*` sans le rôle d'administration) ;
|
|
8
10
|
* - `409` — plafond `apiKeys.maxPerSubject` atteint ;
|
|
9
11
|
* - `503` — store de jetons indisponible (clés activées mais non provisionnées).
|
|
10
12
|
*
|
|
@@ -12,6 +14,6 @@ import { nodefonyError } from "nodefony";
|
|
|
12
14
|
* 401, message uniforme anti-énumération).
|
|
13
15
|
*/
|
|
14
16
|
export declare class ApiKeyError extends nodefonyError {
|
|
15
|
-
constructor(message: string, code: 400 | 409 | 503);
|
|
17
|
+
constructor(message: string, code: 400 | 403 | 409 | 503);
|
|
16
18
|
}
|
|
17
19
|
export default ApiKeyError;
|
|
@@ -1,29 +1,50 @@
|
|
|
1
1
|
import { Service, Module } from "nodefony";
|
|
2
|
-
import {
|
|
2
|
+
import type { IAccessTokenVerifier } from "nodefony";
|
|
3
3
|
/**
|
|
4
|
-
* Pose (ou non) le vérificateur de jetons d'accès
|
|
4
|
+
* Pose (ou non) le vérificateur de jetons d'accès dans le conteneur — celui
|
|
5
|
+
* qu'une ressource protégée (porte MCP, zone `external-jwt`) interroge.
|
|
5
6
|
*
|
|
6
|
-
* Ce service est une **décision de câblage**, pas de la cryptographie :
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Ce service est une **décision de câblage**, pas de la cryptographie : la
|
|
8
|
+
* vérification d'un jeton TIERS vit dans {@link RemoteJwtVerifier}, celle d'un
|
|
9
|
+
* jeton de CETTE application dans {@link JwtAuthenticator}, et la doctrine de
|
|
10
|
+
* refus dans le cœur (`nodefony/src/oauth/`). Ici, on tranche deux questions :
|
|
11
|
+
* l'application signe-t-elle ses jetons, et en accepte-t-elle d'ailleurs ?
|
|
10
12
|
*
|
|
11
|
-
* **
|
|
12
|
-
*
|
|
13
|
-
*
|
|
13
|
+
* ⭐ **Un jeton signé par cette application est TOUJOURS vérifiable**, dès que
|
|
14
|
+
* sa capacité JWT est active (`security.jwt.enabled`, le défaut) — sans rien
|
|
15
|
+
* déclarer dans `resourceServer.issuers`. Une application qui émet un jeton
|
|
16
|
+
* pour sa propre porte (`nodefony security:mcp:token --resource …`) doit pouvoir
|
|
17
|
+
* le lire : sans cette règle, la porte refusait un jeton parfaitement valide,
|
|
18
|
+
* et l'application était un serveur d'autorisation dont aucune ressource ne
|
|
19
|
+
* reconnaissait la signature. Rien de nouveau ne devient valide : l'audience
|
|
20
|
+
* reste exigée, et seules celles de `jwt.audiences` sont émises.
|
|
21
|
+
*
|
|
22
|
+
* 🔴 **Il est vérifié par les règles de l'authentificateur `jwt`, pas par son
|
|
23
|
+
* jeu de clés seul** : révocation ciblée et en masse, compte encore actif. Lu
|
|
24
|
+
* comme un jeton tiers, un jeton RÉVOQUÉ — ou celui d'un compte désactivé —
|
|
25
|
+
* ouvrait encore la porte jusqu'à son expiration.
|
|
26
|
+
*
|
|
27
|
+
* L'aiguillage se fait sur l'émetteur revendiqué (`peekIssuer`), comme dans le
|
|
28
|
+
* pare-feu : l'émetteur de l'application va à la vérification locale, tout
|
|
29
|
+
* autre à la liste fermée des émetteurs tiers. Un émetteur inconnu est refusé
|
|
30
|
+
* sans la moindre requête sortante.
|
|
31
|
+
*
|
|
32
|
+
* **Rien à vérifier = rien n'est posé** (capacité JWT coupée, aucun émetteur
|
|
33
|
+
* tiers) : une porte protégée qui ne trouve pas de vérificateur refuse alors de
|
|
34
|
+
* servir en le disant (503 + CRITIC), là où un vérificateur vide refuserait
|
|
14
35
|
* chaque jeton un par un — même résultat pour l'appelant, diagnostic beaucoup
|
|
15
36
|
* plus difficile pour l'exploitant.
|
|
16
37
|
*
|
|
17
|
-
* Le coût est nul
|
|
18
|
-
*
|
|
19
|
-
*
|
|
38
|
+
* Le coût est nul tant qu'aucun jeton n'est présenté : aucune requête au
|
|
39
|
+
* démarrage, clés locales lues au premier jeton, découverte d'un émetteur tiers
|
|
40
|
+
* au premier jeton qui s'en réclame.
|
|
20
41
|
*/
|
|
21
42
|
declare class AccessTokenVerifierService extends Service {
|
|
22
43
|
#private;
|
|
23
44
|
module: Module;
|
|
24
45
|
constructor(module: Module);
|
|
25
|
-
/** Le vérificateur
|
|
26
|
-
get verifier():
|
|
46
|
+
/** Le vérificateur posé, ou `null` si rien n'est vérifiable. */
|
|
47
|
+
get verifier(): IAccessTokenVerifier | null;
|
|
27
48
|
}
|
|
28
49
|
export default AccessTokenVerifierService;
|
|
29
50
|
export { AccessTokenVerifierService };
|
|
@@ -40,11 +40,22 @@ declare class ApiKeyService extends Service {
|
|
|
40
40
|
* Émet une nouvelle clé API pour un porteur — renvoie sa vue publique **+ le
|
|
41
41
|
* token en clair** (affiché une seule fois).
|
|
42
42
|
*
|
|
43
|
+
* Un scope que le porteur ne peut pas OBTENIR — réservé par
|
|
44
|
+
* `grantableScopes` à des rôles qu'il n'a pas, ou `admin:*` sans le rôle
|
|
45
|
+
* d'administration — refuse la création entière : une clé émise avec moins
|
|
46
|
+
* que demandé surprendrait sans qu'aucune réponse ne le dise. Même juge que
|
|
47
|
+
* l'émission des jetons ({@link refusedScopes}).
|
|
48
|
+
*
|
|
49
|
+
* @param roles - rôles du porteur tels que son compte les porte ; omis =
|
|
50
|
+
* aucun rôle, donc tout scope réservé est refusé (fail-closed).
|
|
43
51
|
* @throws ApiKeyError 400 — nom vide/trop long, scope hors catalogue, expiry invalide.
|
|
52
|
+
* @throws ApiKeyError 403 — scope réservé à des rôles que le porteur n'a pas.
|
|
44
53
|
* @throws ApiKeyError 409 — plafond `maxPerSubject` atteint.
|
|
45
|
-
* @throws ApiKeyError
|
|
54
|
+
* @throws ApiKeyError 403 — aucun compte sous cet identifiant (la clé est liée
|
|
55
|
+
* à l'`id` immuable du compte, lu dans l'annuaire).
|
|
56
|
+
* @throws ApiKeyError 503 — store ou annuaire `users` indisponible.
|
|
46
57
|
*/
|
|
47
|
-
createForSubject(subjectId: string, subjectType: "user" | "service", opts: ICreateApiKeyOptions): Promise<IApiKeyCreated>;
|
|
58
|
+
createForSubject(subjectId: string, subjectType: "user" | "service", opts: ICreateApiKeyOptions, roles?: readonly string[]): Promise<IApiKeyCreated>;
|
|
48
59
|
/** Liste les clés (PAT) d'un porteur — vue publique, sans secret, récentes d'abord. */
|
|
49
60
|
listForSubject(subjectId: string): Promise<IApiKeyView[]>;
|
|
50
61
|
/**
|
|
@@ -71,6 +71,26 @@ export interface IOAuthAuthorization {
|
|
|
71
71
|
* @throws BootConfigurationError - émetteur requis absent, ou mal formé.
|
|
72
72
|
*/
|
|
73
73
|
export declare function checkProviderIssuer(name: string, issuer: string | undefined): void;
|
|
74
|
+
/**
|
|
75
|
+
* Annonce qu'un annuaire accorde des rôles de PLATEFORME, à la sévérité de
|
|
76
|
+
* l'environnement — ou `null` quand il n'y a rien à dire.
|
|
77
|
+
*
|
|
78
|
+
* L'ouverture est ÉCRITE (`allowPlatformRoles`), donc voulue : l'annonce ne
|
|
79
|
+
* la conteste pas, elle la rappelle. Le risque — un annuaire compromis
|
|
80
|
+
* administre l'instance — n'est réel qu'en production : là, `WARNING` ;
|
|
81
|
+
* ailleurs le même fait est dit en `INFO`, sans sonner une alarme à chaque
|
|
82
|
+
* démarrage d'un décor de développement.
|
|
83
|
+
*
|
|
84
|
+
* @param name - nom du fournisseur (`oauth2.providers.<name>`).
|
|
85
|
+
* @param allowed - `allowPlatformRoles` tel que déclaré.
|
|
86
|
+
* @param granted - rôles de l'application que la table accorde.
|
|
87
|
+
* @param production - l'application tourne en production.
|
|
88
|
+
* @returns le message et sa sévérité, ou `null`.
|
|
89
|
+
*/
|
|
90
|
+
export declare function platformRolesNotice(name: string, allowed: boolean | undefined, granted: readonly string[], production: boolean): {
|
|
91
|
+
message: string;
|
|
92
|
+
severity: "WARNING" | "INFO";
|
|
93
|
+
} | null;
|
|
74
94
|
/**
|
|
75
95
|
* **Social login OAuth 2.0** (P6 J9) — orchestrateur du flux *Authorization Code*.
|
|
76
96
|
*
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { IUser, IUserProvider } from "@nodefony/user";
|
|
2
|
+
/**
|
|
3
|
+
* Claim d'un jeton d'accès émis par l'application qui porte l'identifiant
|
|
4
|
+
* IMMUABLE du compte (`IUser.id`) — `sub` porte l'identifiant fonctionnel.
|
|
5
|
+
*/
|
|
6
|
+
export declare const ACCOUNT_ID_CLAIM = "uid";
|
|
7
|
+
/**
|
|
8
|
+
* Clé, dans `metadata` d'un enregistrement de jeton (refresh, clé d'API) comme
|
|
9
|
+
* dans le `metaBag` d'une session, qui porte l'identifiant immuable du compte.
|
|
10
|
+
*/
|
|
11
|
+
export declare const ACCOUNT_ID_KEY = "accountId";
|
|
12
|
+
/**
|
|
13
|
+
* Claim d'un jeton d'accès émis par l'application qui porte la version des
|
|
14
|
+
* identifiants du compte à l'émission (`IUser.credentialsVersion`).
|
|
15
|
+
*/
|
|
16
|
+
export declare const CREDENTIALS_VERSION_CLAIM = "cv";
|
|
17
|
+
/**
|
|
18
|
+
* Clé, dans `metadata` d'un enregistrement de jeton comme dans le `metaBag`
|
|
19
|
+
* d'une session, qui porte la version des identifiants lue à l'émission.
|
|
20
|
+
*/
|
|
21
|
+
export declare const CREDENTIALS_VERSION_KEY = "credentialsVersion";
|
|
22
|
+
/**
|
|
23
|
+
* La version des identifiants à inscrire dans un artefact qu'on émet pour ce
|
|
24
|
+
* compte — 0 quand le compte n'en porte pas encore.
|
|
25
|
+
*
|
|
26
|
+
* @param user - compte pour lequel l'artefact est émis.
|
|
27
|
+
* @returns la version courante, jamais `null`.
|
|
28
|
+
*/
|
|
29
|
+
export declare function credentialsVersionOf(user: IUser): number;
|
|
30
|
+
/**
|
|
31
|
+
* Dit si un artefact d'accès (jeton, session, clé) appartient bien au compte
|
|
32
|
+
* qu'on vient de recharger par son identifiant fonctionnel, ET s'il a été émis
|
|
33
|
+
* depuis le dernier changement de mot de passe.
|
|
34
|
+
*
|
|
35
|
+
* Les artefacts sont rangés sous l'`identifier`, qu'un compte RECRÉÉ peut
|
|
36
|
+
* reprendre : supprimé hors du framework (`DELETE` SQL, restauration, import)
|
|
37
|
+
* puis revenu sous le même nom, l'identité se rechargerait et l'artefact de
|
|
38
|
+
* l'ancien titulaire ouvrirait le compte du nouveau. L'identifiant immuable
|
|
39
|
+
* lève l'ambiguïté sans dépendre d'aucun événement ni d'aucun stockage : deux
|
|
40
|
+
* comptes successifs portent deux `id` différents. SEULE règle de
|
|
41
|
+
* comparaison — chaque authentificateur l'appelle, aucun ne la recopie.
|
|
42
|
+
*
|
|
43
|
+
* Un artefact SANS liaison est refusé : il a été émis avant que la règle
|
|
44
|
+
* existe, et rien ne dit à qui il appartient.
|
|
45
|
+
*
|
|
46
|
+
* La version des identifiants fait de la coupure au changement de mot de passe
|
|
47
|
+
* un ÉTAT du compte, et non une suppression : si la cascade de révocation
|
|
48
|
+
* échoue, ou si une requête en vol sur un autre processus réécrit la session
|
|
49
|
+
* qu'on vient d'effacer, l'artefact porte toujours l'ancienne version, et il
|
|
50
|
+
* tombe ici. Un artefact émis avant cette règle n'en porte pas : il vaut 0,
|
|
51
|
+
* comme un compte qui n'a jamais changé de mot de passe — le déploiement ne
|
|
52
|
+
* déconnecte personne. Une valeur d'un autre type est refusée.
|
|
53
|
+
*
|
|
54
|
+
* ⚠️ Chemin chaud — appelée sur CHAQUE requête authentifiée : deux comparaisons
|
|
55
|
+
* de chaîne et d'entier, aucune allocation.
|
|
56
|
+
*
|
|
57
|
+
* @param user - compte rechargé par l'identifiant que porte l'artefact.
|
|
58
|
+
* @param boundAccountId - identifiant immuable inscrit dans l'artefact.
|
|
59
|
+
* @param boundCredentialsVersion - version des identifiants inscrite dans
|
|
60
|
+
* l'artefact ({@link CREDENTIALS_VERSION_KEY}), `undefined` si absente.
|
|
61
|
+
* Obligatoire dans la signature pour qu'aucune porte ne l'oublie.
|
|
62
|
+
* @returns `true` si l'artefact a été émis pour CE compte, à sa version courante.
|
|
63
|
+
*/
|
|
64
|
+
export declare function isBoundToAccount(user: IUser, boundAccountId: unknown, boundCredentialsVersion: unknown): boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Recharge le compte qu'un jeton d'accès de CETTE application désigne, ou
|
|
67
|
+
* `null` : sujet inconnu, compte inactif ou verrouillé, jeton émis pour un
|
|
68
|
+
* autre compte du même nom ou avant un changement de mot de passe.
|
|
69
|
+
*
|
|
70
|
+
* SEULE implémentation de la règle « ce jeton ouvre-t-il encore ce compte ? »
|
|
71
|
+
* pour un jeton maison : la zone `jwt` (`JwtAuthenticator`) et le résolveur de
|
|
72
|
+
* compte d'une ressource protégée l'appellent, aucun ne la recopie. Un refus
|
|
73
|
+
* est `null`, jamais une exception : chaque appelant le traduit dans son
|
|
74
|
+
* propre contrat (401 uniforme, outil en `isError`).
|
|
75
|
+
*
|
|
76
|
+
* @param provider - annuaire des comptes de l'application.
|
|
77
|
+
* @param identifier - identifiant fonctionnel porté par le jeton (`sub`).
|
|
78
|
+
* @param boundAccountId - identifiant immuable inscrit dans le jeton ({@link ACCOUNT_ID_CLAIM}).
|
|
79
|
+
* @param boundCredentialsVersion - version des identifiants inscrite ({@link CREDENTIALS_VERSION_CLAIM}).
|
|
80
|
+
* @returns le compte, ou `null` s'il n'est pas (ou plus) celui du jeton.
|
|
81
|
+
*/
|
|
82
|
+
export declare function loadBoundAccount(provider: IUserProvider, identifier: string, boundAccountId: unknown, boundCredentialsVersion: unknown): Promise<IUser | null>;
|
|
@@ -29,6 +29,23 @@ interface KernelListenerLike {
|
|
|
29
29
|
* @param now - horloge (epoch ms) injectable pour les tests.
|
|
30
30
|
*/
|
|
31
31
|
export declare function cascadeUserRevocation(container: Container, event: IUserRevokedEvent, now?: number): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Purge, à la NAISSANCE d'un compte, tout ce qu'un ancien titulaire du même
|
|
34
|
+
* identifiant a laissé derrière lui : sessions, jetons et facteurs. Un compte
|
|
35
|
+
* qui vient de naître n'a encore rien émis — tout artefact déjà rangé sous son
|
|
36
|
+
* identifiant appartient donc à quelqu'un d'autre. C'est ce qui ferme le cas
|
|
37
|
+
* d'un compte supprimé HORS du framework (`DELETE` SQL, aucun événement), dont
|
|
38
|
+
* l'accès redeviendrait valide dès qu'un compte renaît sous son nom.
|
|
39
|
+
*
|
|
40
|
+
* Le seuil est tronqué à la SECONDE : l'`iat` d'un JWT l'est aussi (RFC 7519
|
|
41
|
+
* §2, NumericDate), et un seuil à la milliseconde rejetterait le jeton du
|
|
42
|
+
* nouveau titulaire émis dans la même seconde que sa création.
|
|
43
|
+
*
|
|
44
|
+
* @param container - container du kernel.
|
|
45
|
+
* @param identifier - `IUser.identifier` du compte créé.
|
|
46
|
+
* @param now - horloge (epoch ms) injectable pour les tests.
|
|
47
|
+
*/
|
|
48
|
+
export declare function purgeInheritedAccess(container: ServiceLookupLike, identifier: string, now?: number): Promise<void>;
|
|
32
49
|
/**
|
|
33
50
|
* Retire les facteurs rangés sous un identifiant — secret TOTP (codes de
|
|
34
51
|
* récupération compris) et passkeys. SEULE implémentation du geste, appelée à
|
|
@@ -45,11 +62,12 @@ export declare function removeAccountFactors(container: ServiceLookupLike, ident
|
|
|
45
62
|
* `@nodefony/security` — avant qu'aucun compte ne puisse être créé ou révoqué
|
|
46
63
|
* (les comptes semés au démarrage compris). Deux abonnements :
|
|
47
64
|
* - {@link USER_REVOKED_EVENT} → {@link cascadeUserRevocation} ;
|
|
48
|
-
* - {@link USER_CREATED_EVENT} →
|
|
49
|
-
* vient de naître n'a encore rien
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
65
|
+
* - {@link USER_CREATED_EVENT} → {@link purgeInheritedAccess} : sessions,
|
|
66
|
+
* jetons et facteurs HÉRITÉS. Un compte qui vient de naître n'a encore rien
|
|
67
|
+
* émis ni enrôlé, donc tout artefact déjà rangé sous son identifiant
|
|
68
|
+
* appartient à un ancien titulaire. Règle exacte, aucune date à comparer.
|
|
69
|
+
* ⚠️ Un import qui chargerait les facteurs AVANT les comptes les verrait
|
|
70
|
+
* retirés : importer les comptes d'abord.
|
|
53
71
|
*
|
|
54
72
|
* Les deux sont émis par `UserService` seul, et attendus : chaque handler rend
|
|
55
73
|
* sa promesse. **Extensible** : tout autre module (webhooks…) s'abonne aux
|
|
@@ -3,50 +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
|
-
/** Comment le sujet d'un jeton devient un utilisateur de cette application. */
|
|
9
|
-
export type ExternalSubjectPolicy = "require" | "ephemeral";
|
|
10
|
-
/** Un émetteur reconnu, et la façon dont ses sujets entrent chez nous. */
|
|
11
|
-
export interface IExternalIssuerBinding {
|
|
12
|
-
/** Émetteur de confiance, sous sa forme canonique. */
|
|
13
|
-
issuer: string;
|
|
14
|
-
/** Comment le `sub` de CET émetteur devient un identifiant local. */
|
|
15
|
-
subjectMapping: ExternalSubjectMapping;
|
|
16
|
-
}
|
|
17
|
-
/** Ce que la fabrique doit fournir à l'authenticator. */
|
|
18
|
-
export interface IExternalJwtAuthenticatorOptions {
|
|
19
|
-
/**
|
|
20
|
-
* Émetteurs de confiance, et leur politique de sujet.
|
|
21
|
-
*
|
|
22
|
-
* La liste sert à deux choses, qu'il ne faut pas confondre : reconnaître les
|
|
23
|
-
* jetons qui relèvent de cet authenticator (aiguillage — elle n'accorde
|
|
24
|
-
* rien, le vérificateur refait le contrôle sur sa propre liste), et savoir
|
|
25
|
-
* dans quel espace de noms lire le sujet de chacun.
|
|
26
|
-
*/
|
|
27
|
-
issuers: readonly IExternalIssuerBinding[];
|
|
28
|
-
/** Politique de rattachement du sujet à un utilisateur local. */
|
|
29
|
-
subjectPolicy: ExternalSubjectPolicy;
|
|
30
|
-
/** Rôles accordés en mode `ephemeral`. */
|
|
31
|
-
ephemeralRoles: readonly string[];
|
|
32
|
-
/**
|
|
33
|
-
* Fournisseurs de CONNEXION (`oauth2.providers`) qui déclarent leur émetteur.
|
|
34
|
-
*
|
|
35
|
-
* Quand l'émetteur d'un jeton est celui d'un fournisseur de connexion, la
|
|
36
|
-
* même personne a pu se connecter par le navigateur : son compte local porte
|
|
37
|
-
* alors le lien `(fournisseur, sub)`. Le chercher d'abord évite qu'un jeton
|
|
38
|
-
* d'API désigne un AUTRE compte que la session de son porteur.
|
|
39
|
-
*/
|
|
40
|
-
oauthProviders?: readonly {
|
|
41
|
-
name: string;
|
|
42
|
-
issuer: string;
|
|
43
|
-
/**
|
|
44
|
-
* Table de correspondance des rôles du fournisseur, compilée — absente ou
|
|
45
|
-
* `null` : les rôles du compte lié restent locaux.
|
|
46
|
-
*/
|
|
47
|
-
roleMapping?: IProviderRoleMapping | null;
|
|
48
|
-
}[];
|
|
49
|
-
}
|
|
6
|
+
import { type IExternalJwtAuthenticatorOptions } from "./ExternalSubjectResolver.js";
|
|
7
|
+
export type { ExternalSubjectPolicy, IExternalIssuerBinding, IExternalJwtAuthenticatorOptions, } from "./ExternalSubjectResolver.js";
|
|
50
8
|
/**
|
|
51
9
|
* Authentification par **jeton d'accès émis par un serveur d'autorisation
|
|
52
10
|
* TIERS** (Keycloak, Auth0, Entra, ou l'émetteur d'une flotte d'agents).
|