emilsoftware-utilities 2.0.0-dev.76 → 2.0.0-dev.78
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/accessi/resource-server/token.js +2 -0
- package/dist/accessi/resource-server/token.js.map +1 -1
- package/dist/accessi/server/standalone.js +2 -1
- package/dist/accessi/server/standalone.js.map +1 -1
- package/dist/accessi-module/AccessiModule.d.ts +9 -0
- package/dist/accessi-module/AccessiModule.js.map +1 -1
- package/dist/accessi-module/Console/accessiFetch.d.ts +7 -0
- package/dist/accessi-module/Console/accessiFetch.js +45 -7
- package/dist/accessi-module/Console/accessiFetch.js.map +1 -1
- package/dist/accessi-module/Console/console.js +83 -22
- package/dist/accessi-module/Console/console.js.map +1 -1
- package/dist/accessi-module/Console/generated/model/getUserByTokenRequest.d.ts +2 -2
- package/dist/accessi-module/Console/generated/model/getUserByTokenRequest.js.map +1 -1
- package/dist/accessi-module/Console/generated/model/refreshTokenRequest.d.ts +2 -2
- package/dist/accessi-module/Console/generated/model/refreshTokenRequest.js.map +1 -1
- package/dist/accessi-module/Console/wiki.js +2 -0
- package/dist/accessi-module/Console/wiki.js.map +1 -1
- package/dist/accessi-module/Controllers/AccessiConsoleController.js +10 -2
- package/dist/accessi-module/Controllers/AccessiConsoleController.js.map +1 -1
- package/dist/accessi-module/Controllers/AccessiIntrospectionController.js +9 -0
- package/dist/accessi-module/Controllers/AccessiIntrospectionController.js.map +1 -1
- package/dist/accessi-module/Controllers/AuthController.js +102 -15
- package/dist/accessi-module/Controllers/AuthController.js.map +1 -1
- package/dist/accessi-module/Controllers/ConsoleEntryController.js +17 -0
- package/dist/accessi-module/Controllers/ConsoleEntryController.js.map +1 -1
- package/dist/accessi-module/Dtos/GetUserByTokenRequest.d.ts +1 -1
- package/dist/accessi-module/Dtos/GetUserByTokenRequest.js +3 -2
- package/dist/accessi-module/Dtos/GetUserByTokenRequest.js.map +1 -1
- package/dist/accessi-module/Dtos/RefreshTokenDtos.d.ts +1 -1
- package/dist/accessi-module/Dtos/RefreshTokenDtos.js +4 -1
- package/dist/accessi-module/Dtos/RefreshTokenDtos.js.map +1 -1
- package/dist/accessi-module/Services/AuthService/AuthService.js +8 -2
- package/dist/accessi-module/Services/AuthService/AuthService.js.map +1 -1
- package/dist/accessi-module/index.d.ts +6 -0
- package/dist/accessi-module/index.js +31 -2
- package/dist/accessi-module/index.js.map +1 -1
- package/dist/accessi-module/observability/metrics.d.ts +79 -0
- package/dist/accessi-module/observability/metrics.js +216 -0
- package/dist/accessi-module/observability/metrics.js.map +1 -0
- package/dist/accessi-module/observability/redisMetrics.d.ts +12 -0
- package/dist/accessi-module/observability/redisMetrics.js +44 -0
- package/dist/accessi-module/observability/redisMetrics.js.map +1 -0
- package/dist/accessi-module/security/accessTokenRevocation.js +5 -3
- package/dist/accessi-module/security/accessTokenRevocation.js.map +1 -1
- package/dist/accessi-module/security/authCookies.d.ts +104 -0
- package/dist/accessi-module/security/authCookies.js +240 -0
- package/dist/accessi-module/security/authCookies.js.map +1 -0
- package/dist/accessi-module/security/consoleEntry.js +5 -3
- package/dist/accessi-module/security/consoleEntry.js.map +1 -1
- package/dist/accessi-module/security/edgeRateLimit.js +3 -0
- package/dist/accessi-module/security/edgeRateLimit.js.map +1 -1
- package/dist/accessi-module/security/jwks.js +3 -0
- package/dist/accessi-module/security/jwks.js.map +1 -1
- package/dist/accessi-module/security/jwtTokenOptions.js +2 -0
- package/dist/accessi-module/security/jwtTokenOptions.js.map +1 -1
- package/dist/accessi-module/security/publicAuthRateLimit.js +3 -0
- package/dist/accessi-module/security/publicAuthRateLimit.js.map +1 -1
- package/dist/accessi-module/security/rateLimitStore.js +4 -2
- package/dist/accessi-module/security/rateLimitStore.js.map +1 -1
- package/dist/accessi-module/security/refreshTokenRotation.js +7 -5
- package/dist/accessi-module/security/refreshTokenRotation.js.map +1 -1
- package/dist/accessi-module/security/serviceTokenRateLimit.js +3 -0
- package/dist/accessi-module/security/serviceTokenRateLimit.js.map +1 -1
- package/dist/accessi-module/security/sessionRevocation.js +7 -3
- package/dist/accessi-module/security/sessionRevocation.js.map +1 -1
- package/dist/accessi-module/security/validateOptions.js +34 -0
- package/dist/accessi-module/security/validateOptions.js.map +1 -1
- package/dist/emilsoftware-utilities-2.0.0-dev.78.tgz +0 -0
- package/dist/initEmilsoftwareModule.js +7 -0
- package/dist/initEmilsoftwareModule.js.map +1 -1
- package/docs/auth-cookies.md +123 -0
- package/docs/metrics.md +153 -0
- package/docs/redis-load-testing.md +88 -0
- package/package.json +4 -3
- package/dist/emilsoftware-utilities-2.0.0-dev.76.tgz +0 -0
|
@@ -27,21 +27,54 @@ function payloadString(payload, key) {
|
|
|
27
27
|
const value = payload[key];
|
|
28
28
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
29
29
|
}
|
|
30
|
+
function readBrowserCookie(name) {
|
|
31
|
+
if (typeof document === "undefined" || !document.cookie) return null;
|
|
32
|
+
for (const part of document.cookie.split(";")) {
|
|
33
|
+
const separator = part.indexOf("=");
|
|
34
|
+
if (separator < 0) continue;
|
|
35
|
+
if (part.slice(0, separator).trim() !== name) continue;
|
|
36
|
+
try {
|
|
37
|
+
return decodeURIComponent(part.slice(separator + 1).trim());
|
|
38
|
+
} catch {
|
|
39
|
+
return part.slice(separator + 1).trim();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
function isCookieAuthMode() {
|
|
45
|
+
return typeof window !== "undefined" && window.__ACCESSI_AUTH_COOKIE__ === true;
|
|
46
|
+
}
|
|
47
|
+
var UNSAFE_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
|
|
30
48
|
async function accessiFetch(url, options) {
|
|
31
49
|
const config = scopedConfig;
|
|
50
|
+
const cookieMode2 = isCookieAuthMode();
|
|
32
51
|
const headers = new Headers(options.headers);
|
|
33
52
|
headers.set("Accept", "application/json");
|
|
34
53
|
if (options.body !== void 0 && !headers.has("Content-Type")) {
|
|
35
54
|
headers.set("Content-Type", "application/json");
|
|
36
55
|
}
|
|
56
|
+
if (cookieMode2) {
|
|
57
|
+
const method = (options.method ?? "GET").toUpperCase();
|
|
58
|
+
if (UNSAFE_METHODS.has(method)) {
|
|
59
|
+
const csrfCookie = window.__ACCESSI_CSRF_COOKIE_NAME__ ?? "accessi_csrf";
|
|
60
|
+
const csrfHeader = window.__ACCESSI_CSRF_HEADER_NAME__ ?? "x-csrf-token";
|
|
61
|
+
const csrfToken = readBrowserCookie(csrfCookie);
|
|
62
|
+
if (csrfToken) headers.set(csrfHeader, csrfToken);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
37
65
|
const resolvedToken = config ? config.getToken ? await config.getToken() : config.token ?? null : tokenProvider ? await tokenProvider() : accessToken;
|
|
38
|
-
const
|
|
66
|
+
const isAuthEntryRequest = /\/(?:auth\/(?:login|refresh|two-factor\/(?:verify|resend))|console\/entry\/exchange)\b/.test(url);
|
|
67
|
+
const authenticatedRequest = !isAuthEntryRequest && (cookieMode2 || resolvedToken !== null && resolvedToken !== void 0);
|
|
39
68
|
if (resolvedToken) headers.set("Authorization", `Bearer ${resolvedToken}`);
|
|
40
69
|
const runtimeBaseUrl = config?.baseUrl !== void 0 ? config.baseUrl : baseUrl ?? (typeof window !== "undefined" ? window.__ACCESSI_API_BASE_URL__ ?? null : null);
|
|
41
70
|
const requestUrl = runtimeBaseUrl && url.startsWith("/") ? `${runtimeBaseUrl.replace(/\/+$/, "")}${url}` : url;
|
|
42
71
|
notifyNetworkActivity(true);
|
|
43
72
|
try {
|
|
44
|
-
const response = await fetch(requestUrl, {
|
|
73
|
+
const response = await fetch(requestUrl, {
|
|
74
|
+
...options,
|
|
75
|
+
headers,
|
|
76
|
+
...cookieMode2 ? { credentials: "include" } : {}
|
|
77
|
+
});
|
|
45
78
|
const payload = await response.json().catch(() => void 0);
|
|
46
79
|
if (!response.ok) {
|
|
47
80
|
const message = payloadString(payload, "message") ?? payloadString(payload, "error") ?? `Errore HTTP ${response.status}`;
|
|
@@ -78,6 +111,21 @@ var getUserByToken = async (getUserByTokenRequest, options) => {
|
|
|
78
111
|
}
|
|
79
112
|
);
|
|
80
113
|
};
|
|
114
|
+
var getRevokeTokenUrl = () => {
|
|
115
|
+
return `/api/accessi/auth/revoke-token`;
|
|
116
|
+
};
|
|
117
|
+
var revokeToken = async (revokeTokenRequest, options) => {
|
|
118
|
+
return accessiFetch(
|
|
119
|
+
getRevokeTokenUrl(),
|
|
120
|
+
{
|
|
121
|
+
...options,
|
|
122
|
+
method: "POST",
|
|
123
|
+
body: JSON.stringify(
|
|
124
|
+
revokeTokenRequest
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
);
|
|
128
|
+
};
|
|
81
129
|
var getLoginUrl = () => {
|
|
82
130
|
return `/api/accessi/auth/login`;
|
|
83
131
|
};
|
|
@@ -1061,6 +1109,8 @@ var WIKI_SECTIONS = [
|
|
|
1061
1109
|
["publicAuthRateLimit", "PublicAuthRateLimitOptions", "no", "Limiti per endpoint pubblici; store condiviso opzionale (Redis) per il cluster."],
|
|
1062
1110
|
["edgeRateLimit", "AccessiEdgeRateLimitOptions", "no", "Limite grossolano per IP davanti alle rotte Accessi; opzionale, store condiviso supportato."],
|
|
1063
1111
|
["http", "{ cors, trustProxy, securityHeaders }", "no", "CORS, IP reali dietro proxy e header di sicurezza con CSP a nonce per la console e direttive configurabili."],
|
|
1112
|
+
["authCookie", "{ enabled, secure, sameSite, csrf, ... }", "no", "Cookie HttpOnly e CSRF double-submit al posto del Bearer. Default disattivato."],
|
|
1113
|
+
["metrics", "{ enabled, sink, prefix }", "no", "Metriche Prometheus di dominio (login, 2FA, refresh, rate limit, store) e sink esterno opzionale."],
|
|
1064
1114
|
["publicRegistration", "{ enabled }", "no", "Registrazione pubblica: disattivata per impostazione predefinita."],
|
|
1065
1115
|
["federatedAuthentication", "FederatedAuthenticationOptions", "no", `Abilita l'SSO generico.`],
|
|
1066
1116
|
["extensionFieldsOptions", "ExtensionFieldsOptions[]", "no", "Tabelle esterne allegate al profilo."],
|
|
@@ -2255,6 +2305,7 @@ var consoleMarkerIndex = window.location.pathname.indexOf(consoleMarker);
|
|
|
2255
2305
|
var consoleBasePath = consoleMarkerIndex >= 0 ? window.location.pathname.slice(0, consoleMarkerIndex + consoleMarker.length) : `${window.location.pathname.replace(/\/$/, "")}`;
|
|
2256
2306
|
var USERS_PAGE_SIZE = 25;
|
|
2257
2307
|
var token = sessionStorage.getItem("accessi-console-token");
|
|
2308
|
+
var cookieMode = typeof window !== "undefined" && window.__ACCESSI_AUTH_COOKIE__ === true;
|
|
2258
2309
|
var currentUser = null;
|
|
2259
2310
|
var federatedAuthenticationAvailable = false;
|
|
2260
2311
|
var usersPage = 0;
|
|
@@ -2708,18 +2759,22 @@ async function completeLoginStep(response) {
|
|
|
2708
2759
|
byId("login-submit").textContent = "Accedi";
|
|
2709
2760
|
return;
|
|
2710
2761
|
}
|
|
2711
|
-
if (
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2762
|
+
if (response.token?.value) {
|
|
2763
|
+
token = response.token.value;
|
|
2764
|
+
sessionStorage.setItem("accessi-console-token", token);
|
|
2765
|
+
setAccessiConsoleToken(token);
|
|
2766
|
+
} else if (!cookieMode) {
|
|
2767
|
+
throw new Error("Risposta di autenticazione incompleta.");
|
|
2768
|
+
}
|
|
2715
2769
|
resetLogin();
|
|
2716
2770
|
await bootstrap();
|
|
2717
2771
|
}
|
|
2718
2772
|
async function bootstrap() {
|
|
2773
|
+
const hadToken = token !== null;
|
|
2719
2774
|
try {
|
|
2720
2775
|
setAccessiConsoleToken(token);
|
|
2721
|
-
if (!token) throw new Error("Inserisci le credenziali amministrative.");
|
|
2722
|
-
const payload = result(await getUserByToken({ token }));
|
|
2776
|
+
if (!token && !cookieMode) throw new Error("Inserisci le credenziali amministrative.");
|
|
2777
|
+
const payload = result(await getUserByToken({ token: token ?? "" }));
|
|
2723
2778
|
currentUser = payload.userData;
|
|
2724
2779
|
requireConsoleAccess();
|
|
2725
2780
|
applyRoleNavigation();
|
|
@@ -2736,19 +2791,20 @@ async function bootstrap() {
|
|
|
2736
2791
|
federatedAuthenticationAvailable = false;
|
|
2737
2792
|
sessionStorage.removeItem("accessi-console-token");
|
|
2738
2793
|
finishBoot("login");
|
|
2739
|
-
byId("login-error").textContent = error instanceof Error ? error.message : "Sessione non valida.";
|
|
2794
|
+
byId("login-error").textContent = cookieMode && !hadToken ? "" : error instanceof Error ? error.message : "Sessione non valida.";
|
|
2740
2795
|
}
|
|
2741
2796
|
}
|
|
2742
2797
|
async function exchangeConsoleEntry(ticket) {
|
|
2743
2798
|
try {
|
|
2744
2799
|
const session = result(await exchangeConsoleEntryTicket({ ticket }));
|
|
2745
2800
|
const value = session.token?.value;
|
|
2746
|
-
if (typeof value
|
|
2801
|
+
if (typeof value === "string" && value !== "") {
|
|
2802
|
+
token = value;
|
|
2803
|
+
sessionStorage.setItem("accessi-console-token", value);
|
|
2804
|
+
setAccessiConsoleToken(value);
|
|
2805
|
+
} else if (!cookieMode) {
|
|
2747
2806
|
throw new Error("Apertura automatica della console non riuscita.");
|
|
2748
2807
|
}
|
|
2749
|
-
token = value;
|
|
2750
|
-
sessionStorage.setItem("accessi-console-token", value);
|
|
2751
|
-
setAccessiConsoleToken(value);
|
|
2752
2808
|
await bootstrap();
|
|
2753
2809
|
} catch (error) {
|
|
2754
2810
|
token = null;
|
|
@@ -4036,13 +4092,18 @@ byId("resend-code").onclick = async () => {
|
|
|
4036
4092
|
}
|
|
4037
4093
|
};
|
|
4038
4094
|
byId("logout").onclick = () => {
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4095
|
+
void (async () => {
|
|
4096
|
+
if (cookieMode) {
|
|
4097
|
+
await revokeToken({}).catch(() => void 0);
|
|
4098
|
+
}
|
|
4099
|
+
resetLogin();
|
|
4100
|
+
token = null;
|
|
4101
|
+
currentUser = null;
|
|
4102
|
+
setAccessiConsoleToken(null);
|
|
4103
|
+
sessionStorage.removeItem("accessi-console-token");
|
|
4104
|
+
window.history.replaceState(null, "", consoleBasePath);
|
|
4105
|
+
void bootstrap();
|
|
4106
|
+
})();
|
|
4046
4107
|
};
|
|
4047
4108
|
byId("nav").onclick = (event) => {
|
|
4048
4109
|
const link = event.target.closest("a[data-view]");
|
|
@@ -4054,7 +4115,7 @@ window.addEventListener("popstate", () => {
|
|
|
4054
4115
|
if (token && currentUser) handleAction(() => show(viewFromLocation()));
|
|
4055
4116
|
});
|
|
4056
4117
|
window.addEventListener(ACCESSI_UNAUTHORIZED_EVENT, () => {
|
|
4057
|
-
if (!token) return;
|
|
4118
|
+
if (!token && !cookieMode) return;
|
|
4058
4119
|
token = null;
|
|
4059
4120
|
currentUser = null;
|
|
4060
4121
|
setAccessiConsoleToken(null);
|
|
@@ -4102,7 +4163,7 @@ if (entryTicket) {
|
|
|
4102
4163
|
window.history.replaceState(null, "", window.location.pathname);
|
|
4103
4164
|
finishBoot("login");
|
|
4104
4165
|
showTwoFactor({ challengeId: ssoChallenge, resendAfterSeconds: 60 });
|
|
4105
|
-
} else if (token) {
|
|
4166
|
+
} else if (token || cookieMode) {
|
|
4106
4167
|
void bootstrap();
|
|
4107
4168
|
} else {
|
|
4108
4169
|
finishBoot("login");
|