@insforge/sdk 1.0.1-refresh.7 → 1.0.1-refresh.9
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.d.mts +46 -169
- package/dist/index.d.ts +46 -169
- package/dist/index.js +209 -406
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +209 -404
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -27,8 +27,6 @@ __export(index_exports, {
|
|
|
27
27
|
HttpClient: () => HttpClient,
|
|
28
28
|
InsForgeClient: () => InsForgeClient,
|
|
29
29
|
InsForgeError: () => InsForgeError,
|
|
30
|
-
LocalSessionStorage: () => LocalSessionStorage,
|
|
31
|
-
SecureSessionStorage: () => SecureSessionStorage,
|
|
32
30
|
Storage: () => Storage,
|
|
33
31
|
StorageBucket: () => StorageBucket,
|
|
34
32
|
TokenManager: () => TokenManager,
|
|
@@ -60,8 +58,6 @@ var InsForgeError = class _InsForgeError extends Error {
|
|
|
60
58
|
var HttpClient = class {
|
|
61
59
|
constructor(config) {
|
|
62
60
|
this.userToken = null;
|
|
63
|
-
this.isRefreshing = false;
|
|
64
|
-
this.refreshQueue = [];
|
|
65
61
|
this.baseUrl = config.baseUrl || "http://localhost:7130";
|
|
66
62
|
this.fetch = config.fetch || (globalThis.fetch ? globalThis.fetch.bind(globalThis) : void 0);
|
|
67
63
|
this.anonKey = config.anonKey;
|
|
@@ -74,12 +70,6 @@ var HttpClient = class {
|
|
|
74
70
|
);
|
|
75
71
|
}
|
|
76
72
|
}
|
|
77
|
-
/**
|
|
78
|
-
* Set the refresh callback for automatic token refresh on 401
|
|
79
|
-
*/
|
|
80
|
-
setRefreshCallback(callback) {
|
|
81
|
-
this.refreshCallback = callback;
|
|
82
|
-
}
|
|
83
73
|
buildUrl(path, params) {
|
|
84
74
|
const url = new URL(path, this.baseUrl);
|
|
85
75
|
if (params) {
|
|
@@ -96,9 +86,6 @@ var HttpClient = class {
|
|
|
96
86
|
return url.toString();
|
|
97
87
|
}
|
|
98
88
|
async request(method, path, options = {}) {
|
|
99
|
-
return this.performRequest(method, path, options, false);
|
|
100
|
-
}
|
|
101
|
-
async performRequest(method, path, options = {}, isRetry = false) {
|
|
102
89
|
const { params, headers = {}, body, ...fetchOptions } = options;
|
|
103
90
|
const url = this.buildUrl(path, params);
|
|
104
91
|
const requestHeaders = {
|
|
@@ -124,17 +111,9 @@ var HttpClient = class {
|
|
|
124
111
|
method,
|
|
125
112
|
headers: requestHeaders,
|
|
126
113
|
body: processedBody,
|
|
127
|
-
|
|
128
|
-
|
|
114
|
+
credentials: "include",
|
|
115
|
+
...fetchOptions
|
|
129
116
|
});
|
|
130
|
-
const isRefreshEndpoint = path.includes("/api/auth/refresh") || path.includes("/api/auth/logout");
|
|
131
|
-
if (response.status === 401 && !isRetry && !isRefreshEndpoint && this.refreshCallback) {
|
|
132
|
-
const newToken = await this.handleTokenRefresh();
|
|
133
|
-
if (newToken) {
|
|
134
|
-
this.setAuthToken(newToken);
|
|
135
|
-
return this.performRequest(method, path, options, true);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
117
|
if (response.status === 204) {
|
|
139
118
|
return void 0;
|
|
140
119
|
}
|
|
@@ -166,38 +145,6 @@ var HttpClient = class {
|
|
|
166
145
|
}
|
|
167
146
|
return data;
|
|
168
147
|
}
|
|
169
|
-
/**
|
|
170
|
-
* Handle token refresh with queue to prevent duplicate refreshes
|
|
171
|
-
* Multiple concurrent 401s will wait for a single refresh to complete
|
|
172
|
-
*/
|
|
173
|
-
async handleTokenRefresh() {
|
|
174
|
-
if (this.isRefreshing) {
|
|
175
|
-
return new Promise((resolve, reject) => {
|
|
176
|
-
this.refreshQueue.push({ resolve, reject });
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
this.isRefreshing = true;
|
|
180
|
-
try {
|
|
181
|
-
const newToken = await this.refreshCallback?.();
|
|
182
|
-
this.refreshQueue.forEach(({ resolve, reject }) => {
|
|
183
|
-
if (newToken) {
|
|
184
|
-
resolve(newToken);
|
|
185
|
-
} else {
|
|
186
|
-
reject(new Error("Token refresh failed"));
|
|
187
|
-
}
|
|
188
|
-
});
|
|
189
|
-
this.refreshQueue = [];
|
|
190
|
-
return newToken || null;
|
|
191
|
-
} catch (error) {
|
|
192
|
-
this.refreshQueue.forEach(({ reject }) => {
|
|
193
|
-
reject(error instanceof Error ? error : new Error("Token refresh failed"));
|
|
194
|
-
});
|
|
195
|
-
this.refreshQueue = [];
|
|
196
|
-
return null;
|
|
197
|
-
} finally {
|
|
198
|
-
this.isRefreshing = false;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
148
|
get(path, options) {
|
|
202
149
|
return this.request("GET", path, options);
|
|
203
150
|
}
|
|
@@ -226,58 +173,48 @@ var HttpClient = class {
|
|
|
226
173
|
}
|
|
227
174
|
};
|
|
228
175
|
|
|
229
|
-
// src/lib/
|
|
176
|
+
// src/lib/token-manager.ts
|
|
230
177
|
var TOKEN_KEY = "insforge-auth-token";
|
|
231
178
|
var USER_KEY = "insforge-auth-user";
|
|
232
179
|
var AUTH_FLAG_COOKIE = "isAuthenticated";
|
|
233
|
-
var
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
180
|
+
var CSRF_TOKEN_COOKIE = "insforge_csrf_token";
|
|
181
|
+
function hasAuthCookie() {
|
|
182
|
+
if (typeof document === "undefined") return false;
|
|
183
|
+
return document.cookie.split(";").some(
|
|
184
|
+
(c) => c.trim().startsWith(`${AUTH_FLAG_COOKIE}=`)
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
function setAuthCookie() {
|
|
188
|
+
if (typeof document === "undefined") return;
|
|
189
|
+
const maxAge = 7 * 24 * 60 * 60;
|
|
190
|
+
document.cookie = `${AUTH_FLAG_COOKIE}=true; path=/; max-age=${maxAge}; SameSite=Lax`;
|
|
191
|
+
}
|
|
192
|
+
function clearAuthCookie() {
|
|
193
|
+
if (typeof document === "undefined") return;
|
|
194
|
+
document.cookie = `${AUTH_FLAG_COOKIE}=; path=/; max-age=0; SameSite=Lax`;
|
|
195
|
+
}
|
|
196
|
+
function getCsrfToken() {
|
|
197
|
+
if (typeof document === "undefined") return null;
|
|
198
|
+
const match = document.cookie.split(";").find((c) => c.trim().startsWith(`${CSRF_TOKEN_COOKIE}=`));
|
|
199
|
+
if (!match) return null;
|
|
200
|
+
return match.split("=")[1] || null;
|
|
201
|
+
}
|
|
202
|
+
function setCsrfToken(token) {
|
|
203
|
+
if (typeof document === "undefined") return;
|
|
204
|
+
const maxAge = 7 * 24 * 60 * 60;
|
|
205
|
+
document.cookie = `${CSRF_TOKEN_COOKIE}=${token}; path=/; max-age=${maxAge}; SameSite=Lax`;
|
|
206
|
+
}
|
|
207
|
+
function clearCsrfToken() {
|
|
208
|
+
if (typeof document === "undefined") return;
|
|
209
|
+
document.cookie = `${CSRF_TOKEN_COOKIE}=; path=/; max-age=0; SameSite=Lax`;
|
|
210
|
+
}
|
|
211
|
+
var TokenManager = class {
|
|
212
|
+
constructor(storage) {
|
|
213
|
+
// In-memory storage
|
|
263
214
|
this.accessToken = null;
|
|
264
215
|
this.user = null;
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
if (this.accessToken) return false;
|
|
268
|
-
return this.hasAuthFlag();
|
|
269
|
-
}
|
|
270
|
-
// --- Private: Auth Flag Cookie Detection (SDK-managed on frontend domain) ---
|
|
271
|
-
hasAuthFlag() {
|
|
272
|
-
if (typeof document === "undefined") return false;
|
|
273
|
-
return document.cookie.split(";").some(
|
|
274
|
-
(c) => c.trim().startsWith(`${AUTH_FLAG_COOKIE}=`)
|
|
275
|
-
);
|
|
276
|
-
}
|
|
277
|
-
};
|
|
278
|
-
var LocalSessionStorage = class {
|
|
279
|
-
constructor(storage) {
|
|
280
|
-
this.strategyId = "local";
|
|
216
|
+
// Mode: 'memory' (new backend) or 'storage' (legacy backend, default)
|
|
217
|
+
this._mode = "storage";
|
|
281
218
|
if (storage) {
|
|
282
219
|
this.storage = storage;
|
|
283
220
|
} else if (typeof window !== "undefined" && window.localStorage) {
|
|
@@ -295,126 +232,111 @@ var LocalSessionStorage = class {
|
|
|
295
232
|
};
|
|
296
233
|
}
|
|
297
234
|
}
|
|
298
|
-
saveSession(session) {
|
|
299
|
-
this.storage.setItem(TOKEN_KEY, session.accessToken);
|
|
300
|
-
this.storage.setItem(USER_KEY, JSON.stringify(session.user));
|
|
301
|
-
}
|
|
302
|
-
getSession() {
|
|
303
|
-
const token = this.storage.getItem(TOKEN_KEY);
|
|
304
|
-
const userStr = this.storage.getItem(USER_KEY);
|
|
305
|
-
if (!token || !userStr) return null;
|
|
306
|
-
try {
|
|
307
|
-
const user = JSON.parse(userStr);
|
|
308
|
-
return { accessToken: token, user };
|
|
309
|
-
} catch {
|
|
310
|
-
this.clearSession();
|
|
311
|
-
return null;
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
getAccessToken() {
|
|
315
|
-
const token = this.storage.getItem(TOKEN_KEY);
|
|
316
|
-
return typeof token === "string" ? token : null;
|
|
317
|
-
}
|
|
318
|
-
setAccessToken(token) {
|
|
319
|
-
this.storage.setItem(TOKEN_KEY, token);
|
|
320
|
-
}
|
|
321
|
-
getUser() {
|
|
322
|
-
const userStr = this.storage.getItem(USER_KEY);
|
|
323
|
-
if (!userStr) return null;
|
|
324
|
-
try {
|
|
325
|
-
return JSON.parse(userStr);
|
|
326
|
-
} catch {
|
|
327
|
-
return null;
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
setUser(user) {
|
|
331
|
-
this.storage.setItem(USER_KEY, JSON.stringify(user));
|
|
332
|
-
}
|
|
333
|
-
clearSession() {
|
|
334
|
-
this.storage.removeItem(TOKEN_KEY);
|
|
335
|
-
this.storage.removeItem(USER_KEY);
|
|
336
|
-
}
|
|
337
|
-
shouldAttemptRefresh() {
|
|
338
|
-
return false;
|
|
339
|
-
}
|
|
340
|
-
};
|
|
341
|
-
|
|
342
|
-
// src/lib/token-manager.ts
|
|
343
|
-
var TokenManager = class {
|
|
344
235
|
/**
|
|
345
|
-
*
|
|
346
|
-
* @param storage - Optional custom storage adapter (used for initial LocalSessionStorage)
|
|
236
|
+
* Get current mode
|
|
347
237
|
*/
|
|
348
|
-
|
|
349
|
-
this.
|
|
238
|
+
get mode() {
|
|
239
|
+
return this._mode;
|
|
350
240
|
}
|
|
351
241
|
/**
|
|
352
|
-
* Set
|
|
353
|
-
* Called after capability discovery to switch to the appropriate strategy
|
|
242
|
+
* Set mode to memory (new backend with cookies + memory)
|
|
354
243
|
*/
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
if (existingSession && previousId !== strategy.strategyId) {
|
|
360
|
-
strategy.saveSession(existingSession);
|
|
244
|
+
setMemoryMode() {
|
|
245
|
+
if (this._mode === "storage") {
|
|
246
|
+
this.storage.removeItem(TOKEN_KEY);
|
|
247
|
+
this.storage.removeItem(USER_KEY);
|
|
361
248
|
}
|
|
249
|
+
this._mode = "memory";
|
|
362
250
|
}
|
|
363
251
|
/**
|
|
364
|
-
*
|
|
252
|
+
* Set mode to storage (legacy backend with localStorage)
|
|
253
|
+
* Also loads existing session from localStorage
|
|
365
254
|
*/
|
|
366
|
-
|
|
367
|
-
|
|
255
|
+
setStorageMode() {
|
|
256
|
+
this._mode = "storage";
|
|
257
|
+
this.loadFromStorage();
|
|
368
258
|
}
|
|
369
|
-
// --- Delegated Methods ---
|
|
370
259
|
/**
|
|
371
|
-
*
|
|
260
|
+
* Load session from localStorage
|
|
261
|
+
*/
|
|
262
|
+
loadFromStorage() {
|
|
263
|
+
const token = this.storage.getItem(TOKEN_KEY);
|
|
264
|
+
const userStr = this.storage.getItem(USER_KEY);
|
|
265
|
+
if (token && userStr) {
|
|
266
|
+
try {
|
|
267
|
+
this.accessToken = token;
|
|
268
|
+
this.user = JSON.parse(userStr);
|
|
269
|
+
} catch {
|
|
270
|
+
this.clearSession();
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Save session (memory always, localStorage only in storage mode)
|
|
372
276
|
*/
|
|
373
277
|
saveSession(session) {
|
|
374
|
-
this.
|
|
278
|
+
this.accessToken = session.accessToken;
|
|
279
|
+
this.user = session.user;
|
|
280
|
+
if (this._mode === "storage") {
|
|
281
|
+
this.storage.setItem(TOKEN_KEY, session.accessToken);
|
|
282
|
+
this.storage.setItem(USER_KEY, JSON.stringify(session.user));
|
|
283
|
+
}
|
|
375
284
|
}
|
|
376
285
|
/**
|
|
377
286
|
* Get current session
|
|
378
287
|
*/
|
|
379
288
|
getSession() {
|
|
380
|
-
|
|
289
|
+
if (!this.accessToken || !this.user) return null;
|
|
290
|
+
return {
|
|
291
|
+
accessToken: this.accessToken,
|
|
292
|
+
user: this.user
|
|
293
|
+
};
|
|
381
294
|
}
|
|
382
295
|
/**
|
|
383
296
|
* Get access token
|
|
384
297
|
*/
|
|
385
298
|
getAccessToken() {
|
|
386
|
-
return this.
|
|
299
|
+
return this.accessToken;
|
|
387
300
|
}
|
|
388
301
|
/**
|
|
389
|
-
*
|
|
302
|
+
* Set access token
|
|
390
303
|
*/
|
|
391
304
|
setAccessToken(token) {
|
|
392
|
-
this.
|
|
305
|
+
this.accessToken = token;
|
|
306
|
+
if (this._mode === "storage") {
|
|
307
|
+
this.storage.setItem(TOKEN_KEY, token);
|
|
308
|
+
}
|
|
393
309
|
}
|
|
394
310
|
/**
|
|
395
|
-
* Get user
|
|
311
|
+
* Get user
|
|
396
312
|
*/
|
|
397
313
|
getUser() {
|
|
398
|
-
return this.
|
|
314
|
+
return this.user;
|
|
399
315
|
}
|
|
400
316
|
/**
|
|
401
|
-
*
|
|
317
|
+
* Set user
|
|
402
318
|
*/
|
|
403
319
|
setUser(user) {
|
|
404
|
-
this.
|
|
320
|
+
this.user = user;
|
|
321
|
+
if (this._mode === "storage") {
|
|
322
|
+
this.storage.setItem(USER_KEY, JSON.stringify(user));
|
|
323
|
+
}
|
|
405
324
|
}
|
|
406
325
|
/**
|
|
407
|
-
* Clear
|
|
326
|
+
* Clear session (both memory and localStorage)
|
|
408
327
|
*/
|
|
409
328
|
clearSession() {
|
|
410
|
-
this.
|
|
329
|
+
this.accessToken = null;
|
|
330
|
+
this.user = null;
|
|
331
|
+
this.storage.removeItem(TOKEN_KEY);
|
|
332
|
+
this.storage.removeItem(USER_KEY);
|
|
411
333
|
}
|
|
412
334
|
/**
|
|
413
|
-
* Check if
|
|
414
|
-
* (e.g., on page reload in secure mode)
|
|
335
|
+
* Check if there's a session in localStorage (for legacy detection)
|
|
415
336
|
*/
|
|
416
|
-
|
|
417
|
-
|
|
337
|
+
hasStoredSession() {
|
|
338
|
+
const token = this.storage.getItem(TOKEN_KEY);
|
|
339
|
+
return !!token;
|
|
418
340
|
}
|
|
419
341
|
};
|
|
420
342
|
|
|
@@ -531,77 +453,83 @@ var Auth = class {
|
|
|
531
453
|
this.detectAuthCallback();
|
|
532
454
|
}
|
|
533
455
|
/**
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
* Called when backend returns sessionMode: 'secure'
|
|
552
|
-
* @internal
|
|
553
|
-
*/
|
|
554
|
-
_switchToSecureStorage() {
|
|
555
|
-
console.log("[InsForge:Auth] _switchToSecureStorage() called, current strategy:", this.tokenManager.getStrategyId());
|
|
556
|
-
if (this.tokenManager.getStrategyId() === "secure") {
|
|
557
|
-
console.log("[InsForge:Auth] _switchToSecureStorage() - already in secure mode, skipping");
|
|
558
|
-
return;
|
|
456
|
+
* Restore session on app initialization
|
|
457
|
+
*
|
|
458
|
+
* @returns Object with isLoggedIn status
|
|
459
|
+
*
|
|
460
|
+
* @example
|
|
461
|
+
* ```typescript
|
|
462
|
+
* const client = new InsForgeClient({ baseUrl: '...' });
|
|
463
|
+
* const { isLoggedIn } = await client.auth.restoreSession();
|
|
464
|
+
*
|
|
465
|
+
* if (isLoggedIn) {
|
|
466
|
+
* const { data } = await client.auth.getCurrentUser();
|
|
467
|
+
* }
|
|
468
|
+
* ```
|
|
469
|
+
*/
|
|
470
|
+
async restoreSession() {
|
|
471
|
+
if (typeof window === "undefined") {
|
|
472
|
+
return { isLoggedIn: false };
|
|
559
473
|
}
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
if (typeof localStorage !== "undefined") {
|
|
563
|
-
console.log("[InsForge:Auth] _switchToSecureStorage() - clearing localStorage");
|
|
564
|
-
localStorage.removeItem(TOKEN_KEY);
|
|
565
|
-
localStorage.removeItem(USER_KEY);
|
|
474
|
+
if (this.tokenManager.getAccessToken()) {
|
|
475
|
+
return { isLoggedIn: true };
|
|
566
476
|
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
477
|
+
if (hasAuthCookie()) {
|
|
478
|
+
try {
|
|
479
|
+
const csrfToken = getCsrfToken();
|
|
480
|
+
const response = await this.http.post(
|
|
481
|
+
"/api/auth/refresh",
|
|
482
|
+
{
|
|
483
|
+
headers: csrfToken ? { "X-CSRF-Token": csrfToken } : {}
|
|
484
|
+
}
|
|
485
|
+
);
|
|
486
|
+
if (response.accessToken) {
|
|
487
|
+
this.tokenManager.setMemoryMode();
|
|
488
|
+
this.tokenManager.setAccessToken(response.accessToken);
|
|
489
|
+
this.http.setAuthToken(response.accessToken);
|
|
490
|
+
if (response.user) {
|
|
491
|
+
this.tokenManager.setUser(response.user);
|
|
492
|
+
}
|
|
493
|
+
if (response.csrfToken) {
|
|
494
|
+
setCsrfToken(response.csrfToken);
|
|
495
|
+
}
|
|
496
|
+
return { isLoggedIn: true };
|
|
497
|
+
}
|
|
498
|
+
} catch (error) {
|
|
499
|
+
if (error instanceof InsForgeError) {
|
|
500
|
+
if (error.statusCode === 404) {
|
|
501
|
+
this.tokenManager.setStorageMode();
|
|
502
|
+
const token = this.tokenManager.getAccessToken();
|
|
503
|
+
if (token) {
|
|
504
|
+
this.http.setAuthToken(token);
|
|
505
|
+
return { isLoggedIn: true };
|
|
506
|
+
}
|
|
507
|
+
return { isLoggedIn: false };
|
|
508
|
+
}
|
|
509
|
+
if (error.statusCode === 401 || error.statusCode === 403) {
|
|
510
|
+
clearAuthCookie();
|
|
511
|
+
clearCsrfToken();
|
|
512
|
+
return { isLoggedIn: false };
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
return { isLoggedIn: false };
|
|
516
|
+
}
|
|
571
517
|
}
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
if (this.tokenManager.getStrategyId() === "local") return;
|
|
580
|
-
const currentSession = this.tokenManager.getSession();
|
|
581
|
-
this.tokenManager.setStrategy(new LocalSessionStorage());
|
|
582
|
-
this.clearAuthenticatedCookie();
|
|
583
|
-
if (currentSession) {
|
|
584
|
-
this.tokenManager.saveSession(currentSession);
|
|
518
|
+
if (this.tokenManager.hasStoredSession()) {
|
|
519
|
+
this.tokenManager.setStorageMode();
|
|
520
|
+
const token = this.tokenManager.getAccessToken();
|
|
521
|
+
if (token) {
|
|
522
|
+
this.http.setAuthToken(token);
|
|
523
|
+
return { isLoggedIn: true };
|
|
524
|
+
}
|
|
585
525
|
}
|
|
526
|
+
return { isLoggedIn: false };
|
|
586
527
|
}
|
|
587
528
|
/**
|
|
588
|
-
*
|
|
589
|
-
*
|
|
590
|
-
*
|
|
529
|
+
* Automatically detect and handle OAuth callback parameters in the URL
|
|
530
|
+
* This runs on initialization to seamlessly complete the OAuth flow
|
|
531
|
+
* Matches the backend's OAuth callback response (backend/src/api/routes/auth.ts:540-544)
|
|
591
532
|
*/
|
|
592
|
-
_detectStorageFromResponse(sessionMode) {
|
|
593
|
-
console.log("[InsForge:Auth] _detectStorageFromResponse() - sessionMode:", sessionMode);
|
|
594
|
-
if (sessionMode === "secure") {
|
|
595
|
-
this._switchToSecureStorage();
|
|
596
|
-
} else {
|
|
597
|
-
this._switchToLocalStorage();
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
/**
|
|
601
|
-
* Automatically detect and handle OAuth callback parameters in the URL
|
|
602
|
-
* This runs on initialization to seamlessly complete the OAuth flow
|
|
603
|
-
* Matches the backend's OAuth callback response (backend/src/api/routes/auth.ts:540-544)
|
|
604
|
-
*/
|
|
605
533
|
detectAuthCallback() {
|
|
606
534
|
if (typeof window === "undefined") return;
|
|
607
535
|
try {
|
|
@@ -610,9 +538,8 @@ var Auth = class {
|
|
|
610
538
|
const userId = params.get("user_id");
|
|
611
539
|
const email = params.get("email");
|
|
612
540
|
const name = params.get("name");
|
|
613
|
-
const
|
|
541
|
+
const csrfToken = params.get("csrf_token");
|
|
614
542
|
if (accessToken && userId && email) {
|
|
615
|
-
this._detectStorageFromResponse(sessionMode || void 0);
|
|
616
543
|
const session = {
|
|
617
544
|
accessToken,
|
|
618
545
|
user: {
|
|
@@ -626,14 +553,18 @@ var Auth = class {
|
|
|
626
553
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
627
554
|
}
|
|
628
555
|
};
|
|
629
|
-
this.tokenManager.saveSession(session);
|
|
630
556
|
this.http.setAuthToken(accessToken);
|
|
557
|
+
this.tokenManager.saveSession(session);
|
|
558
|
+
setAuthCookie();
|
|
559
|
+
if (csrfToken) {
|
|
560
|
+
setCsrfToken(csrfToken);
|
|
561
|
+
}
|
|
631
562
|
const url = new URL(window.location.href);
|
|
632
563
|
url.searchParams.delete("access_token");
|
|
633
564
|
url.searchParams.delete("user_id");
|
|
634
565
|
url.searchParams.delete("email");
|
|
635
566
|
url.searchParams.delete("name");
|
|
636
|
-
url.searchParams.delete("
|
|
567
|
+
url.searchParams.delete("csrf_token");
|
|
637
568
|
if (params.has("error")) {
|
|
638
569
|
url.searchParams.delete("error");
|
|
639
570
|
}
|
|
@@ -649,17 +580,17 @@ var Auth = class {
|
|
|
649
580
|
async signUp(request) {
|
|
650
581
|
try {
|
|
651
582
|
const response = await this.http.post("/api/auth/users", request);
|
|
652
|
-
|
|
653
|
-
this._detectStorageFromResponse(sessionMode);
|
|
654
|
-
if (response.accessToken && response.user) {
|
|
583
|
+
if (response.accessToken && response.user && !isHostedAuthEnvironment()) {
|
|
655
584
|
const session = {
|
|
656
585
|
accessToken: response.accessToken,
|
|
657
586
|
user: response.user
|
|
658
587
|
};
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
}
|
|
588
|
+
this.tokenManager.saveSession(session);
|
|
589
|
+
setAuthCookie();
|
|
662
590
|
this.http.setAuthToken(response.accessToken);
|
|
591
|
+
if (response.csrfToken) {
|
|
592
|
+
setCsrfToken(response.csrfToken);
|
|
593
|
+
}
|
|
663
594
|
}
|
|
664
595
|
return {
|
|
665
596
|
data: response,
|
|
@@ -685,23 +616,18 @@ var Auth = class {
|
|
|
685
616
|
async signInWithPassword(request) {
|
|
686
617
|
try {
|
|
687
618
|
const response = await this.http.post("/api/auth/sessions", request);
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
id: "",
|
|
694
|
-
email: "",
|
|
695
|
-
name: "",
|
|
696
|
-
emailVerified: false,
|
|
697
|
-
createdAt: "",
|
|
698
|
-
updatedAt: ""
|
|
699
|
-
}
|
|
700
|
-
};
|
|
701
|
-
if (!isHostedAuthEnvironment()) {
|
|
619
|
+
if (response.accessToken && response.user && !isHostedAuthEnvironment()) {
|
|
620
|
+
const session = {
|
|
621
|
+
accessToken: response.accessToken,
|
|
622
|
+
user: response.user
|
|
623
|
+
};
|
|
702
624
|
this.tokenManager.saveSession(session);
|
|
625
|
+
setAuthCookie();
|
|
626
|
+
this.http.setAuthToken(response.accessToken);
|
|
627
|
+
if (response.csrfToken) {
|
|
628
|
+
setCsrfToken(response.csrfToken);
|
|
629
|
+
}
|
|
703
630
|
}
|
|
704
|
-
this.http.setAuthToken(response.accessToken || "");
|
|
705
631
|
return {
|
|
706
632
|
data: response,
|
|
707
633
|
error: null
|
|
@@ -756,28 +682,19 @@ var Auth = class {
|
|
|
756
682
|
}
|
|
757
683
|
/**
|
|
758
684
|
* Sign out the current user
|
|
759
|
-
* In modern mode, also calls backend to clear the refresh token cookie
|
|
760
685
|
*/
|
|
761
686
|
async signOut() {
|
|
762
|
-
console.log("[InsForge:Auth] signOut() called");
|
|
763
|
-
console.log("[InsForge:Auth] signOut() stack trace:", new Error().stack);
|
|
764
687
|
try {
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
await this.http.post("/api/auth/logout");
|
|
769
|
-
console.log("[InsForge:Auth] signOut() - backend logout successful");
|
|
770
|
-
} catch (e) {
|
|
771
|
-
console.log("[InsForge:Auth] signOut() - backend logout failed (ignored):", e);
|
|
772
|
-
}
|
|
688
|
+
try {
|
|
689
|
+
await this.http.post("/api/auth/logout");
|
|
690
|
+
} catch {
|
|
773
691
|
}
|
|
774
692
|
this.tokenManager.clearSession();
|
|
775
693
|
this.http.setAuthToken(null);
|
|
776
|
-
|
|
777
|
-
|
|
694
|
+
clearAuthCookie();
|
|
695
|
+
clearCsrfToken();
|
|
778
696
|
return { error: null };
|
|
779
697
|
} catch (error) {
|
|
780
|
-
console.error("[InsForge:Auth] signOut() - error:", error);
|
|
781
698
|
return {
|
|
782
699
|
error: new InsForgeError(
|
|
783
700
|
"Failed to sign out",
|
|
@@ -787,58 +704,6 @@ var Auth = class {
|
|
|
787
704
|
};
|
|
788
705
|
}
|
|
789
706
|
}
|
|
790
|
-
/**
|
|
791
|
-
* Refresh the access token using the httpOnly refresh token cookie
|
|
792
|
-
* Only works when backend supports secure session storage (httpOnly cookies)
|
|
793
|
-
*
|
|
794
|
-
* @returns New access token or throws an error
|
|
795
|
-
*/
|
|
796
|
-
async refreshToken() {
|
|
797
|
-
console.log("[InsForge:Auth] refreshToken() called");
|
|
798
|
-
try {
|
|
799
|
-
const response = await this.http.post(
|
|
800
|
-
"/api/auth/refresh"
|
|
801
|
-
);
|
|
802
|
-
console.log("[InsForge:Auth] refreshToken() - response received, hasAccessToken:", !!response.accessToken);
|
|
803
|
-
if (response.accessToken) {
|
|
804
|
-
this._detectStorageFromResponse(response.sessionMode);
|
|
805
|
-
this.tokenManager.setAccessToken(response.accessToken);
|
|
806
|
-
this.http.setAuthToken(response.accessToken);
|
|
807
|
-
if (response.user) {
|
|
808
|
-
this.tokenManager.setUser(response.user);
|
|
809
|
-
}
|
|
810
|
-
console.log("[InsForge:Auth] refreshToken() - success");
|
|
811
|
-
return response.accessToken;
|
|
812
|
-
}
|
|
813
|
-
throw new InsForgeError(
|
|
814
|
-
"No access token in refresh response",
|
|
815
|
-
500,
|
|
816
|
-
"REFRESH_FAILED"
|
|
817
|
-
);
|
|
818
|
-
} catch (error) {
|
|
819
|
-
console.error("[InsForge:Auth] refreshToken() - error:", error);
|
|
820
|
-
if (error instanceof InsForgeError) {
|
|
821
|
-
if (error.statusCode === 404) {
|
|
822
|
-
console.log("[InsForge:Auth] refreshToken() - 404 detected, backend does not support refresh endpoint");
|
|
823
|
-
console.log("[InsForge:Auth] refreshToken() - switching to LocalSessionStorage for backward compatibility");
|
|
824
|
-
this._switchToLocalStorage();
|
|
825
|
-
this.clearAuthenticatedCookie();
|
|
826
|
-
}
|
|
827
|
-
if (error.statusCode === 401 || error.statusCode === 403) {
|
|
828
|
-
console.log("[InsForge:Auth] refreshToken() - clearing session due to 401/403");
|
|
829
|
-
this.tokenManager.clearSession();
|
|
830
|
-
this.http.setAuthToken(null);
|
|
831
|
-
this.clearAuthenticatedCookie();
|
|
832
|
-
}
|
|
833
|
-
throw error;
|
|
834
|
-
}
|
|
835
|
-
throw new InsForgeError(
|
|
836
|
-
"Token refresh failed",
|
|
837
|
-
500,
|
|
838
|
-
"REFRESH_FAILED"
|
|
839
|
-
);
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
707
|
/**
|
|
843
708
|
* Get all public authentication configuration (OAuth + Email)
|
|
844
709
|
* Returns both OAuth providers and email authentication settings in one request
|
|
@@ -879,40 +744,19 @@ var Auth = class {
|
|
|
879
744
|
/**
|
|
880
745
|
* Get the current user with full profile information
|
|
881
746
|
* Returns both auth info (id, email, role) and profile data (dynamic fields from users table)
|
|
882
|
-
*
|
|
883
|
-
* In secure session mode (httpOnly cookie), this method will automatically attempt
|
|
884
|
-
* to refresh the session if no access token is available (e.g., after page reload).
|
|
885
747
|
*/
|
|
886
748
|
async getCurrentUser() {
|
|
887
|
-
console.log("[InsForge:Auth] getCurrentUser() called");
|
|
888
749
|
try {
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
console.log("[InsForge:Auth] getCurrentUser() - hasAccessToken:", !!accessToken, "shouldAttemptRefresh:", shouldRefresh);
|
|
892
|
-
if (!accessToken && shouldRefresh) {
|
|
893
|
-
console.log("[InsForge:Auth] getCurrentUser() - attempting refresh");
|
|
894
|
-
try {
|
|
895
|
-
accessToken = await this.refreshToken();
|
|
896
|
-
} catch (error) {
|
|
897
|
-
console.log("[InsForge:Auth] getCurrentUser() - refresh failed:", error);
|
|
898
|
-
if (error instanceof InsForgeError && (error.statusCode === 401 || error.statusCode === 403)) {
|
|
899
|
-
return { data: null, error };
|
|
900
|
-
}
|
|
901
|
-
return { data: null, error: error instanceof InsForgeError ? error : new InsForgeError("Token refresh failed", 500, "REFRESH_FAILED") };
|
|
902
|
-
}
|
|
903
|
-
}
|
|
904
|
-
if (!accessToken) {
|
|
905
|
-
console.log("[InsForge:Auth] getCurrentUser() - no access token, returning null");
|
|
750
|
+
const session = this.tokenManager.getSession();
|
|
751
|
+
if (!session?.accessToken) {
|
|
906
752
|
return { data: null, error: null };
|
|
907
753
|
}
|
|
908
|
-
this.http.setAuthToken(accessToken);
|
|
909
|
-
console.log("[InsForge:Auth] getCurrentUser() - fetching user from API");
|
|
754
|
+
this.http.setAuthToken(session.accessToken);
|
|
910
755
|
const authResponse = await this.http.get("/api/auth/sessions/current");
|
|
911
756
|
const { data: profile, error: profileError } = await this.database.from("users").select("*").eq("id", authResponse.user.id).single();
|
|
912
757
|
if (profileError && profileError.code !== "PGRST116") {
|
|
913
758
|
return { data: null, error: profileError };
|
|
914
759
|
}
|
|
915
|
-
console.log("[InsForge:Auth] getCurrentUser() - success");
|
|
916
760
|
return {
|
|
917
761
|
data: {
|
|
918
762
|
user: authResponse.user,
|
|
@@ -921,12 +765,8 @@ var Auth = class {
|
|
|
921
765
|
error: null
|
|
922
766
|
};
|
|
923
767
|
} catch (error) {
|
|
924
|
-
console.error("[InsForge:Auth] getCurrentUser() - catch error:", error);
|
|
925
768
|
if (error instanceof InsForgeError && error.statusCode === 401) {
|
|
926
|
-
|
|
927
|
-
this.tokenManager.clearSession();
|
|
928
|
-
this.http.setAuthToken(null);
|
|
929
|
-
this.clearAuthenticatedCookie();
|
|
769
|
+
await this.signOut();
|
|
930
770
|
return { data: null, error: null };
|
|
931
771
|
}
|
|
932
772
|
if (error instanceof InsForgeError) {
|
|
@@ -1174,15 +1014,17 @@ var Auth = class {
|
|
|
1174
1014
|
"/api/auth/email/verify",
|
|
1175
1015
|
request
|
|
1176
1016
|
);
|
|
1177
|
-
|
|
1178
|
-
this._detectStorageFromResponse(sessionMode);
|
|
1179
|
-
if (response.accessToken) {
|
|
1017
|
+
if (response.accessToken && !isHostedAuthEnvironment()) {
|
|
1180
1018
|
const session = {
|
|
1181
1019
|
accessToken: response.accessToken,
|
|
1182
1020
|
user: response.user || {}
|
|
1183
1021
|
};
|
|
1184
1022
|
this.tokenManager.saveSession(session);
|
|
1185
1023
|
this.http.setAuthToken(response.accessToken);
|
|
1024
|
+
setAuthCookie();
|
|
1025
|
+
if (response.csrfToken) {
|
|
1026
|
+
setCsrfToken(response.csrfToken);
|
|
1027
|
+
}
|
|
1186
1028
|
}
|
|
1187
1029
|
return {
|
|
1188
1030
|
data: response,
|
|
@@ -1726,24 +1568,10 @@ var Functions = class {
|
|
|
1726
1568
|
};
|
|
1727
1569
|
|
|
1728
1570
|
// src/client.ts
|
|
1729
|
-
function hasAuthenticatedCookie() {
|
|
1730
|
-
if (typeof document === "undefined") return false;
|
|
1731
|
-
return document.cookie.split(";").some(
|
|
1732
|
-
(c) => c.trim().startsWith(`${AUTH_FLAG_COOKIE}=`)
|
|
1733
|
-
);
|
|
1734
|
-
}
|
|
1735
1571
|
var InsForgeClient = class {
|
|
1736
1572
|
constructor(config = {}) {
|
|
1737
|
-
console.log("[InsForge:Client] Initializing SDK");
|
|
1738
1573
|
this.http = new HttpClient(config);
|
|
1739
1574
|
this.tokenManager = new TokenManager(config.storage);
|
|
1740
|
-
const hasAuthCookie = hasAuthenticatedCookie();
|
|
1741
|
-
console.log("[InsForge:Client] hasAuthenticatedCookie:", hasAuthCookie);
|
|
1742
|
-
console.log("[InsForge:Client] document.cookie:", typeof document !== "undefined" ? document.cookie : "N/A (SSR)");
|
|
1743
|
-
if (hasAuthCookie) {
|
|
1744
|
-
console.log("[InsForge:Client] Switching to SecureSessionStorage");
|
|
1745
|
-
this.tokenManager.setStrategy(new SecureSessionStorage());
|
|
1746
|
-
}
|
|
1747
1575
|
if (config.edgeFunctionToken) {
|
|
1748
1576
|
this.http.setAuthToken(config.edgeFunctionToken);
|
|
1749
1577
|
this.tokenManager.saveSession({
|
|
@@ -1752,32 +1580,15 @@ var InsForgeClient = class {
|
|
|
1752
1580
|
// Will be populated by getCurrentUser()
|
|
1753
1581
|
});
|
|
1754
1582
|
}
|
|
1755
|
-
this.http.setRefreshCallback(async () => {
|
|
1756
|
-
console.log("[InsForge:Client] HTTP 401 refresh callback triggered");
|
|
1757
|
-
try {
|
|
1758
|
-
return await this.auth.refreshToken();
|
|
1759
|
-
} catch (e) {
|
|
1760
|
-
console.log("[InsForge:Client] Refresh callback failed:", e);
|
|
1761
|
-
if (this.tokenManager.getStrategyId() === "secure") {
|
|
1762
|
-
console.log("[InsForge:Client] Falling back to LocalSessionStorage");
|
|
1763
|
-
this.auth._switchToLocalStorage();
|
|
1764
|
-
}
|
|
1765
|
-
return null;
|
|
1766
|
-
}
|
|
1767
|
-
});
|
|
1768
1583
|
const existingSession = this.tokenManager.getSession();
|
|
1769
|
-
console.log("[InsForge:Client] existingSession:", !!existingSession, "strategyId:", this.tokenManager.getStrategyId());
|
|
1770
1584
|
if (existingSession?.accessToken) {
|
|
1771
1585
|
this.http.setAuthToken(existingSession.accessToken);
|
|
1772
|
-
} else if (this.tokenManager.getStrategyId() === "secure") {
|
|
1773
|
-
console.log("[InsForge:Client] Secure mode, no session in memory - will refresh on first API call");
|
|
1774
1586
|
}
|
|
1775
1587
|
this.auth = new Auth(this.http, this.tokenManager);
|
|
1776
1588
|
this.database = new Database(this.http, this.tokenManager);
|
|
1777
1589
|
this.storage = new Storage(this.http);
|
|
1778
1590
|
this.ai = new AI(this.http);
|
|
1779
1591
|
this.functions = new Functions(this.http);
|
|
1780
|
-
console.log("[InsForge:Client] SDK initialized");
|
|
1781
1592
|
}
|
|
1782
1593
|
/**
|
|
1783
1594
|
* Get the underlying HTTP client for custom requests
|
|
@@ -1791,12 +1602,6 @@ var InsForgeClient = class {
|
|
|
1791
1602
|
getHttpClient() {
|
|
1792
1603
|
return this.http;
|
|
1793
1604
|
}
|
|
1794
|
-
/**
|
|
1795
|
-
* Get the current storage strategy identifier
|
|
1796
|
-
*/
|
|
1797
|
-
getStorageStrategy() {
|
|
1798
|
-
return this.tokenManager.getStrategyId();
|
|
1799
|
-
}
|
|
1800
1605
|
/**
|
|
1801
1606
|
* Future modules will be added here:
|
|
1802
1607
|
* - database: Database operations
|
|
@@ -1821,8 +1626,6 @@ var index_default = InsForgeClient;
|
|
|
1821
1626
|
HttpClient,
|
|
1822
1627
|
InsForgeClient,
|
|
1823
1628
|
InsForgeError,
|
|
1824
|
-
LocalSessionStorage,
|
|
1825
|
-
SecureSessionStorage,
|
|
1826
1629
|
Storage,
|
|
1827
1630
|
StorageBucket,
|
|
1828
1631
|
TokenManager,
|