@spidy092/auth-client 2.1.8 → 3.0.1
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/Readme.md +9 -0
- package/dist/api.cjs +337 -0
- package/dist/api.cjs.map +1 -0
- package/dist/api.js +307 -0
- package/dist/api.js.map +1 -0
- package/dist/index.cjs +1065 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js +1026 -0
- package/dist/index.js.map +1 -0
- package/dist/react/AuthProvider.cjs +700 -0
- package/dist/react/AuthProvider.cjs.map +1 -0
- package/dist/react/AuthProvider.js +665 -0
- package/dist/react/AuthProvider.js.map +1 -0
- package/dist/react/useAuth.cjs +92 -0
- package/dist/react/useAuth.cjs.map +1 -0
- package/dist/react/useAuth.js +58 -0
- package/dist/react/useAuth.js.map +1 -0
- package/dist/react/useSessionMonitor.cjs +944 -0
- package/dist/react/useSessionMonitor.cjs.map +1 -0
- package/dist/react/useSessionMonitor.js +910 -0
- package/dist/react/useSessionMonitor.js.map +1 -0
- package/dist/utils/jwt.cjs +72 -0
- package/dist/utils/jwt.cjs.map +1 -0
- package/dist/utils/jwt.js +46 -0
- package/dist/utils/jwt.js.map +1 -0
- package/package.json +34 -13
- package/api.js +0 -95
- package/config.js +0 -63
- package/core.js +0 -567
- package/index.js +0 -106
- package/react/AuthProvider.jsx +0 -150
- package/react/useAuth.js +0 -10
- package/react/useSessionMonitor.js +0 -121
- package/token.js +0 -455
- package/utils/jwt.js +0 -27
|
@@ -0,0 +1,700 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
|
|
29
|
+
// react/AuthProvider.jsx
|
|
30
|
+
var AuthProvider_exports = {};
|
|
31
|
+
__export(AuthProvider_exports, {
|
|
32
|
+
AuthContext: () => AuthContext,
|
|
33
|
+
AuthProvider: () => AuthProvider
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(AuthProvider_exports);
|
|
36
|
+
var import_react = __toESM(require("react"), 1);
|
|
37
|
+
|
|
38
|
+
// token.js
|
|
39
|
+
var import_jwt_decode = require("jwt-decode");
|
|
40
|
+
var accessToken = null;
|
|
41
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
42
|
+
var REFRESH_COOKIE = "account_refresh_token";
|
|
43
|
+
var COOKIE_MAX_AGE = 7 * 24 * 60 * 60;
|
|
44
|
+
function secureAttribute() {
|
|
45
|
+
var _a;
|
|
46
|
+
try {
|
|
47
|
+
return typeof window !== "undefined" && ((_a = window.location) == null ? void 0 : _a.protocol) === "https:" ? "; Secure" : "";
|
|
48
|
+
} catch (err) {
|
|
49
|
+
return "";
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function writeAccessToken(token) {
|
|
53
|
+
if (!token) {
|
|
54
|
+
try {
|
|
55
|
+
localStorage.removeItem("authToken");
|
|
56
|
+
} catch (err) {
|
|
57
|
+
console.warn("Could not clear token from localStorage:", err);
|
|
58
|
+
}
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
localStorage.setItem("authToken", token);
|
|
63
|
+
} catch (err) {
|
|
64
|
+
console.warn("Could not persist token to localStorage:", err);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function readAccessToken() {
|
|
68
|
+
try {
|
|
69
|
+
return localStorage.getItem("authToken");
|
|
70
|
+
} catch (err) {
|
|
71
|
+
console.warn("Could not read token from localStorage:", err);
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function decode(token) {
|
|
76
|
+
try {
|
|
77
|
+
return (0, import_jwt_decode.jwtDecode)(token);
|
|
78
|
+
} catch (err) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function getTimeUntilExpiry(token) {
|
|
83
|
+
if (!token) return -1;
|
|
84
|
+
const decoded = decode(token);
|
|
85
|
+
if (!(decoded == null ? void 0 : decoded.exp)) return -1;
|
|
86
|
+
const now = Date.now() / 1e3;
|
|
87
|
+
return Math.floor(decoded.exp - now);
|
|
88
|
+
}
|
|
89
|
+
function setToken(token) {
|
|
90
|
+
const previousToken = accessToken;
|
|
91
|
+
accessToken = token || null;
|
|
92
|
+
writeAccessToken(accessToken);
|
|
93
|
+
if (previousToken !== accessToken) {
|
|
94
|
+
listeners.forEach((listener) => {
|
|
95
|
+
try {
|
|
96
|
+
listener(accessToken, previousToken);
|
|
97
|
+
} catch (err) {
|
|
98
|
+
console.warn("Token listener error:", err);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function getToken() {
|
|
104
|
+
if (accessToken) return accessToken;
|
|
105
|
+
accessToken = readAccessToken();
|
|
106
|
+
return accessToken;
|
|
107
|
+
}
|
|
108
|
+
function clearToken() {
|
|
109
|
+
if (!accessToken) {
|
|
110
|
+
writeAccessToken(null);
|
|
111
|
+
clearRefreshToken();
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const previousToken = accessToken;
|
|
115
|
+
accessToken = null;
|
|
116
|
+
writeAccessToken(null);
|
|
117
|
+
clearRefreshToken();
|
|
118
|
+
listeners.forEach((listener) => {
|
|
119
|
+
try {
|
|
120
|
+
listener(null, previousToken);
|
|
121
|
+
} catch (err) {
|
|
122
|
+
console.warn("Token listener error:", err);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
var REFRESH_TOKEN_KEY = "auth_refresh_token";
|
|
127
|
+
var _persistRefreshToken = false;
|
|
128
|
+
function shouldUseLocalStorage() {
|
|
129
|
+
var _a;
|
|
130
|
+
if (_persistRefreshToken) return true;
|
|
131
|
+
try {
|
|
132
|
+
return typeof window !== "undefined" && ((_a = window.location) == null ? void 0 : _a.protocol) === "http:";
|
|
133
|
+
} catch (err) {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function setRefreshToken(token) {
|
|
138
|
+
if (!token) {
|
|
139
|
+
clearRefreshToken();
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (shouldUseLocalStorage()) {
|
|
143
|
+
try {
|
|
144
|
+
localStorage.setItem(REFRESH_TOKEN_KEY, token);
|
|
145
|
+
console.log(`\u{1F4E6} Refresh token stored in localStorage (${_persistRefreshToken ? "persistence enabled" : "HTTP dev mode"})`);
|
|
146
|
+
} catch (err) {
|
|
147
|
+
console.warn("Could not store refresh token:", err);
|
|
148
|
+
}
|
|
149
|
+
} else {
|
|
150
|
+
console.log("\u{1F512} Refresh token managed by server httpOnly cookie (production mode)");
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
function getRefreshToken() {
|
|
154
|
+
if (shouldUseLocalStorage()) {
|
|
155
|
+
try {
|
|
156
|
+
const token = localStorage.getItem(REFRESH_TOKEN_KEY);
|
|
157
|
+
return token;
|
|
158
|
+
} catch (err) {
|
|
159
|
+
console.warn("Could not read refresh token:", err);
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
function clearRefreshToken() {
|
|
166
|
+
try {
|
|
167
|
+
localStorage.removeItem(REFRESH_TOKEN_KEY);
|
|
168
|
+
} catch (err) {
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
document.cookie = `${REFRESH_COOKIE}=; Path=/; SameSite=Strict${secureAttribute()}; Expires=Thu, 01 Jan 1970 00:00:00 GMT`;
|
|
172
|
+
} catch (err) {
|
|
173
|
+
console.warn("Could not clear refresh token cookie:", err);
|
|
174
|
+
}
|
|
175
|
+
try {
|
|
176
|
+
sessionStorage.removeItem(REFRESH_COOKIE);
|
|
177
|
+
} catch (err) {
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// config.js
|
|
182
|
+
var config = {
|
|
183
|
+
clientKey: null,
|
|
184
|
+
authBaseUrl: null,
|
|
185
|
+
redirectUri: null,
|
|
186
|
+
accountUiUrl: null,
|
|
187
|
+
isRouter: false,
|
|
188
|
+
// ✅ Add router flag
|
|
189
|
+
// ========== SESSION SECURITY SETTINGS ==========
|
|
190
|
+
// Buffer time (in seconds) before token expiry to trigger proactive refresh
|
|
191
|
+
// With 5-minute access tokens, refreshing 60s before expiry ensures seamless UX
|
|
192
|
+
tokenRefreshBuffer: 60,
|
|
193
|
+
// Interval (in milliseconds) for periodic session validation
|
|
194
|
+
// Validates that the session still exists in Keycloak (not deleted by admin)
|
|
195
|
+
// Default: 15 minutes (900000ms) - Increased from 2m to avoid frequent checks
|
|
196
|
+
sessionValidationInterval: 15 * 60 * 1e3,
|
|
197
|
+
// Enable/disable periodic session validation
|
|
198
|
+
// When enabled, the client will ping the server to verify session is still active
|
|
199
|
+
enableSessionValidation: true,
|
|
200
|
+
// Enable/disable proactive token refresh
|
|
201
|
+
// When enabled, tokens are refreshed before they expire (using tokenRefreshBuffer)
|
|
202
|
+
enableProactiveRefresh: true,
|
|
203
|
+
// Validate session when browser tab becomes visible again
|
|
204
|
+
// Catches session deletions that happened while the tab was inactive
|
|
205
|
+
validateOnVisibility: true,
|
|
206
|
+
// ========== REFRESH TOKEN PERSISTENCE ==========
|
|
207
|
+
// When true, stores refresh token in localStorage even on HTTPS
|
|
208
|
+
// Required for local dev with mkcert/self-signed certs where httpOnly cookies
|
|
209
|
+
// may not work reliably across origins
|
|
210
|
+
// ⚠️ In true production, set to false and rely on httpOnly cookies
|
|
211
|
+
persistRefreshToken: false
|
|
212
|
+
};
|
|
213
|
+
function getConfig() {
|
|
214
|
+
return { ...config };
|
|
215
|
+
}
|
|
216
|
+
function isRouterMode() {
|
|
217
|
+
return config.isRouter;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// core.js
|
|
221
|
+
var callbackProcessed = false;
|
|
222
|
+
function login(clientKeyArg, redirectUriArg) {
|
|
223
|
+
resetCallbackState();
|
|
224
|
+
const {
|
|
225
|
+
clientKey: defaultClientKey,
|
|
226
|
+
authBaseUrl,
|
|
227
|
+
redirectUri: defaultRedirectUri,
|
|
228
|
+
accountUiUrl
|
|
229
|
+
} = getConfig();
|
|
230
|
+
const clientKey = clientKeyArg || defaultClientKey;
|
|
231
|
+
const redirectUri = redirectUriArg || defaultRedirectUri;
|
|
232
|
+
console.log("\u{1F504} Smart Login initiated:", {
|
|
233
|
+
mode: isRouterMode() ? "ROUTER" : "CLIENT",
|
|
234
|
+
clientKey,
|
|
235
|
+
redirectUri
|
|
236
|
+
});
|
|
237
|
+
if (!clientKey || !redirectUri) {
|
|
238
|
+
throw new Error("Missing clientKey or redirectUri");
|
|
239
|
+
}
|
|
240
|
+
sessionStorage.setItem("originalApp", clientKey);
|
|
241
|
+
sessionStorage.setItem("returnUrl", redirectUri);
|
|
242
|
+
if (isRouterMode()) {
|
|
243
|
+
return routerLogin(clientKey, redirectUri);
|
|
244
|
+
} else {
|
|
245
|
+
return clientLogin(clientKey, redirectUri);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
function routerLogin(clientKey, redirectUri) {
|
|
249
|
+
const { authBaseUrl } = getConfig();
|
|
250
|
+
const params = new URLSearchParams();
|
|
251
|
+
if (redirectUri) {
|
|
252
|
+
params.append("redirect_uri", redirectUri);
|
|
253
|
+
}
|
|
254
|
+
const query = params.toString();
|
|
255
|
+
const backendLoginUrl = `${authBaseUrl}/login/${clientKey}${query ? `?${query}` : ""}`;
|
|
256
|
+
console.log("\u{1F3ED} Router Login: Direct backend authentication", {
|
|
257
|
+
clientKey,
|
|
258
|
+
redirectUri,
|
|
259
|
+
backendUrl: backendLoginUrl
|
|
260
|
+
});
|
|
261
|
+
window.location.href = backendLoginUrl;
|
|
262
|
+
}
|
|
263
|
+
function clientLogin(clientKey, redirectUri) {
|
|
264
|
+
const { accountUiUrl } = getConfig();
|
|
265
|
+
const params = new URLSearchParams({
|
|
266
|
+
client: clientKey
|
|
267
|
+
});
|
|
268
|
+
if (redirectUri) {
|
|
269
|
+
params.append("redirect_uri", redirectUri);
|
|
270
|
+
}
|
|
271
|
+
const centralizedLoginUrl = `${accountUiUrl}/login?${params.toString()}`;
|
|
272
|
+
console.log("\u{1F504} Client Login: Redirecting to centralized login", {
|
|
273
|
+
clientKey,
|
|
274
|
+
redirectUri,
|
|
275
|
+
centralizedUrl: centralizedLoginUrl
|
|
276
|
+
});
|
|
277
|
+
window.location.href = centralizedLoginUrl;
|
|
278
|
+
}
|
|
279
|
+
function logout() {
|
|
280
|
+
resetCallbackState();
|
|
281
|
+
const { clientKey, authBaseUrl, accountUiUrl } = getConfig();
|
|
282
|
+
const token = getToken();
|
|
283
|
+
console.log("\u{1F6AA} Smart Logout initiated");
|
|
284
|
+
clearToken();
|
|
285
|
+
clearRefreshToken();
|
|
286
|
+
sessionStorage.removeItem("originalApp");
|
|
287
|
+
sessionStorage.removeItem("returnUrl");
|
|
288
|
+
if (isRouterMode()) {
|
|
289
|
+
return routerLogout(clientKey, authBaseUrl, accountUiUrl, token);
|
|
290
|
+
} else {
|
|
291
|
+
return clientLogout(clientKey, accountUiUrl);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
async function routerLogout(clientKey, authBaseUrl, accountUiUrl, token) {
|
|
295
|
+
console.log("\u{1F3ED} Router Logout");
|
|
296
|
+
const refreshToken2 = getRefreshToken();
|
|
297
|
+
try {
|
|
298
|
+
const response = await fetch(`${authBaseUrl}/logout/${clientKey}`, {
|
|
299
|
+
method: "POST",
|
|
300
|
+
credentials: "include",
|
|
301
|
+
headers: {
|
|
302
|
+
"Authorization": token ? `Bearer ${token}` : "",
|
|
303
|
+
"Content-Type": "application/json"
|
|
304
|
+
},
|
|
305
|
+
body: JSON.stringify({
|
|
306
|
+
refreshToken: refreshToken2
|
|
307
|
+
})
|
|
308
|
+
});
|
|
309
|
+
const data = await response.json();
|
|
310
|
+
console.log("\u2705 Logout response:", data);
|
|
311
|
+
clearRefreshToken();
|
|
312
|
+
clearToken();
|
|
313
|
+
console.log("\u{1F504} Redirecting to login (skipping Keycloak confirmation)");
|
|
314
|
+
window.location.href = "/login";
|
|
315
|
+
} catch (error) {
|
|
316
|
+
console.warn("\u26A0\uFE0F Logout failed:", error);
|
|
317
|
+
clearRefreshToken();
|
|
318
|
+
clearToken();
|
|
319
|
+
window.location.href = "/login";
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
function clientLogout(clientKey, accountUiUrl) {
|
|
323
|
+
console.log("\u{1F504} Client Logout");
|
|
324
|
+
const logoutUrl = `${accountUiUrl}/login?client=${clientKey}&logout=true`;
|
|
325
|
+
window.location.href = logoutUrl;
|
|
326
|
+
}
|
|
327
|
+
function resetCallbackState() {
|
|
328
|
+
callbackProcessed = false;
|
|
329
|
+
}
|
|
330
|
+
var refreshInProgress = false;
|
|
331
|
+
var refreshPromise = null;
|
|
332
|
+
async function refreshToken() {
|
|
333
|
+
const { clientKey, authBaseUrl } = getConfig();
|
|
334
|
+
if (refreshInProgress && refreshPromise) {
|
|
335
|
+
console.log("\u{1F504} Token refresh already in progress, waiting...");
|
|
336
|
+
return refreshPromise;
|
|
337
|
+
}
|
|
338
|
+
refreshInProgress = true;
|
|
339
|
+
refreshPromise = (async () => {
|
|
340
|
+
try {
|
|
341
|
+
const storedRefreshToken = getRefreshToken();
|
|
342
|
+
console.log("\u{1F504} Refreshing token:", {
|
|
343
|
+
clientKey,
|
|
344
|
+
mode: isRouterMode() ? "ROUTER" : "CLIENT",
|
|
345
|
+
hasStoredRefreshToken: !!storedRefreshToken
|
|
346
|
+
});
|
|
347
|
+
const requestOptions = {
|
|
348
|
+
method: "POST",
|
|
349
|
+
credentials: "include",
|
|
350
|
+
// ✅ Include httpOnly cookies (for HTTPS)
|
|
351
|
+
headers: {
|
|
352
|
+
"Content-Type": "application/json"
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
if (storedRefreshToken) {
|
|
356
|
+
requestOptions.headers["X-Refresh-Token"] = storedRefreshToken;
|
|
357
|
+
requestOptions.body = JSON.stringify({ refreshToken: storedRefreshToken });
|
|
358
|
+
}
|
|
359
|
+
const response = await fetch(`${authBaseUrl}/refresh/${clientKey}`, requestOptions);
|
|
360
|
+
if (!response.ok) {
|
|
361
|
+
const errorText = await response.text();
|
|
362
|
+
console.error("\u274C Token refresh failed:", response.status, errorText);
|
|
363
|
+
throw new Error(`Refresh failed: ${response.status}`);
|
|
364
|
+
}
|
|
365
|
+
const data = await response.json();
|
|
366
|
+
const { access_token, refresh_token: new_refresh_token } = data;
|
|
367
|
+
if (!access_token) {
|
|
368
|
+
throw new Error("No access token in refresh response");
|
|
369
|
+
}
|
|
370
|
+
setToken(access_token);
|
|
371
|
+
if (new_refresh_token) {
|
|
372
|
+
setRefreshToken(new_refresh_token);
|
|
373
|
+
console.log("\u{1F504} New refresh token stored from rotation");
|
|
374
|
+
}
|
|
375
|
+
console.log("\u2705 Token refresh successful, listeners notified");
|
|
376
|
+
return access_token;
|
|
377
|
+
} catch (err) {
|
|
378
|
+
console.error("\u274C Token refresh error:", err);
|
|
379
|
+
clearToken();
|
|
380
|
+
clearRefreshToken();
|
|
381
|
+
throw err;
|
|
382
|
+
} finally {
|
|
383
|
+
refreshInProgress = false;
|
|
384
|
+
refreshPromise = null;
|
|
385
|
+
}
|
|
386
|
+
})();
|
|
387
|
+
return refreshPromise;
|
|
388
|
+
}
|
|
389
|
+
async function validateCurrentSession() {
|
|
390
|
+
try {
|
|
391
|
+
const { authBaseUrl } = getConfig();
|
|
392
|
+
const token = getToken();
|
|
393
|
+
if (!token || !authBaseUrl) {
|
|
394
|
+
return false;
|
|
395
|
+
}
|
|
396
|
+
const response = await fetch(`${authBaseUrl}/account/validate-session`, {
|
|
397
|
+
method: "GET",
|
|
398
|
+
headers: {
|
|
399
|
+
"Authorization": `Bearer ${token}`,
|
|
400
|
+
"Content-Type": "application/json"
|
|
401
|
+
},
|
|
402
|
+
credentials: "include"
|
|
403
|
+
});
|
|
404
|
+
if (!response.ok) {
|
|
405
|
+
if (response.status === 401) {
|
|
406
|
+
return false;
|
|
407
|
+
}
|
|
408
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
409
|
+
}
|
|
410
|
+
const data = await response.json();
|
|
411
|
+
return data.valid === true;
|
|
412
|
+
} catch (error) {
|
|
413
|
+
console.warn("Session validation failed:", error.message);
|
|
414
|
+
if (error.message.includes("401")) {
|
|
415
|
+
return false;
|
|
416
|
+
}
|
|
417
|
+
throw error;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
var proactiveRefreshTimer = null;
|
|
421
|
+
var sessionValidationTimer = null;
|
|
422
|
+
var visibilityHandler = null;
|
|
423
|
+
var sessionInvalidCallbacks = /* @__PURE__ */ new Set();
|
|
424
|
+
function onSessionInvalid(callback) {
|
|
425
|
+
if (typeof callback === "function") {
|
|
426
|
+
sessionInvalidCallbacks.add(callback);
|
|
427
|
+
}
|
|
428
|
+
return () => sessionInvalidCallbacks.delete(callback);
|
|
429
|
+
}
|
|
430
|
+
function notifySessionInvalid(reason = "session_deleted") {
|
|
431
|
+
console.log("\u{1F6A8} Session invalidated:", reason);
|
|
432
|
+
sessionInvalidCallbacks.forEach((callback) => {
|
|
433
|
+
try {
|
|
434
|
+
callback(reason);
|
|
435
|
+
} catch (err) {
|
|
436
|
+
console.error("Session invalid callback error:", err);
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
function startProactiveRefresh() {
|
|
441
|
+
const { enableProactiveRefresh, tokenRefreshBuffer } = getConfig();
|
|
442
|
+
if (!enableProactiveRefresh) {
|
|
443
|
+
console.log("\u23F8\uFE0F Proactive refresh disabled by config");
|
|
444
|
+
return null;
|
|
445
|
+
}
|
|
446
|
+
stopProactiveRefresh();
|
|
447
|
+
const token = getToken();
|
|
448
|
+
if (!token) {
|
|
449
|
+
console.log("\u23F8\uFE0F No token, skipping proactive refresh setup");
|
|
450
|
+
return null;
|
|
451
|
+
}
|
|
452
|
+
const timeUntilExpiry = getTimeUntilExpiry(token);
|
|
453
|
+
if (timeUntilExpiry <= 0) {
|
|
454
|
+
console.log("\u26A0\uFE0F Token already expired, attempting immediate refresh");
|
|
455
|
+
refreshToken().catch((err) => {
|
|
456
|
+
console.error("\u274C Immediate refresh failed:", err);
|
|
457
|
+
notifySessionInvalid("token_expired");
|
|
458
|
+
});
|
|
459
|
+
return null;
|
|
460
|
+
}
|
|
461
|
+
const refreshIn = Math.max(0, timeUntilExpiry - tokenRefreshBuffer) * 1e3;
|
|
462
|
+
console.log(`\u{1F504} Scheduling proactive refresh in ${Math.round(refreshIn / 1e3)}s (token expires in ${timeUntilExpiry}s)`);
|
|
463
|
+
proactiveRefreshTimer = setTimeout(async () => {
|
|
464
|
+
var _a;
|
|
465
|
+
try {
|
|
466
|
+
console.log("\u{1F504} Proactive token refresh triggered");
|
|
467
|
+
await refreshToken();
|
|
468
|
+
console.log("\u2705 Proactive refresh successful, scheduling next refresh");
|
|
469
|
+
startProactiveRefresh();
|
|
470
|
+
} catch (err) {
|
|
471
|
+
console.error("\u274C Proactive refresh failed:", err);
|
|
472
|
+
const errorMessage = ((_a = err.message) == null ? void 0 : _a.toLowerCase()) || "";
|
|
473
|
+
const isPermanentFailure = errorMessage.includes("401") || errorMessage.includes("revoked") || errorMessage.includes("invalid") || errorMessage.includes("expired") || errorMessage.includes("unauthorized");
|
|
474
|
+
if (isPermanentFailure) {
|
|
475
|
+
console.log("\u{1F6A8} Token permanently invalid, triggering session expiry");
|
|
476
|
+
notifySessionInvalid("refresh_token_revoked");
|
|
477
|
+
} else {
|
|
478
|
+
proactiveRefreshTimer = setTimeout(() => startProactiveRefresh(), 3e4);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}, refreshIn);
|
|
482
|
+
return proactiveRefreshTimer;
|
|
483
|
+
}
|
|
484
|
+
function stopProactiveRefresh() {
|
|
485
|
+
if (proactiveRefreshTimer) {
|
|
486
|
+
clearTimeout(proactiveRefreshTimer);
|
|
487
|
+
proactiveRefreshTimer = null;
|
|
488
|
+
console.log("\u23F9\uFE0F Proactive refresh stopped");
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
function startSessionMonitor(onInvalid) {
|
|
492
|
+
const { enableSessionValidation, sessionValidationInterval, validateOnVisibility } = getConfig();
|
|
493
|
+
if (!enableSessionValidation) {
|
|
494
|
+
console.log("\u23F8\uFE0F Session validation disabled by config");
|
|
495
|
+
return null;
|
|
496
|
+
}
|
|
497
|
+
if (onInvalid && typeof onInvalid === "function") {
|
|
498
|
+
sessionInvalidCallbacks.add(onInvalid);
|
|
499
|
+
}
|
|
500
|
+
stopSessionMonitor();
|
|
501
|
+
const token = getToken();
|
|
502
|
+
if (!token) {
|
|
503
|
+
console.log("\u23F8\uFE0F No token, skipping session monitor setup");
|
|
504
|
+
return null;
|
|
505
|
+
}
|
|
506
|
+
console.log(`\u{1F441}\uFE0F Starting session monitor (interval: ${sessionValidationInterval / 1e3}s)`);
|
|
507
|
+
sessionValidationTimer = setInterval(async () => {
|
|
508
|
+
try {
|
|
509
|
+
const currentToken = getToken();
|
|
510
|
+
if (!currentToken) {
|
|
511
|
+
console.log("\u23F8\uFE0F No token, stopping session validation");
|
|
512
|
+
stopSessionMonitor();
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
console.log("\u{1F50D} Validating session...");
|
|
516
|
+
const isValid = await validateCurrentSession();
|
|
517
|
+
if (!isValid) {
|
|
518
|
+
console.log("\u274C Session no longer valid on server");
|
|
519
|
+
stopSessionMonitor();
|
|
520
|
+
stopProactiveRefresh();
|
|
521
|
+
clearToken();
|
|
522
|
+
clearRefreshToken();
|
|
523
|
+
notifySessionInvalid("session_deleted");
|
|
524
|
+
} else {
|
|
525
|
+
console.log("\u2705 Session still valid");
|
|
526
|
+
}
|
|
527
|
+
} catch (error) {
|
|
528
|
+
console.warn("\u26A0\uFE0F Session validation check failed:", error.message);
|
|
529
|
+
}
|
|
530
|
+
}, sessionValidationInterval);
|
|
531
|
+
if (validateOnVisibility && typeof document !== "undefined") {
|
|
532
|
+
visibilityHandler = async () => {
|
|
533
|
+
if (document.visibilityState === "visible") {
|
|
534
|
+
const currentToken = getToken();
|
|
535
|
+
if (!currentToken) return;
|
|
536
|
+
console.log("\u{1F441}\uFE0F Tab visible - validating session");
|
|
537
|
+
try {
|
|
538
|
+
const isValid = await validateCurrentSession();
|
|
539
|
+
if (!isValid) {
|
|
540
|
+
console.log("\u274C Session expired while tab was hidden");
|
|
541
|
+
stopSessionMonitor();
|
|
542
|
+
stopProactiveRefresh();
|
|
543
|
+
clearToken();
|
|
544
|
+
clearRefreshToken();
|
|
545
|
+
notifySessionInvalid("session_deleted_while_hidden");
|
|
546
|
+
}
|
|
547
|
+
} catch (error) {
|
|
548
|
+
console.warn("\u26A0\uFE0F Visibility check failed:", error.message);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
};
|
|
552
|
+
document.addEventListener("visibilitychange", visibilityHandler);
|
|
553
|
+
}
|
|
554
|
+
return sessionValidationTimer;
|
|
555
|
+
}
|
|
556
|
+
function stopSessionMonitor() {
|
|
557
|
+
if (sessionValidationTimer) {
|
|
558
|
+
clearInterval(sessionValidationTimer);
|
|
559
|
+
sessionValidationTimer = null;
|
|
560
|
+
console.log("\u23F9\uFE0F Session monitor stopped");
|
|
561
|
+
}
|
|
562
|
+
if (visibilityHandler && typeof document !== "undefined") {
|
|
563
|
+
document.removeEventListener("visibilitychange", visibilityHandler);
|
|
564
|
+
visibilityHandler = null;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
function startSessionSecurity(onSessionInvalidCallback) {
|
|
568
|
+
console.log("\u{1F510} Starting session security (proactive refresh + session monitoring)");
|
|
569
|
+
startProactiveRefresh();
|
|
570
|
+
startSessionMonitor(onSessionInvalidCallback);
|
|
571
|
+
return {
|
|
572
|
+
stopAll: () => {
|
|
573
|
+
stopProactiveRefresh();
|
|
574
|
+
stopSessionMonitor();
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
function stopSessionSecurity() {
|
|
579
|
+
stopProactiveRefresh();
|
|
580
|
+
stopSessionMonitor();
|
|
581
|
+
sessionInvalidCallbacks.clear();
|
|
582
|
+
console.log("\u{1F510} Session security stopped");
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// react/AuthProvider.jsx
|
|
586
|
+
var AuthContext = (0, import_react.createContext)();
|
|
587
|
+
function AuthProvider({ children, onSessionExpired }) {
|
|
588
|
+
const [token, setTokenState] = (0, import_react.useState)(getToken());
|
|
589
|
+
const [user, setUser] = (0, import_react.useState)(null);
|
|
590
|
+
const [loading, setLoading] = (0, import_react.useState)(!!token);
|
|
591
|
+
const [sessionValid, setSessionValid] = (0, import_react.useState)(true);
|
|
592
|
+
const sessionSecurityRef = (0, import_react.useRef)(null);
|
|
593
|
+
const handleSessionInvalid = (reason) => {
|
|
594
|
+
console.log("\u{1F6A8} AuthProvider: Session invalidated -", reason);
|
|
595
|
+
setSessionValid(false);
|
|
596
|
+
setUser(null);
|
|
597
|
+
setTokenState(null);
|
|
598
|
+
if (onSessionExpired && typeof onSessionExpired === "function") {
|
|
599
|
+
onSessionExpired(reason);
|
|
600
|
+
}
|
|
601
|
+
};
|
|
602
|
+
(0, import_react.useEffect)(() => {
|
|
603
|
+
if (token && !sessionSecurityRef.current) {
|
|
604
|
+
console.log("\u{1F510} AuthProvider: Starting session security");
|
|
605
|
+
const unsubscribe = onSessionInvalid(handleSessionInvalid);
|
|
606
|
+
sessionSecurityRef.current = startSessionSecurity(handleSessionInvalid);
|
|
607
|
+
return () => {
|
|
608
|
+
unsubscribe();
|
|
609
|
+
if (sessionSecurityRef.current) {
|
|
610
|
+
sessionSecurityRef.current.stopAll();
|
|
611
|
+
sessionSecurityRef.current = null;
|
|
612
|
+
}
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
if (!token && sessionSecurityRef.current) {
|
|
616
|
+
sessionSecurityRef.current.stopAll();
|
|
617
|
+
sessionSecurityRef.current = null;
|
|
618
|
+
}
|
|
619
|
+
}, [token]);
|
|
620
|
+
(0, import_react.useEffect)(() => {
|
|
621
|
+
console.log("\u{1F50D} AuthProvider useEffect triggered:", {
|
|
622
|
+
hasToken: !!token,
|
|
623
|
+
tokenLength: token == null ? void 0 : token.length
|
|
624
|
+
});
|
|
625
|
+
if (!token) {
|
|
626
|
+
console.log("\u26A0\uFE0F AuthProvider: No token, setting loading=false");
|
|
627
|
+
setLoading(false);
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
const { authBaseUrl } = getConfig();
|
|
631
|
+
if (!authBaseUrl) {
|
|
632
|
+
console.warn("AuthProvider: No authBaseUrl configured");
|
|
633
|
+
setLoading(false);
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
console.log("\u{1F310} AuthProvider: Fetching profile with token...", {
|
|
637
|
+
authBaseUrl,
|
|
638
|
+
tokenPreview: token.slice(0, 50) + "..."
|
|
639
|
+
});
|
|
640
|
+
fetch(`${authBaseUrl}/account/profile`, {
|
|
641
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
642
|
+
credentials: "include"
|
|
643
|
+
}).then((res) => {
|
|
644
|
+
console.log("\u{1F4E5} Profile response status:", res.status);
|
|
645
|
+
if (!res.ok) throw new Error("Failed to fetch user");
|
|
646
|
+
return res.json();
|
|
647
|
+
}).then((userData) => {
|
|
648
|
+
console.log("\u2705 Profile fetched successfully:", userData.email);
|
|
649
|
+
setUser(userData);
|
|
650
|
+
setSessionValid(true);
|
|
651
|
+
setLoading(false);
|
|
652
|
+
}).catch((err) => {
|
|
653
|
+
console.error("\u274C Fetch user error:", err);
|
|
654
|
+
clearToken();
|
|
655
|
+
setTokenState(null);
|
|
656
|
+
setUser(null);
|
|
657
|
+
setLoading(false);
|
|
658
|
+
});
|
|
659
|
+
}, [token]);
|
|
660
|
+
const login2 = (clientKey, redirectUri, state) => {
|
|
661
|
+
login(clientKey, redirectUri, state);
|
|
662
|
+
};
|
|
663
|
+
const logout2 = () => {
|
|
664
|
+
stopSessionSecurity();
|
|
665
|
+
sessionSecurityRef.current = null;
|
|
666
|
+
logout();
|
|
667
|
+
setUser(null);
|
|
668
|
+
setTokenState(null);
|
|
669
|
+
setSessionValid(true);
|
|
670
|
+
};
|
|
671
|
+
const value = {
|
|
672
|
+
token,
|
|
673
|
+
user,
|
|
674
|
+
loading,
|
|
675
|
+
login: login2,
|
|
676
|
+
logout: logout2,
|
|
677
|
+
isAuthenticated: !!token && !!user && sessionValid,
|
|
678
|
+
sessionValid,
|
|
679
|
+
setUser,
|
|
680
|
+
setToken: (newToken) => {
|
|
681
|
+
setToken(newToken);
|
|
682
|
+
setTokenState(newToken);
|
|
683
|
+
setSessionValid(true);
|
|
684
|
+
},
|
|
685
|
+
clearToken: () => {
|
|
686
|
+
stopSessionSecurity();
|
|
687
|
+
sessionSecurityRef.current = null;
|
|
688
|
+
clearToken();
|
|
689
|
+
setTokenState(null);
|
|
690
|
+
setUser(null);
|
|
691
|
+
}
|
|
692
|
+
};
|
|
693
|
+
return /* @__PURE__ */ import_react.default.createElement(AuthContext.Provider, { value }, children);
|
|
694
|
+
}
|
|
695
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
696
|
+
0 && (module.exports = {
|
|
697
|
+
AuthContext,
|
|
698
|
+
AuthProvider
|
|
699
|
+
});
|
|
700
|
+
//# sourceMappingURL=AuthProvider.cjs.map
|