@spidy092/auth-client 3.1.3 → 3.1.5
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/api.cjs +34 -0
- package/dist/api.cjs.map +1 -1
- package/dist/api.js +34 -0
- package/dist/api.js.map +1 -1
- package/dist/index.cjs +194 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +194 -31
- package/dist/index.js.map +1 -1
- package/dist/preferences.cjs +34 -0
- package/dist/preferences.cjs.map +1 -1
- package/dist/preferences.js +34 -0
- package/dist/preferences.js.map +1 -1
- package/dist/react/AuthProvider.cjs +188 -31
- package/dist/react/AuthProvider.cjs.map +1 -1
- package/dist/react/AuthProvider.js +188 -31
- package/dist/react/AuthProvider.js.map +1 -1
- package/dist/react/PreferencesProvider.cjs +34 -0
- package/dist/react/PreferencesProvider.cjs.map +1 -1
- package/dist/react/PreferencesProvider.js +34 -0
- package/dist/react/PreferencesProvider.js.map +1 -1
- package/dist/react/useAuth.cjs.map +1 -1
- package/dist/react/useAuth.js.map +1 -1
- package/dist/react/useSessionMonitor.cjs +61 -1
- package/dist/react/useSessionMonitor.cjs.map +1 -1
- package/dist/react/useSessionMonitor.js +61 -1
- package/dist/react/useSessionMonitor.js.map +1 -1
- package/dist/utils/jwt.cjs.map +1 -1
- package/dist/utils/jwt.js.map +1 -1
- package/package.json +1 -1
|
@@ -4,8 +4,10 @@ import React, { createContext, useState, useEffect, useRef } from "react";
|
|
|
4
4
|
// token.js
|
|
5
5
|
import { jwtDecode } from "jwt-decode";
|
|
6
6
|
var accessToken = null;
|
|
7
|
+
var idToken = null;
|
|
7
8
|
var listeners = /* @__PURE__ */ new Set();
|
|
8
9
|
var REFRESH_COOKIE = "account_refresh_token";
|
|
10
|
+
var ID_TOKEN_KEY = "auth_id_token";
|
|
9
11
|
var COOKIE_MAX_AGE = 7 * 24 * 60 * 60;
|
|
10
12
|
function secureAttribute() {
|
|
11
13
|
var _a;
|
|
@@ -74,12 +76,14 @@ function getToken() {
|
|
|
74
76
|
function clearToken() {
|
|
75
77
|
if (!accessToken) {
|
|
76
78
|
writeAccessToken(null);
|
|
79
|
+
clearIdToken();
|
|
77
80
|
clearRefreshToken();
|
|
78
81
|
return;
|
|
79
82
|
}
|
|
80
83
|
const previousToken = accessToken;
|
|
81
84
|
accessToken = null;
|
|
82
85
|
writeAccessToken(null);
|
|
86
|
+
clearIdToken();
|
|
83
87
|
clearRefreshToken();
|
|
84
88
|
listeners.forEach((listener) => {
|
|
85
89
|
try {
|
|
@@ -89,6 +93,22 @@ function clearToken() {
|
|
|
89
93
|
}
|
|
90
94
|
});
|
|
91
95
|
}
|
|
96
|
+
function getIdToken() {
|
|
97
|
+
if (idToken) return idToken;
|
|
98
|
+
try {
|
|
99
|
+
idToken = sessionStorage.getItem(ID_TOKEN_KEY) || null;
|
|
100
|
+
} catch {
|
|
101
|
+
idToken = null;
|
|
102
|
+
}
|
|
103
|
+
return idToken;
|
|
104
|
+
}
|
|
105
|
+
function clearIdToken() {
|
|
106
|
+
idToken = null;
|
|
107
|
+
try {
|
|
108
|
+
sessionStorage.removeItem(ID_TOKEN_KEY);
|
|
109
|
+
} catch {
|
|
110
|
+
}
|
|
111
|
+
}
|
|
92
112
|
var REFRESH_TOKEN_KEY = "auth_refresh_token";
|
|
93
113
|
var _persistRefreshToken = false;
|
|
94
114
|
function shouldUseLocalStorage() {
|
|
@@ -143,6 +163,15 @@ function clearRefreshToken() {
|
|
|
143
163
|
} catch (err) {
|
|
144
164
|
}
|
|
145
165
|
}
|
|
166
|
+
function addTokenListener(listener) {
|
|
167
|
+
if (typeof listener !== "function") {
|
|
168
|
+
throw new Error("Token listener must be a function");
|
|
169
|
+
}
|
|
170
|
+
listeners.add(listener);
|
|
171
|
+
return () => {
|
|
172
|
+
listeners.delete(listener);
|
|
173
|
+
};
|
|
174
|
+
}
|
|
146
175
|
if (typeof window !== "undefined" && window.addEventListener) {
|
|
147
176
|
window.addEventListener("storage", (event) => {
|
|
148
177
|
if (event.key !== "authToken") return;
|
|
@@ -256,6 +285,28 @@ function acquireLoginLock(clientKey, redirectUri, ttlMs = 5e3) {
|
|
|
256
285
|
}
|
|
257
286
|
return true;
|
|
258
287
|
}
|
|
288
|
+
async function reportClientDiagnostic(safe) {
|
|
289
|
+
if (typeof fetch !== "function" || typeof getToken !== "function") return;
|
|
290
|
+
if (safe.outcome === "SUCCESS" && !safe.event.startsWith("PROFILE_") && !safe.event.startsWith("SESSION_")) return;
|
|
291
|
+
const { authBaseUrl } = getConfig();
|
|
292
|
+
const token = getToken();
|
|
293
|
+
if (!authBaseUrl || !token) return;
|
|
294
|
+
try {
|
|
295
|
+
await fetch(`${authBaseUrl.replace(/\/+$/, "")}/client-telemetry`, {
|
|
296
|
+
method: "POST",
|
|
297
|
+
credentials: "include",
|
|
298
|
+
keepalive: true,
|
|
299
|
+
headers: {
|
|
300
|
+
"Content-Type": "application/json",
|
|
301
|
+
Authorization: `Bearer ${token}`,
|
|
302
|
+
"X-Correlation-ID": safe.correlationId,
|
|
303
|
+
"X-Request-ID": safe.correlationId
|
|
304
|
+
},
|
|
305
|
+
body: JSON.stringify(safe)
|
|
306
|
+
});
|
|
307
|
+
} catch {
|
|
308
|
+
}
|
|
309
|
+
}
|
|
259
310
|
async function emitAuthDiagnostic(event, outcome, reasonCode, details = {}) {
|
|
260
311
|
const context = getDiagnosticContext();
|
|
261
312
|
const safe = {
|
|
@@ -272,6 +323,7 @@ async function emitAuthDiagnostic(event, outcome, reasonCode, details = {}) {
|
|
|
272
323
|
};
|
|
273
324
|
const method = outcome === "FAILURE" ? "error" : outcome === "WARNING" ? "warn" : "info";
|
|
274
325
|
console[method]("[auth-client]", safe);
|
|
326
|
+
void reportClientDiagnostic(safe);
|
|
275
327
|
return safe;
|
|
276
328
|
}
|
|
277
329
|
|
|
@@ -334,6 +386,7 @@ async function logout(options = {}) {
|
|
|
334
386
|
const { clientKey, authBaseUrl, accountUiUrl } = getConfig();
|
|
335
387
|
const scope = options.scope === "client" ? "client" : "sso";
|
|
336
388
|
const token = getToken();
|
|
389
|
+
const idToken2 = getIdToken();
|
|
337
390
|
const refreshToken2 = getRefreshToken();
|
|
338
391
|
console.log("\u{1F6AA} Smart Logout initiated", {
|
|
339
392
|
mode: isRouterMode() ? "ROUTER" : "CLIENT",
|
|
@@ -342,6 +395,7 @@ async function logout(options = {}) {
|
|
|
342
395
|
});
|
|
343
396
|
emitAuthDiagnostic("LOGOUT_INITIATED", "PENDING", "NONE", { clientKey });
|
|
344
397
|
clearToken();
|
|
398
|
+
clearIdToken();
|
|
345
399
|
clearRefreshToken();
|
|
346
400
|
sessionStorage.removeItem("originalApp");
|
|
347
401
|
sessionStorage.removeItem("returnUrl");
|
|
@@ -354,7 +408,7 @@ async function logout(options = {}) {
|
|
|
354
408
|
"Authorization": token ? `Bearer ${token}` : "",
|
|
355
409
|
"Content-Type": "application/json"
|
|
356
410
|
},
|
|
357
|
-
body: JSON.stringify({ refreshToken: refreshToken2, scope })
|
|
411
|
+
body: JSON.stringify({ refreshToken: refreshToken2, idToken: idToken2, scope })
|
|
358
412
|
});
|
|
359
413
|
if (!response.ok) {
|
|
360
414
|
throw new Error(`Logout failed: ${response.status}`);
|
|
@@ -739,23 +793,72 @@ function stopSessionSecurity() {
|
|
|
739
793
|
|
|
740
794
|
// react/AuthProvider.jsx
|
|
741
795
|
var AuthContext = createContext();
|
|
742
|
-
function AuthProvider({ children, onSessionExpired }) {
|
|
796
|
+
function AuthProvider({ children, onSessionExpired, manageSessionSecurity = true }) {
|
|
743
797
|
const [token, setTokenState] = useState(getToken());
|
|
744
798
|
const [user, setUser] = useState(null);
|
|
745
799
|
const [loading, setLoading] = useState(!!token);
|
|
746
800
|
const [sessionValid, setSessionValid] = useState(true);
|
|
747
801
|
const sessionSecurityRef = useRef(null);
|
|
802
|
+
const onSessionExpiredRef = useRef(onSessionExpired);
|
|
803
|
+
const recoveryInFlightRef = useRef(null);
|
|
804
|
+
const profileRecoveryTokensRef = useRef(/* @__PURE__ */ new Set());
|
|
805
|
+
useEffect(() => {
|
|
806
|
+
onSessionExpiredRef.current = onSessionExpired;
|
|
807
|
+
}, [onSessionExpired]);
|
|
808
|
+
useEffect(() => {
|
|
809
|
+
const unsubscribe = addTokenListener((nextToken, previousToken) => {
|
|
810
|
+
if (nextToken === previousToken) return;
|
|
811
|
+
setTokenState(nextToken);
|
|
812
|
+
if (nextToken) {
|
|
813
|
+
setSessionValid(true);
|
|
814
|
+
setLoading(true);
|
|
815
|
+
} else {
|
|
816
|
+
setSessionValid(false);
|
|
817
|
+
setUser(null);
|
|
818
|
+
setLoading(false);
|
|
819
|
+
}
|
|
820
|
+
});
|
|
821
|
+
return unsubscribe;
|
|
822
|
+
}, []);
|
|
823
|
+
const invalidateLocalSession = () => {
|
|
824
|
+
clearToken();
|
|
825
|
+
setTokenState(null);
|
|
826
|
+
setUser(null);
|
|
827
|
+
setSessionValid(false);
|
|
828
|
+
setLoading(false);
|
|
829
|
+
};
|
|
748
830
|
const handleSessionInvalid = (reason) => {
|
|
749
831
|
console.log("\u{1F6A8} AuthProvider: Session invalidated -", reason);
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
832
|
+
if (recoveryInFlightRef.current) return recoveryInFlightRef.current;
|
|
833
|
+
const recover = async () => {
|
|
834
|
+
const callback = onSessionExpiredRef.current;
|
|
835
|
+
if (typeof callback === "function") {
|
|
836
|
+
try {
|
|
837
|
+
await callback(reason);
|
|
838
|
+
const recoveredToken = getToken();
|
|
839
|
+
if (recoveredToken) {
|
|
840
|
+
setTokenState(recoveredToken);
|
|
841
|
+
setSessionValid(true);
|
|
842
|
+
setLoading(true);
|
|
843
|
+
return true;
|
|
844
|
+
}
|
|
845
|
+
} catch (error) {
|
|
846
|
+
await emitAuthDiagnostic("SESSION_RECOVERY_FAILED", "FAILURE", reason, {
|
|
847
|
+
status: (error == null ? void 0 : error.status) || null,
|
|
848
|
+
errorCode: (error == null ? void 0 : error.code) || null
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
invalidateLocalSession();
|
|
853
|
+
return false;
|
|
854
|
+
};
|
|
855
|
+
recoveryInFlightRef.current = recover().finally(() => {
|
|
856
|
+
recoveryInFlightRef.current = null;
|
|
857
|
+
});
|
|
858
|
+
return recoveryInFlightRef.current;
|
|
756
859
|
};
|
|
757
860
|
useEffect(() => {
|
|
758
|
-
if (token && !sessionSecurityRef.current) {
|
|
861
|
+
if (manageSessionSecurity && token && !sessionSecurityRef.current) {
|
|
759
862
|
console.log("\u{1F510} AuthProvider: Starting session security");
|
|
760
863
|
const unsubscribe = onSessionInvalid(handleSessionInvalid);
|
|
761
864
|
sessionSecurityRef.current = startSessionSecurity(handleSessionInvalid);
|
|
@@ -767,11 +870,11 @@ function AuthProvider({ children, onSessionExpired }) {
|
|
|
767
870
|
}
|
|
768
871
|
};
|
|
769
872
|
}
|
|
770
|
-
if (!token && sessionSecurityRef.current) {
|
|
873
|
+
if ((!manageSessionSecurity || !token) && sessionSecurityRef.current) {
|
|
771
874
|
sessionSecurityRef.current.stopAll();
|
|
772
875
|
sessionSecurityRef.current = null;
|
|
773
876
|
}
|
|
774
|
-
}, [token]);
|
|
877
|
+
}, [manageSessionSecurity, token]);
|
|
775
878
|
useEffect(() => {
|
|
776
879
|
console.log("\u{1F50D} AuthProvider useEffect triggered:", {
|
|
777
880
|
hasToken: !!token,
|
|
@@ -792,26 +895,79 @@ function AuthProvider({ children, onSessionExpired }) {
|
|
|
792
895
|
authBaseUrl,
|
|
793
896
|
tokenPreview: token.slice(0, 50) + "..."
|
|
794
897
|
});
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
898
|
+
let cancelled = false;
|
|
899
|
+
const fetchProfile = async (accessToken2) => {
|
|
900
|
+
const response = await fetch(`${authBaseUrl}/account/profile`, {
|
|
901
|
+
headers: { Authorization: `Bearer ${accessToken2}` },
|
|
902
|
+
credentials: "include"
|
|
903
|
+
});
|
|
904
|
+
console.log("\u{1F4E5} Profile response status:", response.status);
|
|
905
|
+
if (!response.ok) {
|
|
906
|
+
const error = new Error(`Profile request failed: ${response.status}`);
|
|
907
|
+
error.status = response.status;
|
|
908
|
+
throw error;
|
|
909
|
+
}
|
|
910
|
+
return response.json();
|
|
911
|
+
};
|
|
912
|
+
const loadProfile = async () => {
|
|
913
|
+
try {
|
|
914
|
+
const responseBody = await fetchProfile(token);
|
|
915
|
+
if (cancelled) return;
|
|
916
|
+
const userData = (responseBody == null ? void 0 : responseBody.data) ?? responseBody;
|
|
917
|
+
console.log("\u2705 Profile fetched successfully:", userData.email);
|
|
918
|
+
setUser(userData);
|
|
919
|
+
setSessionValid(true);
|
|
920
|
+
setLoading(false);
|
|
921
|
+
} catch (error) {
|
|
922
|
+
if (cancelled) return;
|
|
923
|
+
const status = Number((error == null ? void 0 : error.status) || 0);
|
|
924
|
+
const isUnauthorized = status === 401;
|
|
925
|
+
const callback = onSessionExpiredRef.current;
|
|
926
|
+
await emitAuthDiagnostic(
|
|
927
|
+
"PROFILE_REQUEST_FAILED",
|
|
928
|
+
isUnauthorized ? "FAILURE" : "WARNING",
|
|
929
|
+
isUnauthorized ? "PROFILE_UNAUTHORIZED" : status ? `PROFILE_HTTP_${status}` : "PROFILE_NETWORK_ERROR",
|
|
930
|
+
{ status, errorName: error == null ? void 0 : error.name, errorCode: error == null ? void 0 : error.code }
|
|
931
|
+
);
|
|
932
|
+
if (!isUnauthorized) {
|
|
933
|
+
console.warn("\u26A0\uFE0F Profile unavailable; retaining the current auth session", error);
|
|
934
|
+
setLoading(false);
|
|
935
|
+
return;
|
|
936
|
+
}
|
|
937
|
+
if (typeof callback === "function" && !profileRecoveryTokensRef.current.has(token)) {
|
|
938
|
+
profileRecoveryTokensRef.current.add(token);
|
|
939
|
+
try {
|
|
940
|
+
await callback("profile_unauthorized");
|
|
941
|
+
const recoveredToken = getToken();
|
|
942
|
+
if (recoveredToken && recoveredToken !== token) {
|
|
943
|
+
setTokenState(recoveredToken);
|
|
944
|
+
setSessionValid(true);
|
|
945
|
+
setLoading(true);
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
948
|
+
if (recoveredToken) {
|
|
949
|
+
const responseBody = await fetchProfile(recoveredToken);
|
|
950
|
+
if (cancelled) return;
|
|
951
|
+
const userData = (responseBody == null ? void 0 : responseBody.data) ?? responseBody;
|
|
952
|
+
setUser(userData);
|
|
953
|
+
setSessionValid(true);
|
|
954
|
+
setLoading(false);
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
} catch (recoveryError) {
|
|
958
|
+
await emitAuthDiagnostic("PROFILE_RECOVERY_FAILED", "FAILURE", "PROFILE_REFRESH_FAILED", {
|
|
959
|
+
status: (recoveryError == null ? void 0 : recoveryError.status) || null,
|
|
960
|
+
errorCode: (recoveryError == null ? void 0 : recoveryError.code) || null
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
invalidateLocalSession();
|
|
965
|
+
}
|
|
966
|
+
};
|
|
967
|
+
void loadProfile();
|
|
968
|
+
return () => {
|
|
969
|
+
cancelled = true;
|
|
970
|
+
};
|
|
815
971
|
}, [token]);
|
|
816
972
|
const login2 = (clientKey, redirectUri, state) => {
|
|
817
973
|
login(clientKey, redirectUri, state);
|
|
@@ -840,6 +996,7 @@ function AuthProvider({ children, onSessionExpired }) {
|
|
|
840
996
|
setToken(newToken);
|
|
841
997
|
setTokenState(newToken);
|
|
842
998
|
setSessionValid(true);
|
|
999
|
+
if (newToken) setLoading(true);
|
|
843
1000
|
},
|
|
844
1001
|
clearToken: () => {
|
|
845
1002
|
stopSessionSecurity();
|