@spidy092/auth-client 3.1.3 → 3.1.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/index.js CHANGED
@@ -340,6 +340,28 @@ function clearLoginLock() {
340
340
  } catch {
341
341
  }
342
342
  }
343
+ async function reportClientDiagnostic(safe) {
344
+ if (typeof fetch !== "function" || typeof getToken !== "function") return;
345
+ if (safe.outcome === "SUCCESS" && !safe.event.startsWith("PROFILE_") && !safe.event.startsWith("SESSION_")) return;
346
+ const { authBaseUrl } = getConfig();
347
+ const token = getToken();
348
+ if (!authBaseUrl || !token) return;
349
+ try {
350
+ await fetch(`${authBaseUrl.replace(/\/+$/, "")}/client-telemetry`, {
351
+ method: "POST",
352
+ credentials: "include",
353
+ keepalive: true,
354
+ headers: {
355
+ "Content-Type": "application/json",
356
+ Authorization: `Bearer ${token}`,
357
+ "X-Correlation-ID": safe.correlationId,
358
+ "X-Request-ID": safe.correlationId
359
+ },
360
+ body: JSON.stringify(safe)
361
+ });
362
+ } catch {
363
+ }
364
+ }
343
365
  async function emitAuthDiagnostic(event, outcome, reasonCode, details = {}) {
344
366
  const context = getDiagnosticContext();
345
367
  const safe = {
@@ -356,6 +378,7 @@ async function emitAuthDiagnostic(event, outcome, reasonCode, details = {}) {
356
378
  };
357
379
  const method = outcome === "FAILURE" ? "error" : outcome === "WARNING" ? "warn" : "info";
358
380
  console[method]("[auth-client]", safe);
381
+ void reportClientDiagnostic(safe);
359
382
  return safe;
360
383
  }
361
384
 
@@ -1185,23 +1208,72 @@ var preferences = {
1185
1208
  // react/AuthProvider.jsx
1186
1209
  import React, { createContext, useState, useEffect, useRef } from "react";
1187
1210
  var AuthContext = createContext();
1188
- function AuthProvider({ children, onSessionExpired }) {
1211
+ function AuthProvider({ children, onSessionExpired, manageSessionSecurity = true }) {
1189
1212
  const [token, setTokenState] = useState(getToken());
1190
1213
  const [user, setUser] = useState(null);
1191
1214
  const [loading, setLoading] = useState(!!token);
1192
1215
  const [sessionValid, setSessionValid] = useState(true);
1193
1216
  const sessionSecurityRef = useRef(null);
1217
+ const onSessionExpiredRef = useRef(onSessionExpired);
1218
+ const recoveryInFlightRef = useRef(null);
1219
+ const profileRecoveryTokensRef = useRef(/* @__PURE__ */ new Set());
1220
+ useEffect(() => {
1221
+ onSessionExpiredRef.current = onSessionExpired;
1222
+ }, [onSessionExpired]);
1223
+ useEffect(() => {
1224
+ const unsubscribe = addTokenListener((nextToken, previousToken) => {
1225
+ if (nextToken === previousToken) return;
1226
+ setTokenState(nextToken);
1227
+ if (nextToken) {
1228
+ setSessionValid(true);
1229
+ setLoading(true);
1230
+ } else {
1231
+ setSessionValid(false);
1232
+ setUser(null);
1233
+ setLoading(false);
1234
+ }
1235
+ });
1236
+ return unsubscribe;
1237
+ }, []);
1238
+ const invalidateLocalSession = () => {
1239
+ clearToken();
1240
+ setTokenState(null);
1241
+ setUser(null);
1242
+ setSessionValid(false);
1243
+ setLoading(false);
1244
+ };
1194
1245
  const handleSessionInvalid = (reason) => {
1195
1246
  console.log("\u{1F6A8} AuthProvider: Session invalidated -", reason);
1196
- setSessionValid(false);
1197
- setUser(null);
1198
- setTokenState(null);
1199
- if (onSessionExpired && typeof onSessionExpired === "function") {
1200
- onSessionExpired(reason);
1201
- }
1247
+ if (recoveryInFlightRef.current) return recoveryInFlightRef.current;
1248
+ const recover = async () => {
1249
+ const callback = onSessionExpiredRef.current;
1250
+ if (typeof callback === "function") {
1251
+ try {
1252
+ await callback(reason);
1253
+ const recoveredToken = getToken();
1254
+ if (recoveredToken) {
1255
+ setTokenState(recoveredToken);
1256
+ setSessionValid(true);
1257
+ setLoading(true);
1258
+ return true;
1259
+ }
1260
+ } catch (error) {
1261
+ await emitAuthDiagnostic("SESSION_RECOVERY_FAILED", "FAILURE", reason, {
1262
+ status: (error == null ? void 0 : error.status) || null,
1263
+ errorCode: (error == null ? void 0 : error.code) || null
1264
+ });
1265
+ }
1266
+ }
1267
+ invalidateLocalSession();
1268
+ return false;
1269
+ };
1270
+ recoveryInFlightRef.current = recover().finally(() => {
1271
+ recoveryInFlightRef.current = null;
1272
+ });
1273
+ return recoveryInFlightRef.current;
1202
1274
  };
1203
1275
  useEffect(() => {
1204
- if (token && !sessionSecurityRef.current) {
1276
+ if (manageSessionSecurity && token && !sessionSecurityRef.current) {
1205
1277
  console.log("\u{1F510} AuthProvider: Starting session security");
1206
1278
  const unsubscribe = onSessionInvalid(handleSessionInvalid);
1207
1279
  sessionSecurityRef.current = startSessionSecurity(handleSessionInvalid);
@@ -1213,11 +1285,11 @@ function AuthProvider({ children, onSessionExpired }) {
1213
1285
  }
1214
1286
  };
1215
1287
  }
1216
- if (!token && sessionSecurityRef.current) {
1288
+ if ((!manageSessionSecurity || !token) && sessionSecurityRef.current) {
1217
1289
  sessionSecurityRef.current.stopAll();
1218
1290
  sessionSecurityRef.current = null;
1219
1291
  }
1220
- }, [token]);
1292
+ }, [manageSessionSecurity, token]);
1221
1293
  useEffect(() => {
1222
1294
  console.log("\u{1F50D} AuthProvider useEffect triggered:", {
1223
1295
  hasToken: !!token,
@@ -1238,26 +1310,79 @@ function AuthProvider({ children, onSessionExpired }) {
1238
1310
  authBaseUrl,
1239
1311
  tokenPreview: token.slice(0, 50) + "..."
1240
1312
  });
1241
- fetch(`${authBaseUrl}/account/profile`, {
1242
- headers: { Authorization: `Bearer ${token}` },
1243
- credentials: "include"
1244
- }).then((res) => {
1245
- console.log("\u{1F4E5} Profile response status:", res.status);
1246
- if (!res.ok) throw new Error("Failed to fetch user");
1247
- return res.json();
1248
- }).then((responseBody) => {
1249
- const userData = (responseBody == null ? void 0 : responseBody.data) ?? responseBody;
1250
- console.log("\u2705 Profile fetched successfully:", userData.email);
1251
- setUser(userData);
1252
- setSessionValid(true);
1253
- setLoading(false);
1254
- }).catch((err) => {
1255
- console.error("\u274C Fetch user error:", err);
1256
- clearToken();
1257
- setTokenState(null);
1258
- setUser(null);
1259
- setLoading(false);
1260
- });
1313
+ let cancelled = false;
1314
+ const fetchProfile = async (accessToken2) => {
1315
+ const response = await fetch(`${authBaseUrl}/account/profile`, {
1316
+ headers: { Authorization: `Bearer ${accessToken2}` },
1317
+ credentials: "include"
1318
+ });
1319
+ console.log("\u{1F4E5} Profile response status:", response.status);
1320
+ if (!response.ok) {
1321
+ const error = new Error(`Profile request failed: ${response.status}`);
1322
+ error.status = response.status;
1323
+ throw error;
1324
+ }
1325
+ return response.json();
1326
+ };
1327
+ const loadProfile = async () => {
1328
+ try {
1329
+ const responseBody = await fetchProfile(token);
1330
+ if (cancelled) return;
1331
+ const userData = (responseBody == null ? void 0 : responseBody.data) ?? responseBody;
1332
+ console.log("\u2705 Profile fetched successfully:", userData.email);
1333
+ setUser(userData);
1334
+ setSessionValid(true);
1335
+ setLoading(false);
1336
+ } catch (error) {
1337
+ if (cancelled) return;
1338
+ const status = Number((error == null ? void 0 : error.status) || 0);
1339
+ const isUnauthorized = status === 401;
1340
+ const callback = onSessionExpiredRef.current;
1341
+ await emitAuthDiagnostic(
1342
+ "PROFILE_REQUEST_FAILED",
1343
+ isUnauthorized ? "FAILURE" : "WARNING",
1344
+ isUnauthorized ? "PROFILE_UNAUTHORIZED" : status ? `PROFILE_HTTP_${status}` : "PROFILE_NETWORK_ERROR",
1345
+ { status, errorName: error == null ? void 0 : error.name, errorCode: error == null ? void 0 : error.code }
1346
+ );
1347
+ if (!isUnauthorized) {
1348
+ console.warn("\u26A0\uFE0F Profile unavailable; retaining the current auth session", error);
1349
+ setLoading(false);
1350
+ return;
1351
+ }
1352
+ if (typeof callback === "function" && !profileRecoveryTokensRef.current.has(token)) {
1353
+ profileRecoveryTokensRef.current.add(token);
1354
+ try {
1355
+ await callback("profile_unauthorized");
1356
+ const recoveredToken = getToken();
1357
+ if (recoveredToken && recoveredToken !== token) {
1358
+ setTokenState(recoveredToken);
1359
+ setSessionValid(true);
1360
+ setLoading(true);
1361
+ return;
1362
+ }
1363
+ if (recoveredToken) {
1364
+ const responseBody = await fetchProfile(recoveredToken);
1365
+ if (cancelled) return;
1366
+ const userData = (responseBody == null ? void 0 : responseBody.data) ?? responseBody;
1367
+ setUser(userData);
1368
+ setSessionValid(true);
1369
+ setLoading(false);
1370
+ return;
1371
+ }
1372
+ } catch (recoveryError) {
1373
+ await emitAuthDiagnostic("PROFILE_RECOVERY_FAILED", "FAILURE", "PROFILE_REFRESH_FAILED", {
1374
+ status: (recoveryError == null ? void 0 : recoveryError.status) || null,
1375
+ errorCode: (recoveryError == null ? void 0 : recoveryError.code) || null
1376
+ });
1377
+ }
1378
+ }
1379
+ invalidateLocalSession();
1380
+ }
1381
+ };
1382
+ void loadProfile();
1383
+ return () => {
1384
+ cancelled = true;
1385
+ };
1261
1386
  }, [token]);
1262
1387
  const login2 = (clientKey, redirectUri, state) => {
1263
1388
  login(clientKey, redirectUri, state);
@@ -1286,6 +1411,7 @@ function AuthProvider({ children, onSessionExpired }) {
1286
1411
  setToken(newToken);
1287
1412
  setTokenState(newToken);
1288
1413
  setSessionValid(true);
1414
+ if (newToken) setLoading(true);
1289
1415
  },
1290
1416
  clearToken: () => {
1291
1417
  stopSessionSecurity();