@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
package/token.js
DELETED
|
@@ -1,455 +0,0 @@
|
|
|
1
|
-
// auth-client/token.js - MINIMAL WORKING VERSION
|
|
2
|
-
|
|
3
|
-
import { jwtDecode } from 'jwt-decode';
|
|
4
|
-
|
|
5
|
-
let accessToken = null;
|
|
6
|
-
const listeners = new Set();
|
|
7
|
-
|
|
8
|
-
const REFRESH_COOKIE = 'account_refresh_token';
|
|
9
|
-
const COOKIE_MAX_AGE = 7 * 24 * 60 * 60;
|
|
10
|
-
|
|
11
|
-
function secureAttribute() {
|
|
12
|
-
try {
|
|
13
|
-
return typeof window !== 'undefined' && window.location?.protocol === 'https:'
|
|
14
|
-
? '; Secure'
|
|
15
|
-
: '';
|
|
16
|
-
} catch (err) {
|
|
17
|
-
return '';
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// ========== ACCESS TOKEN ==========
|
|
22
|
-
function writeAccessToken(token) {
|
|
23
|
-
if (!token) {
|
|
24
|
-
try {
|
|
25
|
-
localStorage.removeItem('authToken');
|
|
26
|
-
} catch (err) {
|
|
27
|
-
console.warn('Could not clear token from localStorage:', err);
|
|
28
|
-
}
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
try {
|
|
33
|
-
localStorage.setItem('authToken', token);
|
|
34
|
-
} catch (err) {
|
|
35
|
-
console.warn('Could not persist token to localStorage:', err);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function readAccessToken() {
|
|
40
|
-
try {
|
|
41
|
-
return localStorage.getItem('authToken');
|
|
42
|
-
} catch (err) {
|
|
43
|
-
console.warn('Could not read token from localStorage:', err);
|
|
44
|
-
return null;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// ========== REFRESH TOKEN (KEEP SIMPLE) ==========
|
|
49
|
-
// export function setRefreshToken(token) {
|
|
50
|
-
// if (!token) {
|
|
51
|
-
// clearRefreshToken();
|
|
52
|
-
// return;
|
|
53
|
-
// }
|
|
54
|
-
|
|
55
|
-
// const expires = new Date(Date.now() + COOKIE_MAX_AGE * 1000);
|
|
56
|
-
|
|
57
|
-
// try {
|
|
58
|
-
// document.cookie = `${REFRESH_COOKIE}=${encodeURIComponent(token)}; Path=/; SameSite=Lax${secureAttribute()}; Expires=${expires.toUTCString()}`;
|
|
59
|
-
// } catch (err) {
|
|
60
|
-
// console.warn('Could not set refresh token:', err);
|
|
61
|
-
// }
|
|
62
|
-
// }
|
|
63
|
-
|
|
64
|
-
// export function getRefreshToken() {
|
|
65
|
-
// try {
|
|
66
|
-
// const match = document.cookie
|
|
67
|
-
// ?.split('; ')
|
|
68
|
-
// ?.find((row) => row.startsWith(`${REFRESH_COOKIE}=`));
|
|
69
|
-
|
|
70
|
-
// if (match) {
|
|
71
|
-
// return decodeURIComponent(match.split('=')[1]);
|
|
72
|
-
// }
|
|
73
|
-
// } catch (err) {
|
|
74
|
-
// console.warn('Could not read refresh token:', err);
|
|
75
|
-
// }
|
|
76
|
-
|
|
77
|
-
// return null;
|
|
78
|
-
// }
|
|
79
|
-
|
|
80
|
-
// export function clearRefreshToken() {
|
|
81
|
-
// try {
|
|
82
|
-
// document.cookie = `${REFRESH_COOKIE}=; Path=/; SameSite=Lax${secureAttribute()}; Expires=Thu, 01 Jan 1970 00:00:00 GMT`;
|
|
83
|
-
// } catch (err) {
|
|
84
|
-
// console.warn('Could not clear refresh token:', err);
|
|
85
|
-
// }
|
|
86
|
-
// }
|
|
87
|
-
|
|
88
|
-
// ========== ACCESS TOKEN FUNCTIONS ==========
|
|
89
|
-
function decode(token) {
|
|
90
|
-
try {
|
|
91
|
-
return jwtDecode(token);
|
|
92
|
-
} catch (err) {
|
|
93
|
-
return null;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function isExpired(token, bufferSeconds = 60) {
|
|
98
|
-
if (!token) return true;
|
|
99
|
-
const decoded = decode(token);
|
|
100
|
-
if (!decoded?.exp) return true;
|
|
101
|
-
const now = Date.now() / 1000;
|
|
102
|
-
return decoded.exp < now + bufferSeconds;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// ========== TOKEN EXPIRY UTILITIES ==========
|
|
106
|
-
// Get the exact expiry time of a token as a Date object
|
|
107
|
-
export function getTokenExpiryTime(token) {
|
|
108
|
-
if (!token) return null;
|
|
109
|
-
const decoded = decode(token);
|
|
110
|
-
if (!decoded?.exp) return null;
|
|
111
|
-
return new Date(decoded.exp * 1000);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Get seconds until token expires (negative if already expired)
|
|
115
|
-
export function getTimeUntilExpiry(token) {
|
|
116
|
-
if (!token) return -1;
|
|
117
|
-
const decoded = decode(token);
|
|
118
|
-
if (!decoded?.exp) return -1;
|
|
119
|
-
const now = Date.now() / 1000;
|
|
120
|
-
return Math.floor(decoded.exp - now);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Check if token will expire within the next N seconds
|
|
124
|
-
export function willExpireSoon(token, withinSeconds = 60) {
|
|
125
|
-
const timeLeft = getTimeUntilExpiry(token);
|
|
126
|
-
return timeLeft >= 0 && timeLeft <= withinSeconds;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
export function setToken(token) {
|
|
130
|
-
const previousToken = accessToken;
|
|
131
|
-
accessToken = token || null;
|
|
132
|
-
writeAccessToken(accessToken);
|
|
133
|
-
|
|
134
|
-
if (previousToken !== accessToken) {
|
|
135
|
-
listeners.forEach((listener) => {
|
|
136
|
-
try {
|
|
137
|
-
listener(accessToken, previousToken);
|
|
138
|
-
} catch (err) {
|
|
139
|
-
console.warn('Token listener error:', err);
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
export function getToken() {
|
|
146
|
-
if (accessToken) return accessToken;
|
|
147
|
-
accessToken = readAccessToken();
|
|
148
|
-
return accessToken;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
export function clearToken() {
|
|
152
|
-
if (!accessToken) {
|
|
153
|
-
writeAccessToken(null);
|
|
154
|
-
clearRefreshToken();
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const previousToken = accessToken;
|
|
159
|
-
accessToken = null;
|
|
160
|
-
writeAccessToken(null);
|
|
161
|
-
clearRefreshToken();
|
|
162
|
-
|
|
163
|
-
listeners.forEach((listener) => {
|
|
164
|
-
try {
|
|
165
|
-
listener(null, previousToken);
|
|
166
|
-
} catch (err) {
|
|
167
|
-
console.warn('Token listener error:', err);
|
|
168
|
-
}
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// ========== REFRESH TOKEN STORAGE FOR HTTP DEVELOPMENT ==========
|
|
173
|
-
// In production (HTTPS), refresh tokens should ONLY be in httpOnly cookies set by server
|
|
174
|
-
// For HTTP development (cross-origin cookies don't work), we store in localStorage
|
|
175
|
-
const REFRESH_TOKEN_KEY = 'auth_refresh_token';
|
|
176
|
-
|
|
177
|
-
function isHttpDevelopment() {
|
|
178
|
-
try {
|
|
179
|
-
return typeof window !== 'undefined' &&
|
|
180
|
-
window.location?.protocol === 'http:';
|
|
181
|
-
} catch (err) {
|
|
182
|
-
return false;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
export function setRefreshToken(token) {
|
|
187
|
-
if (!token) {
|
|
188
|
-
clearRefreshToken();
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// For HTTP development, store in localStorage (since httpOnly cookies don't work cross-origin)
|
|
193
|
-
if (isHttpDevelopment()) {
|
|
194
|
-
try {
|
|
195
|
-
localStorage.setItem(REFRESH_TOKEN_KEY, token);
|
|
196
|
-
console.log('📦 Refresh token stored in localStorage (HTTP dev mode)');
|
|
197
|
-
} catch (err) {
|
|
198
|
-
console.warn('Could not store refresh token:', err);
|
|
199
|
-
}
|
|
200
|
-
} else {
|
|
201
|
-
// In production (HTTPS), refresh token should be in httpOnly cookie only
|
|
202
|
-
console.log('🔒 Refresh token managed by server httpOnly cookie (production mode)');
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
export function getRefreshToken() {
|
|
207
|
-
// For HTTP development, read from localStorage
|
|
208
|
-
if (isHttpDevelopment()) {
|
|
209
|
-
try {
|
|
210
|
-
const token = localStorage.getItem(REFRESH_TOKEN_KEY);
|
|
211
|
-
return token;
|
|
212
|
-
} catch (err) {
|
|
213
|
-
console.warn('Could not read refresh token:', err);
|
|
214
|
-
return null;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// In production, refresh token is in httpOnly cookie (not accessible via JS)
|
|
219
|
-
// The refresh endpoint uses credentials: 'include' to send the cookie
|
|
220
|
-
return null;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
export function clearRefreshToken() {
|
|
224
|
-
// Clear localStorage (for HTTP dev)
|
|
225
|
-
try {
|
|
226
|
-
localStorage.removeItem(REFRESH_TOKEN_KEY);
|
|
227
|
-
} catch (err) {
|
|
228
|
-
// Ignore
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// Clear cookie (for production)
|
|
232
|
-
try {
|
|
233
|
-
document.cookie = `${REFRESH_COOKIE}=; Path=/; SameSite=Strict${secureAttribute()}; Expires=Thu, 01 Jan 1970 00:00:00 GMT`;
|
|
234
|
-
} catch (err) {
|
|
235
|
-
console.warn('Could not clear refresh token cookie:', err);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// Clear sessionStorage
|
|
239
|
-
try {
|
|
240
|
-
sessionStorage.removeItem(REFRESH_COOKIE);
|
|
241
|
-
} catch (err) {
|
|
242
|
-
// Ignore
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
export function addTokenListener(listener) {
|
|
247
|
-
if (typeof listener !== 'function') {
|
|
248
|
-
throw new Error('Token listener must be a function');
|
|
249
|
-
}
|
|
250
|
-
listeners.add(listener);
|
|
251
|
-
return () => {
|
|
252
|
-
listeners.delete(listener);
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
export function removeTokenListener(listener) {
|
|
257
|
-
listeners.delete(listener);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
export function getListenerCount() {
|
|
261
|
-
return listeners.size;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
export function isAuthenticated() {
|
|
265
|
-
const token = getToken();
|
|
266
|
-
return !!token && !isExpired(token, 15);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
// // auth-client/token.js
|
|
273
|
-
// import { jwtDecode } from 'jwt-decode';
|
|
274
|
-
|
|
275
|
-
// let accessToken = null;
|
|
276
|
-
// const listeners = new Set();
|
|
277
|
-
|
|
278
|
-
// const REFRESH_COOKIE = 'account_refresh_token';
|
|
279
|
-
// const COOKIE_MAX_AGE = 7 * 24 * 60 * 60; // 7 days in seconds
|
|
280
|
-
|
|
281
|
-
// function secureAttribute() {
|
|
282
|
-
// try {
|
|
283
|
-
// return typeof window !== 'undefined' && window.location?.protocol === 'https:'
|
|
284
|
-
// ? '; Secure'
|
|
285
|
-
// : '';
|
|
286
|
-
// } catch (err) {
|
|
287
|
-
// return '';
|
|
288
|
-
// }
|
|
289
|
-
// }
|
|
290
|
-
|
|
291
|
-
// function writeAccessToken(token) {
|
|
292
|
-
// if (!token) {
|
|
293
|
-
// try {
|
|
294
|
-
// localStorage.removeItem('authToken');
|
|
295
|
-
// } catch (err) {
|
|
296
|
-
// console.warn('Could not clear token from localStorage:', err);
|
|
297
|
-
// }
|
|
298
|
-
// return;
|
|
299
|
-
// }
|
|
300
|
-
|
|
301
|
-
// try {
|
|
302
|
-
// localStorage.setItem('authToken', token);
|
|
303
|
-
// } catch (err) {
|
|
304
|
-
// console.warn('Could not persist token to localStorage:', err);
|
|
305
|
-
// }
|
|
306
|
-
// }
|
|
307
|
-
|
|
308
|
-
// function readAccessToken() {
|
|
309
|
-
// try {
|
|
310
|
-
// return localStorage.getItem('authToken');
|
|
311
|
-
// } catch (err) {
|
|
312
|
-
// console.warn('Could not read token from localStorage:', err);
|
|
313
|
-
// return null;
|
|
314
|
-
// }
|
|
315
|
-
// }
|
|
316
|
-
|
|
317
|
-
// function decode(token) {
|
|
318
|
-
// try {
|
|
319
|
-
// return jwtDecode(token);
|
|
320
|
-
// } catch (err) {
|
|
321
|
-
// return null;
|
|
322
|
-
// }
|
|
323
|
-
// }
|
|
324
|
-
|
|
325
|
-
// function isExpired(token, bufferSeconds = 60) {
|
|
326
|
-
// if (!token) return true;
|
|
327
|
-
// const decoded = decode(token);
|
|
328
|
-
// if (!decoded?.exp) return true;
|
|
329
|
-
// const now = Date.now() / 1000;
|
|
330
|
-
// return decoded.exp < now + bufferSeconds;
|
|
331
|
-
// }
|
|
332
|
-
|
|
333
|
-
// export function setToken(token) {
|
|
334
|
-
// const previousToken = accessToken;
|
|
335
|
-
// accessToken = token || null;
|
|
336
|
-
// writeAccessToken(accessToken);
|
|
337
|
-
|
|
338
|
-
// if (previousToken !== accessToken) {
|
|
339
|
-
// listeners.forEach((listener) => {
|
|
340
|
-
// try {
|
|
341
|
-
// listener(accessToken, previousToken);
|
|
342
|
-
// } catch (err) {
|
|
343
|
-
// console.warn('Token listener error:', err);
|
|
344
|
-
// }
|
|
345
|
-
// });
|
|
346
|
-
// }
|
|
347
|
-
// }
|
|
348
|
-
|
|
349
|
-
// export function getToken() {
|
|
350
|
-
// if (accessToken) return accessToken;
|
|
351
|
-
// accessToken = readAccessToken();
|
|
352
|
-
// return accessToken;
|
|
353
|
-
// }
|
|
354
|
-
|
|
355
|
-
// export function clearToken() {
|
|
356
|
-
// if (!accessToken) {
|
|
357
|
-
// writeAccessToken(null);
|
|
358
|
-
// clearRefreshToken();
|
|
359
|
-
// return;
|
|
360
|
-
// }
|
|
361
|
-
|
|
362
|
-
// const previousToken = accessToken;
|
|
363
|
-
// accessToken = null;
|
|
364
|
-
// writeAccessToken(null);
|
|
365
|
-
// clearRefreshToken();
|
|
366
|
-
|
|
367
|
-
// listeners.forEach((listener) => {
|
|
368
|
-
// try {
|
|
369
|
-
// listener(null, previousToken);
|
|
370
|
-
// } catch (err) {
|
|
371
|
-
// console.warn('Token listener error:', err);
|
|
372
|
-
// }
|
|
373
|
-
// });
|
|
374
|
-
// }
|
|
375
|
-
|
|
376
|
-
// export function setRefreshToken(token) {
|
|
377
|
-
// if (!token) {
|
|
378
|
-
// clearRefreshToken();
|
|
379
|
-
// return;
|
|
380
|
-
// }
|
|
381
|
-
|
|
382
|
-
// const expires = new Date(Date.now() + COOKIE_MAX_AGE * 1000);
|
|
383
|
-
// try {
|
|
384
|
-
// document.cookie = `${REFRESH_COOKIE}=${encodeURIComponent(token)}; Path=/; SameSite=Strict${secureAttribute()}; Expires=${expires.toUTCString()}`;
|
|
385
|
-
// } catch (err) {
|
|
386
|
-
// console.warn('Could not persist refresh token cookie:', err);
|
|
387
|
-
// }
|
|
388
|
-
|
|
389
|
-
// try {
|
|
390
|
-
// sessionStorage.setItem(REFRESH_COOKIE, token);
|
|
391
|
-
// } catch (err) {
|
|
392
|
-
// console.warn('Could not persist refresh token to sessionStorage:', err);
|
|
393
|
-
// }
|
|
394
|
-
// }
|
|
395
|
-
|
|
396
|
-
// export function getRefreshToken() {
|
|
397
|
-
// // Prefer cookie to align with server expectations
|
|
398
|
-
// let cookieMatch = null;
|
|
399
|
-
|
|
400
|
-
// try {
|
|
401
|
-
// cookieMatch = document.cookie
|
|
402
|
-
// ?.split('; ')
|
|
403
|
-
// ?.find((row) => row.startsWith(`${REFRESH_COOKIE}=`));
|
|
404
|
-
// } catch (err) {
|
|
405
|
-
// cookieMatch = null;
|
|
406
|
-
// }
|
|
407
|
-
|
|
408
|
-
// if (cookieMatch) {
|
|
409
|
-
// return decodeURIComponent(cookieMatch.split('=')[1]);
|
|
410
|
-
// }
|
|
411
|
-
|
|
412
|
-
// try {
|
|
413
|
-
// return sessionStorage.getItem(REFRESH_COOKIE);
|
|
414
|
-
// } catch (err) {
|
|
415
|
-
// console.warn('Could not read refresh token from sessionStorage:', err);
|
|
416
|
-
// return null;
|
|
417
|
-
// }
|
|
418
|
-
// }
|
|
419
|
-
|
|
420
|
-
// export function clearRefreshToken() {
|
|
421
|
-
// try {
|
|
422
|
-
// document.cookie = `${REFRESH_COOKIE}=; Path=/; SameSite=Strict${secureAttribute()}; Expires=Thu, 01 Jan 1970 00:00:00 GMT`;
|
|
423
|
-
// } catch (err) {
|
|
424
|
-
// console.warn('Could not clear refresh token cookie:', err);
|
|
425
|
-
// }
|
|
426
|
-
// try {
|
|
427
|
-
// sessionStorage.removeItem(REFRESH_COOKIE);
|
|
428
|
-
// } catch (err) {
|
|
429
|
-
// console.warn('Could not clear refresh token from sessionStorage:', err);
|
|
430
|
-
// }
|
|
431
|
-
// }
|
|
432
|
-
|
|
433
|
-
// export function addTokenListener(listener) {
|
|
434
|
-
// if (typeof listener !== 'function') {
|
|
435
|
-
// throw new Error('Token listener must be a function');
|
|
436
|
-
// }
|
|
437
|
-
// listeners.add(listener);
|
|
438
|
-
// return () => {
|
|
439
|
-
// listeners.delete(listener);
|
|
440
|
-
// };
|
|
441
|
-
// }
|
|
442
|
-
|
|
443
|
-
// export function removeTokenListener(listener) {
|
|
444
|
-
// listeners.delete(listener);
|
|
445
|
-
// }
|
|
446
|
-
|
|
447
|
-
// export function getListenerCount() {
|
|
448
|
-
// return listeners.size;
|
|
449
|
-
// }
|
|
450
|
-
|
|
451
|
-
// export function isAuthenticated() {
|
|
452
|
-
// const token = getToken();
|
|
453
|
-
// return !!token && !isExpired(token, 15);
|
|
454
|
-
// }
|
|
455
|
-
|
package/utils/jwt.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
// auth-client/utils/jwt.js
|
|
2
|
-
import { jwtDecode } from 'jwt-decode';
|
|
3
|
-
import { getToken } from '../token';
|
|
4
|
-
|
|
5
|
-
export function decodeToken(token) {
|
|
6
|
-
try {
|
|
7
|
-
return jwtDecode(token);
|
|
8
|
-
} catch (err) {
|
|
9
|
-
console.warn('Failed to decode JWT:', err);
|
|
10
|
-
return null;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function isTokenExpired(token, bufferSeconds = 60) {
|
|
15
|
-
const decoded = decodeToken(token);
|
|
16
|
-
if (!decoded || !decoded.exp) return true;
|
|
17
|
-
const currentTime = Date.now() / 1000;
|
|
18
|
-
return decoded.exp < currentTime + bufferSeconds;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
// ✅ Check if user is authenticated
|
|
23
|
-
export function isAuthenticated() {
|
|
24
|
-
const token = getToken();
|
|
25
|
-
return !!token && !isTokenExpired(token);
|
|
26
|
-
}
|
|
27
|
-
|