@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.mjs
CHANGED
|
@@ -21,8 +21,6 @@ var InsForgeError = class _InsForgeError extends Error {
|
|
|
21
21
|
var HttpClient = class {
|
|
22
22
|
constructor(config) {
|
|
23
23
|
this.userToken = null;
|
|
24
|
-
this.isRefreshing = false;
|
|
25
|
-
this.refreshQueue = [];
|
|
26
24
|
this.baseUrl = config.baseUrl || "http://localhost:7130";
|
|
27
25
|
this.fetch = config.fetch || (globalThis.fetch ? globalThis.fetch.bind(globalThis) : void 0);
|
|
28
26
|
this.anonKey = config.anonKey;
|
|
@@ -35,12 +33,6 @@ var HttpClient = class {
|
|
|
35
33
|
);
|
|
36
34
|
}
|
|
37
35
|
}
|
|
38
|
-
/**
|
|
39
|
-
* Set the refresh callback for automatic token refresh on 401
|
|
40
|
-
*/
|
|
41
|
-
setRefreshCallback(callback) {
|
|
42
|
-
this.refreshCallback = callback;
|
|
43
|
-
}
|
|
44
36
|
buildUrl(path, params) {
|
|
45
37
|
const url = new URL(path, this.baseUrl);
|
|
46
38
|
if (params) {
|
|
@@ -57,9 +49,6 @@ var HttpClient = class {
|
|
|
57
49
|
return url.toString();
|
|
58
50
|
}
|
|
59
51
|
async request(method, path, options = {}) {
|
|
60
|
-
return this.performRequest(method, path, options, false);
|
|
61
|
-
}
|
|
62
|
-
async performRequest(method, path, options = {}, isRetry = false) {
|
|
63
52
|
const { params, headers = {}, body, ...fetchOptions } = options;
|
|
64
53
|
const url = this.buildUrl(path, params);
|
|
65
54
|
const requestHeaders = {
|
|
@@ -85,17 +74,9 @@ var HttpClient = class {
|
|
|
85
74
|
method,
|
|
86
75
|
headers: requestHeaders,
|
|
87
76
|
body: processedBody,
|
|
88
|
-
|
|
89
|
-
|
|
77
|
+
credentials: "include",
|
|
78
|
+
...fetchOptions
|
|
90
79
|
});
|
|
91
|
-
const isRefreshEndpoint = path.includes("/api/auth/refresh") || path.includes("/api/auth/logout");
|
|
92
|
-
if (response.status === 401 && !isRetry && !isRefreshEndpoint && this.refreshCallback) {
|
|
93
|
-
const newToken = await this.handleTokenRefresh();
|
|
94
|
-
if (newToken) {
|
|
95
|
-
this.setAuthToken(newToken);
|
|
96
|
-
return this.performRequest(method, path, options, true);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
80
|
if (response.status === 204) {
|
|
100
81
|
return void 0;
|
|
101
82
|
}
|
|
@@ -127,38 +108,6 @@ var HttpClient = class {
|
|
|
127
108
|
}
|
|
128
109
|
return data;
|
|
129
110
|
}
|
|
130
|
-
/**
|
|
131
|
-
* Handle token refresh with queue to prevent duplicate refreshes
|
|
132
|
-
* Multiple concurrent 401s will wait for a single refresh to complete
|
|
133
|
-
*/
|
|
134
|
-
async handleTokenRefresh() {
|
|
135
|
-
if (this.isRefreshing) {
|
|
136
|
-
return new Promise((resolve, reject) => {
|
|
137
|
-
this.refreshQueue.push({ resolve, reject });
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
this.isRefreshing = true;
|
|
141
|
-
try {
|
|
142
|
-
const newToken = await this.refreshCallback?.();
|
|
143
|
-
this.refreshQueue.forEach(({ resolve, reject }) => {
|
|
144
|
-
if (newToken) {
|
|
145
|
-
resolve(newToken);
|
|
146
|
-
} else {
|
|
147
|
-
reject(new Error("Token refresh failed"));
|
|
148
|
-
}
|
|
149
|
-
});
|
|
150
|
-
this.refreshQueue = [];
|
|
151
|
-
return newToken || null;
|
|
152
|
-
} catch (error) {
|
|
153
|
-
this.refreshQueue.forEach(({ reject }) => {
|
|
154
|
-
reject(error instanceof Error ? error : new Error("Token refresh failed"));
|
|
155
|
-
});
|
|
156
|
-
this.refreshQueue = [];
|
|
157
|
-
return null;
|
|
158
|
-
} finally {
|
|
159
|
-
this.isRefreshing = false;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
111
|
get(path, options) {
|
|
163
112
|
return this.request("GET", path, options);
|
|
164
113
|
}
|
|
@@ -187,58 +136,48 @@ var HttpClient = class {
|
|
|
187
136
|
}
|
|
188
137
|
};
|
|
189
138
|
|
|
190
|
-
// src/lib/
|
|
139
|
+
// src/lib/token-manager.ts
|
|
191
140
|
var TOKEN_KEY = "insforge-auth-token";
|
|
192
141
|
var USER_KEY = "insforge-auth-user";
|
|
193
142
|
var AUTH_FLAG_COOKIE = "isAuthenticated";
|
|
194
|
-
var
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
143
|
+
var CSRF_TOKEN_COOKIE = "insforge_csrf_token";
|
|
144
|
+
function hasAuthCookie() {
|
|
145
|
+
if (typeof document === "undefined") return false;
|
|
146
|
+
return document.cookie.split(";").some(
|
|
147
|
+
(c) => c.trim().startsWith(`${AUTH_FLAG_COOKIE}=`)
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
function setAuthCookie() {
|
|
151
|
+
if (typeof document === "undefined") return;
|
|
152
|
+
const maxAge = 7 * 24 * 60 * 60;
|
|
153
|
+
document.cookie = `${AUTH_FLAG_COOKIE}=true; path=/; max-age=${maxAge}; SameSite=Lax`;
|
|
154
|
+
}
|
|
155
|
+
function clearAuthCookie() {
|
|
156
|
+
if (typeof document === "undefined") return;
|
|
157
|
+
document.cookie = `${AUTH_FLAG_COOKIE}=; path=/; max-age=0; SameSite=Lax`;
|
|
158
|
+
}
|
|
159
|
+
function getCsrfToken() {
|
|
160
|
+
if (typeof document === "undefined") return null;
|
|
161
|
+
const match = document.cookie.split(";").find((c) => c.trim().startsWith(`${CSRF_TOKEN_COOKIE}=`));
|
|
162
|
+
if (!match) return null;
|
|
163
|
+
return match.split("=")[1] || null;
|
|
164
|
+
}
|
|
165
|
+
function setCsrfToken(token) {
|
|
166
|
+
if (typeof document === "undefined") return;
|
|
167
|
+
const maxAge = 7 * 24 * 60 * 60;
|
|
168
|
+
document.cookie = `${CSRF_TOKEN_COOKIE}=${token}; path=/; max-age=${maxAge}; SameSite=Lax`;
|
|
169
|
+
}
|
|
170
|
+
function clearCsrfToken() {
|
|
171
|
+
if (typeof document === "undefined") return;
|
|
172
|
+
document.cookie = `${CSRF_TOKEN_COOKIE}=; path=/; max-age=0; SameSite=Lax`;
|
|
173
|
+
}
|
|
174
|
+
var TokenManager = class {
|
|
175
|
+
constructor(storage) {
|
|
176
|
+
// In-memory storage
|
|
224
177
|
this.accessToken = null;
|
|
225
178
|
this.user = null;
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
if (this.accessToken) return false;
|
|
229
|
-
return this.hasAuthFlag();
|
|
230
|
-
}
|
|
231
|
-
// --- Private: Auth Flag Cookie Detection (SDK-managed on frontend domain) ---
|
|
232
|
-
hasAuthFlag() {
|
|
233
|
-
if (typeof document === "undefined") return false;
|
|
234
|
-
return document.cookie.split(";").some(
|
|
235
|
-
(c) => c.trim().startsWith(`${AUTH_FLAG_COOKIE}=`)
|
|
236
|
-
);
|
|
237
|
-
}
|
|
238
|
-
};
|
|
239
|
-
var LocalSessionStorage = class {
|
|
240
|
-
constructor(storage) {
|
|
241
|
-
this.strategyId = "local";
|
|
179
|
+
// Mode: 'memory' (new backend) or 'storage' (legacy backend, default)
|
|
180
|
+
this._mode = "storage";
|
|
242
181
|
if (storage) {
|
|
243
182
|
this.storage = storage;
|
|
244
183
|
} else if (typeof window !== "undefined" && window.localStorage) {
|
|
@@ -256,126 +195,111 @@ var LocalSessionStorage = class {
|
|
|
256
195
|
};
|
|
257
196
|
}
|
|
258
197
|
}
|
|
259
|
-
saveSession(session) {
|
|
260
|
-
this.storage.setItem(TOKEN_KEY, session.accessToken);
|
|
261
|
-
this.storage.setItem(USER_KEY, JSON.stringify(session.user));
|
|
262
|
-
}
|
|
263
|
-
getSession() {
|
|
264
|
-
const token = this.storage.getItem(TOKEN_KEY);
|
|
265
|
-
const userStr = this.storage.getItem(USER_KEY);
|
|
266
|
-
if (!token || !userStr) return null;
|
|
267
|
-
try {
|
|
268
|
-
const user = JSON.parse(userStr);
|
|
269
|
-
return { accessToken: token, user };
|
|
270
|
-
} catch {
|
|
271
|
-
this.clearSession();
|
|
272
|
-
return null;
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
getAccessToken() {
|
|
276
|
-
const token = this.storage.getItem(TOKEN_KEY);
|
|
277
|
-
return typeof token === "string" ? token : null;
|
|
278
|
-
}
|
|
279
|
-
setAccessToken(token) {
|
|
280
|
-
this.storage.setItem(TOKEN_KEY, token);
|
|
281
|
-
}
|
|
282
|
-
getUser() {
|
|
283
|
-
const userStr = this.storage.getItem(USER_KEY);
|
|
284
|
-
if (!userStr) return null;
|
|
285
|
-
try {
|
|
286
|
-
return JSON.parse(userStr);
|
|
287
|
-
} catch {
|
|
288
|
-
return null;
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
setUser(user) {
|
|
292
|
-
this.storage.setItem(USER_KEY, JSON.stringify(user));
|
|
293
|
-
}
|
|
294
|
-
clearSession() {
|
|
295
|
-
this.storage.removeItem(TOKEN_KEY);
|
|
296
|
-
this.storage.removeItem(USER_KEY);
|
|
297
|
-
}
|
|
298
|
-
shouldAttemptRefresh() {
|
|
299
|
-
return false;
|
|
300
|
-
}
|
|
301
|
-
};
|
|
302
|
-
|
|
303
|
-
// src/lib/token-manager.ts
|
|
304
|
-
var TokenManager = class {
|
|
305
198
|
/**
|
|
306
|
-
*
|
|
307
|
-
* @param storage - Optional custom storage adapter (used for initial LocalSessionStorage)
|
|
199
|
+
* Get current mode
|
|
308
200
|
*/
|
|
309
|
-
|
|
310
|
-
this.
|
|
201
|
+
get mode() {
|
|
202
|
+
return this._mode;
|
|
311
203
|
}
|
|
312
204
|
/**
|
|
313
|
-
* Set
|
|
314
|
-
* Called after capability discovery to switch to the appropriate strategy
|
|
205
|
+
* Set mode to memory (new backend with cookies + memory)
|
|
315
206
|
*/
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
if (existingSession && previousId !== strategy.strategyId) {
|
|
321
|
-
strategy.saveSession(existingSession);
|
|
207
|
+
setMemoryMode() {
|
|
208
|
+
if (this._mode === "storage") {
|
|
209
|
+
this.storage.removeItem(TOKEN_KEY);
|
|
210
|
+
this.storage.removeItem(USER_KEY);
|
|
322
211
|
}
|
|
212
|
+
this._mode = "memory";
|
|
323
213
|
}
|
|
324
214
|
/**
|
|
325
|
-
*
|
|
215
|
+
* Set mode to storage (legacy backend with localStorage)
|
|
216
|
+
* Also loads existing session from localStorage
|
|
326
217
|
*/
|
|
327
|
-
|
|
328
|
-
|
|
218
|
+
setStorageMode() {
|
|
219
|
+
this._mode = "storage";
|
|
220
|
+
this.loadFromStorage();
|
|
329
221
|
}
|
|
330
|
-
// --- Delegated Methods ---
|
|
331
222
|
/**
|
|
332
|
-
*
|
|
223
|
+
* Load session from localStorage
|
|
224
|
+
*/
|
|
225
|
+
loadFromStorage() {
|
|
226
|
+
const token = this.storage.getItem(TOKEN_KEY);
|
|
227
|
+
const userStr = this.storage.getItem(USER_KEY);
|
|
228
|
+
if (token && userStr) {
|
|
229
|
+
try {
|
|
230
|
+
this.accessToken = token;
|
|
231
|
+
this.user = JSON.parse(userStr);
|
|
232
|
+
} catch {
|
|
233
|
+
this.clearSession();
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Save session (memory always, localStorage only in storage mode)
|
|
333
239
|
*/
|
|
334
240
|
saveSession(session) {
|
|
335
|
-
this.
|
|
241
|
+
this.accessToken = session.accessToken;
|
|
242
|
+
this.user = session.user;
|
|
243
|
+
if (this._mode === "storage") {
|
|
244
|
+
this.storage.setItem(TOKEN_KEY, session.accessToken);
|
|
245
|
+
this.storage.setItem(USER_KEY, JSON.stringify(session.user));
|
|
246
|
+
}
|
|
336
247
|
}
|
|
337
248
|
/**
|
|
338
249
|
* Get current session
|
|
339
250
|
*/
|
|
340
251
|
getSession() {
|
|
341
|
-
|
|
252
|
+
if (!this.accessToken || !this.user) return null;
|
|
253
|
+
return {
|
|
254
|
+
accessToken: this.accessToken,
|
|
255
|
+
user: this.user
|
|
256
|
+
};
|
|
342
257
|
}
|
|
343
258
|
/**
|
|
344
259
|
* Get access token
|
|
345
260
|
*/
|
|
346
261
|
getAccessToken() {
|
|
347
|
-
return this.
|
|
262
|
+
return this.accessToken;
|
|
348
263
|
}
|
|
349
264
|
/**
|
|
350
|
-
*
|
|
265
|
+
* Set access token
|
|
351
266
|
*/
|
|
352
267
|
setAccessToken(token) {
|
|
353
|
-
this.
|
|
268
|
+
this.accessToken = token;
|
|
269
|
+
if (this._mode === "storage") {
|
|
270
|
+
this.storage.setItem(TOKEN_KEY, token);
|
|
271
|
+
}
|
|
354
272
|
}
|
|
355
273
|
/**
|
|
356
|
-
* Get user
|
|
274
|
+
* Get user
|
|
357
275
|
*/
|
|
358
276
|
getUser() {
|
|
359
|
-
return this.
|
|
277
|
+
return this.user;
|
|
360
278
|
}
|
|
361
279
|
/**
|
|
362
|
-
*
|
|
280
|
+
* Set user
|
|
363
281
|
*/
|
|
364
282
|
setUser(user) {
|
|
365
|
-
this.
|
|
283
|
+
this.user = user;
|
|
284
|
+
if (this._mode === "storage") {
|
|
285
|
+
this.storage.setItem(USER_KEY, JSON.stringify(user));
|
|
286
|
+
}
|
|
366
287
|
}
|
|
367
288
|
/**
|
|
368
|
-
* Clear
|
|
289
|
+
* Clear session (both memory and localStorage)
|
|
369
290
|
*/
|
|
370
291
|
clearSession() {
|
|
371
|
-
this.
|
|
292
|
+
this.accessToken = null;
|
|
293
|
+
this.user = null;
|
|
294
|
+
this.storage.removeItem(TOKEN_KEY);
|
|
295
|
+
this.storage.removeItem(USER_KEY);
|
|
372
296
|
}
|
|
373
297
|
/**
|
|
374
|
-
* Check if
|
|
375
|
-
* (e.g., on page reload in secure mode)
|
|
298
|
+
* Check if there's a session in localStorage (for legacy detection)
|
|
376
299
|
*/
|
|
377
|
-
|
|
378
|
-
|
|
300
|
+
hasStoredSession() {
|
|
301
|
+
const token = this.storage.getItem(TOKEN_KEY);
|
|
302
|
+
return !!token;
|
|
379
303
|
}
|
|
380
304
|
};
|
|
381
305
|
|
|
@@ -492,77 +416,83 @@ var Auth = class {
|
|
|
492
416
|
this.detectAuthCallback();
|
|
493
417
|
}
|
|
494
418
|
/**
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
* Called when backend returns sessionMode: 'secure'
|
|
513
|
-
* @internal
|
|
514
|
-
*/
|
|
515
|
-
_switchToSecureStorage() {
|
|
516
|
-
console.log("[InsForge:Auth] _switchToSecureStorage() called, current strategy:", this.tokenManager.getStrategyId());
|
|
517
|
-
if (this.tokenManager.getStrategyId() === "secure") {
|
|
518
|
-
console.log("[InsForge:Auth] _switchToSecureStorage() - already in secure mode, skipping");
|
|
519
|
-
return;
|
|
419
|
+
* Restore session on app initialization
|
|
420
|
+
*
|
|
421
|
+
* @returns Object with isLoggedIn status
|
|
422
|
+
*
|
|
423
|
+
* @example
|
|
424
|
+
* ```typescript
|
|
425
|
+
* const client = new InsForgeClient({ baseUrl: '...' });
|
|
426
|
+
* const { isLoggedIn } = await client.auth.restoreSession();
|
|
427
|
+
*
|
|
428
|
+
* if (isLoggedIn) {
|
|
429
|
+
* const { data } = await client.auth.getCurrentUser();
|
|
430
|
+
* }
|
|
431
|
+
* ```
|
|
432
|
+
*/
|
|
433
|
+
async restoreSession() {
|
|
434
|
+
if (typeof window === "undefined") {
|
|
435
|
+
return { isLoggedIn: false };
|
|
520
436
|
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
if (typeof localStorage !== "undefined") {
|
|
524
|
-
console.log("[InsForge:Auth] _switchToSecureStorage() - clearing localStorage");
|
|
525
|
-
localStorage.removeItem(TOKEN_KEY);
|
|
526
|
-
localStorage.removeItem(USER_KEY);
|
|
437
|
+
if (this.tokenManager.getAccessToken()) {
|
|
438
|
+
return { isLoggedIn: true };
|
|
527
439
|
}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
440
|
+
if (hasAuthCookie()) {
|
|
441
|
+
try {
|
|
442
|
+
const csrfToken = getCsrfToken();
|
|
443
|
+
const response = await this.http.post(
|
|
444
|
+
"/api/auth/refresh",
|
|
445
|
+
{
|
|
446
|
+
headers: csrfToken ? { "X-CSRF-Token": csrfToken } : {}
|
|
447
|
+
}
|
|
448
|
+
);
|
|
449
|
+
if (response.accessToken) {
|
|
450
|
+
this.tokenManager.setMemoryMode();
|
|
451
|
+
this.tokenManager.setAccessToken(response.accessToken);
|
|
452
|
+
this.http.setAuthToken(response.accessToken);
|
|
453
|
+
if (response.user) {
|
|
454
|
+
this.tokenManager.setUser(response.user);
|
|
455
|
+
}
|
|
456
|
+
if (response.csrfToken) {
|
|
457
|
+
setCsrfToken(response.csrfToken);
|
|
458
|
+
}
|
|
459
|
+
return { isLoggedIn: true };
|
|
460
|
+
}
|
|
461
|
+
} catch (error) {
|
|
462
|
+
if (error instanceof InsForgeError) {
|
|
463
|
+
if (error.statusCode === 404) {
|
|
464
|
+
this.tokenManager.setStorageMode();
|
|
465
|
+
const token = this.tokenManager.getAccessToken();
|
|
466
|
+
if (token) {
|
|
467
|
+
this.http.setAuthToken(token);
|
|
468
|
+
return { isLoggedIn: true };
|
|
469
|
+
}
|
|
470
|
+
return { isLoggedIn: false };
|
|
471
|
+
}
|
|
472
|
+
if (error.statusCode === 401 || error.statusCode === 403) {
|
|
473
|
+
clearAuthCookie();
|
|
474
|
+
clearCsrfToken();
|
|
475
|
+
return { isLoggedIn: false };
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
return { isLoggedIn: false };
|
|
479
|
+
}
|
|
532
480
|
}
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
if (this.tokenManager.getStrategyId() === "local") return;
|
|
541
|
-
const currentSession = this.tokenManager.getSession();
|
|
542
|
-
this.tokenManager.setStrategy(new LocalSessionStorage());
|
|
543
|
-
this.clearAuthenticatedCookie();
|
|
544
|
-
if (currentSession) {
|
|
545
|
-
this.tokenManager.saveSession(currentSession);
|
|
481
|
+
if (this.tokenManager.hasStoredSession()) {
|
|
482
|
+
this.tokenManager.setStorageMode();
|
|
483
|
+
const token = this.tokenManager.getAccessToken();
|
|
484
|
+
if (token) {
|
|
485
|
+
this.http.setAuthToken(token);
|
|
486
|
+
return { isLoggedIn: true };
|
|
487
|
+
}
|
|
546
488
|
}
|
|
489
|
+
return { isLoggedIn: false };
|
|
547
490
|
}
|
|
548
491
|
/**
|
|
549
|
-
*
|
|
550
|
-
*
|
|
551
|
-
*
|
|
492
|
+
* Automatically detect and handle OAuth callback parameters in the URL
|
|
493
|
+
* This runs on initialization to seamlessly complete the OAuth flow
|
|
494
|
+
* Matches the backend's OAuth callback response (backend/src/api/routes/auth.ts:540-544)
|
|
552
495
|
*/
|
|
553
|
-
_detectStorageFromResponse(sessionMode) {
|
|
554
|
-
console.log("[InsForge:Auth] _detectStorageFromResponse() - sessionMode:", sessionMode);
|
|
555
|
-
if (sessionMode === "secure") {
|
|
556
|
-
this._switchToSecureStorage();
|
|
557
|
-
} else {
|
|
558
|
-
this._switchToLocalStorage();
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
/**
|
|
562
|
-
* Automatically detect and handle OAuth callback parameters in the URL
|
|
563
|
-
* This runs on initialization to seamlessly complete the OAuth flow
|
|
564
|
-
* Matches the backend's OAuth callback response (backend/src/api/routes/auth.ts:540-544)
|
|
565
|
-
*/
|
|
566
496
|
detectAuthCallback() {
|
|
567
497
|
if (typeof window === "undefined") return;
|
|
568
498
|
try {
|
|
@@ -571,9 +501,8 @@ var Auth = class {
|
|
|
571
501
|
const userId = params.get("user_id");
|
|
572
502
|
const email = params.get("email");
|
|
573
503
|
const name = params.get("name");
|
|
574
|
-
const
|
|
504
|
+
const csrfToken = params.get("csrf_token");
|
|
575
505
|
if (accessToken && userId && email) {
|
|
576
|
-
this._detectStorageFromResponse(sessionMode || void 0);
|
|
577
506
|
const session = {
|
|
578
507
|
accessToken,
|
|
579
508
|
user: {
|
|
@@ -587,14 +516,18 @@ var Auth = class {
|
|
|
587
516
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
588
517
|
}
|
|
589
518
|
};
|
|
590
|
-
this.tokenManager.saveSession(session);
|
|
591
519
|
this.http.setAuthToken(accessToken);
|
|
520
|
+
this.tokenManager.saveSession(session);
|
|
521
|
+
setAuthCookie();
|
|
522
|
+
if (csrfToken) {
|
|
523
|
+
setCsrfToken(csrfToken);
|
|
524
|
+
}
|
|
592
525
|
const url = new URL(window.location.href);
|
|
593
526
|
url.searchParams.delete("access_token");
|
|
594
527
|
url.searchParams.delete("user_id");
|
|
595
528
|
url.searchParams.delete("email");
|
|
596
529
|
url.searchParams.delete("name");
|
|
597
|
-
url.searchParams.delete("
|
|
530
|
+
url.searchParams.delete("csrf_token");
|
|
598
531
|
if (params.has("error")) {
|
|
599
532
|
url.searchParams.delete("error");
|
|
600
533
|
}
|
|
@@ -610,17 +543,17 @@ var Auth = class {
|
|
|
610
543
|
async signUp(request) {
|
|
611
544
|
try {
|
|
612
545
|
const response = await this.http.post("/api/auth/users", request);
|
|
613
|
-
|
|
614
|
-
this._detectStorageFromResponse(sessionMode);
|
|
615
|
-
if (response.accessToken && response.user) {
|
|
546
|
+
if (response.accessToken && response.user && !isHostedAuthEnvironment()) {
|
|
616
547
|
const session = {
|
|
617
548
|
accessToken: response.accessToken,
|
|
618
549
|
user: response.user
|
|
619
550
|
};
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
}
|
|
551
|
+
this.tokenManager.saveSession(session);
|
|
552
|
+
setAuthCookie();
|
|
623
553
|
this.http.setAuthToken(response.accessToken);
|
|
554
|
+
if (response.csrfToken) {
|
|
555
|
+
setCsrfToken(response.csrfToken);
|
|
556
|
+
}
|
|
624
557
|
}
|
|
625
558
|
return {
|
|
626
559
|
data: response,
|
|
@@ -646,23 +579,18 @@ var Auth = class {
|
|
|
646
579
|
async signInWithPassword(request) {
|
|
647
580
|
try {
|
|
648
581
|
const response = await this.http.post("/api/auth/sessions", request);
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
id: "",
|
|
655
|
-
email: "",
|
|
656
|
-
name: "",
|
|
657
|
-
emailVerified: false,
|
|
658
|
-
createdAt: "",
|
|
659
|
-
updatedAt: ""
|
|
660
|
-
}
|
|
661
|
-
};
|
|
662
|
-
if (!isHostedAuthEnvironment()) {
|
|
582
|
+
if (response.accessToken && response.user && !isHostedAuthEnvironment()) {
|
|
583
|
+
const session = {
|
|
584
|
+
accessToken: response.accessToken,
|
|
585
|
+
user: response.user
|
|
586
|
+
};
|
|
663
587
|
this.tokenManager.saveSession(session);
|
|
588
|
+
setAuthCookie();
|
|
589
|
+
this.http.setAuthToken(response.accessToken);
|
|
590
|
+
if (response.csrfToken) {
|
|
591
|
+
setCsrfToken(response.csrfToken);
|
|
592
|
+
}
|
|
664
593
|
}
|
|
665
|
-
this.http.setAuthToken(response.accessToken || "");
|
|
666
594
|
return {
|
|
667
595
|
data: response,
|
|
668
596
|
error: null
|
|
@@ -717,28 +645,19 @@ var Auth = class {
|
|
|
717
645
|
}
|
|
718
646
|
/**
|
|
719
647
|
* Sign out the current user
|
|
720
|
-
* In modern mode, also calls backend to clear the refresh token cookie
|
|
721
648
|
*/
|
|
722
649
|
async signOut() {
|
|
723
|
-
console.log("[InsForge:Auth] signOut() called");
|
|
724
|
-
console.log("[InsForge:Auth] signOut() stack trace:", new Error().stack);
|
|
725
650
|
try {
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
await this.http.post("/api/auth/logout");
|
|
730
|
-
console.log("[InsForge:Auth] signOut() - backend logout successful");
|
|
731
|
-
} catch (e) {
|
|
732
|
-
console.log("[InsForge:Auth] signOut() - backend logout failed (ignored):", e);
|
|
733
|
-
}
|
|
651
|
+
try {
|
|
652
|
+
await this.http.post("/api/auth/logout");
|
|
653
|
+
} catch {
|
|
734
654
|
}
|
|
735
655
|
this.tokenManager.clearSession();
|
|
736
656
|
this.http.setAuthToken(null);
|
|
737
|
-
|
|
738
|
-
|
|
657
|
+
clearAuthCookie();
|
|
658
|
+
clearCsrfToken();
|
|
739
659
|
return { error: null };
|
|
740
660
|
} catch (error) {
|
|
741
|
-
console.error("[InsForge:Auth] signOut() - error:", error);
|
|
742
661
|
return {
|
|
743
662
|
error: new InsForgeError(
|
|
744
663
|
"Failed to sign out",
|
|
@@ -748,58 +667,6 @@ var Auth = class {
|
|
|
748
667
|
};
|
|
749
668
|
}
|
|
750
669
|
}
|
|
751
|
-
/**
|
|
752
|
-
* Refresh the access token using the httpOnly refresh token cookie
|
|
753
|
-
* Only works when backend supports secure session storage (httpOnly cookies)
|
|
754
|
-
*
|
|
755
|
-
* @returns New access token or throws an error
|
|
756
|
-
*/
|
|
757
|
-
async refreshToken() {
|
|
758
|
-
console.log("[InsForge:Auth] refreshToken() called");
|
|
759
|
-
try {
|
|
760
|
-
const response = await this.http.post(
|
|
761
|
-
"/api/auth/refresh"
|
|
762
|
-
);
|
|
763
|
-
console.log("[InsForge:Auth] refreshToken() - response received, hasAccessToken:", !!response.accessToken);
|
|
764
|
-
if (response.accessToken) {
|
|
765
|
-
this._detectStorageFromResponse(response.sessionMode);
|
|
766
|
-
this.tokenManager.setAccessToken(response.accessToken);
|
|
767
|
-
this.http.setAuthToken(response.accessToken);
|
|
768
|
-
if (response.user) {
|
|
769
|
-
this.tokenManager.setUser(response.user);
|
|
770
|
-
}
|
|
771
|
-
console.log("[InsForge:Auth] refreshToken() - success");
|
|
772
|
-
return response.accessToken;
|
|
773
|
-
}
|
|
774
|
-
throw new InsForgeError(
|
|
775
|
-
"No access token in refresh response",
|
|
776
|
-
500,
|
|
777
|
-
"REFRESH_FAILED"
|
|
778
|
-
);
|
|
779
|
-
} catch (error) {
|
|
780
|
-
console.error("[InsForge:Auth] refreshToken() - error:", error);
|
|
781
|
-
if (error instanceof InsForgeError) {
|
|
782
|
-
if (error.statusCode === 404) {
|
|
783
|
-
console.log("[InsForge:Auth] refreshToken() - 404 detected, backend does not support refresh endpoint");
|
|
784
|
-
console.log("[InsForge:Auth] refreshToken() - switching to LocalSessionStorage for backward compatibility");
|
|
785
|
-
this._switchToLocalStorage();
|
|
786
|
-
this.clearAuthenticatedCookie();
|
|
787
|
-
}
|
|
788
|
-
if (error.statusCode === 401 || error.statusCode === 403) {
|
|
789
|
-
console.log("[InsForge:Auth] refreshToken() - clearing session due to 401/403");
|
|
790
|
-
this.tokenManager.clearSession();
|
|
791
|
-
this.http.setAuthToken(null);
|
|
792
|
-
this.clearAuthenticatedCookie();
|
|
793
|
-
}
|
|
794
|
-
throw error;
|
|
795
|
-
}
|
|
796
|
-
throw new InsForgeError(
|
|
797
|
-
"Token refresh failed",
|
|
798
|
-
500,
|
|
799
|
-
"REFRESH_FAILED"
|
|
800
|
-
);
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
670
|
/**
|
|
804
671
|
* Get all public authentication configuration (OAuth + Email)
|
|
805
672
|
* Returns both OAuth providers and email authentication settings in one request
|
|
@@ -840,40 +707,19 @@ var Auth = class {
|
|
|
840
707
|
/**
|
|
841
708
|
* Get the current user with full profile information
|
|
842
709
|
* Returns both auth info (id, email, role) and profile data (dynamic fields from users table)
|
|
843
|
-
*
|
|
844
|
-
* In secure session mode (httpOnly cookie), this method will automatically attempt
|
|
845
|
-
* to refresh the session if no access token is available (e.g., after page reload).
|
|
846
710
|
*/
|
|
847
711
|
async getCurrentUser() {
|
|
848
|
-
console.log("[InsForge:Auth] getCurrentUser() called");
|
|
849
712
|
try {
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
console.log("[InsForge:Auth] getCurrentUser() - hasAccessToken:", !!accessToken, "shouldAttemptRefresh:", shouldRefresh);
|
|
853
|
-
if (!accessToken && shouldRefresh) {
|
|
854
|
-
console.log("[InsForge:Auth] getCurrentUser() - attempting refresh");
|
|
855
|
-
try {
|
|
856
|
-
accessToken = await this.refreshToken();
|
|
857
|
-
} catch (error) {
|
|
858
|
-
console.log("[InsForge:Auth] getCurrentUser() - refresh failed:", error);
|
|
859
|
-
if (error instanceof InsForgeError && (error.statusCode === 401 || error.statusCode === 403)) {
|
|
860
|
-
return { data: null, error };
|
|
861
|
-
}
|
|
862
|
-
return { data: null, error: error instanceof InsForgeError ? error : new InsForgeError("Token refresh failed", 500, "REFRESH_FAILED") };
|
|
863
|
-
}
|
|
864
|
-
}
|
|
865
|
-
if (!accessToken) {
|
|
866
|
-
console.log("[InsForge:Auth] getCurrentUser() - no access token, returning null");
|
|
713
|
+
const session = this.tokenManager.getSession();
|
|
714
|
+
if (!session?.accessToken) {
|
|
867
715
|
return { data: null, error: null };
|
|
868
716
|
}
|
|
869
|
-
this.http.setAuthToken(accessToken);
|
|
870
|
-
console.log("[InsForge:Auth] getCurrentUser() - fetching user from API");
|
|
717
|
+
this.http.setAuthToken(session.accessToken);
|
|
871
718
|
const authResponse = await this.http.get("/api/auth/sessions/current");
|
|
872
719
|
const { data: profile, error: profileError } = await this.database.from("users").select("*").eq("id", authResponse.user.id).single();
|
|
873
720
|
if (profileError && profileError.code !== "PGRST116") {
|
|
874
721
|
return { data: null, error: profileError };
|
|
875
722
|
}
|
|
876
|
-
console.log("[InsForge:Auth] getCurrentUser() - success");
|
|
877
723
|
return {
|
|
878
724
|
data: {
|
|
879
725
|
user: authResponse.user,
|
|
@@ -882,12 +728,8 @@ var Auth = class {
|
|
|
882
728
|
error: null
|
|
883
729
|
};
|
|
884
730
|
} catch (error) {
|
|
885
|
-
console.error("[InsForge:Auth] getCurrentUser() - catch error:", error);
|
|
886
731
|
if (error instanceof InsForgeError && error.statusCode === 401) {
|
|
887
|
-
|
|
888
|
-
this.tokenManager.clearSession();
|
|
889
|
-
this.http.setAuthToken(null);
|
|
890
|
-
this.clearAuthenticatedCookie();
|
|
732
|
+
await this.signOut();
|
|
891
733
|
return { data: null, error: null };
|
|
892
734
|
}
|
|
893
735
|
if (error instanceof InsForgeError) {
|
|
@@ -1135,15 +977,17 @@ var Auth = class {
|
|
|
1135
977
|
"/api/auth/email/verify",
|
|
1136
978
|
request
|
|
1137
979
|
);
|
|
1138
|
-
|
|
1139
|
-
this._detectStorageFromResponse(sessionMode);
|
|
1140
|
-
if (response.accessToken) {
|
|
980
|
+
if (response.accessToken && !isHostedAuthEnvironment()) {
|
|
1141
981
|
const session = {
|
|
1142
982
|
accessToken: response.accessToken,
|
|
1143
983
|
user: response.user || {}
|
|
1144
984
|
};
|
|
1145
985
|
this.tokenManager.saveSession(session);
|
|
1146
986
|
this.http.setAuthToken(response.accessToken);
|
|
987
|
+
setAuthCookie();
|
|
988
|
+
if (response.csrfToken) {
|
|
989
|
+
setCsrfToken(response.csrfToken);
|
|
990
|
+
}
|
|
1147
991
|
}
|
|
1148
992
|
return {
|
|
1149
993
|
data: response,
|
|
@@ -1687,24 +1531,10 @@ var Functions = class {
|
|
|
1687
1531
|
};
|
|
1688
1532
|
|
|
1689
1533
|
// src/client.ts
|
|
1690
|
-
function hasAuthenticatedCookie() {
|
|
1691
|
-
if (typeof document === "undefined") return false;
|
|
1692
|
-
return document.cookie.split(";").some(
|
|
1693
|
-
(c) => c.trim().startsWith(`${AUTH_FLAG_COOKIE}=`)
|
|
1694
|
-
);
|
|
1695
|
-
}
|
|
1696
1534
|
var InsForgeClient = class {
|
|
1697
1535
|
constructor(config = {}) {
|
|
1698
|
-
console.log("[InsForge:Client] Initializing SDK");
|
|
1699
1536
|
this.http = new HttpClient(config);
|
|
1700
1537
|
this.tokenManager = new TokenManager(config.storage);
|
|
1701
|
-
const hasAuthCookie = hasAuthenticatedCookie();
|
|
1702
|
-
console.log("[InsForge:Client] hasAuthenticatedCookie:", hasAuthCookie);
|
|
1703
|
-
console.log("[InsForge:Client] document.cookie:", typeof document !== "undefined" ? document.cookie : "N/A (SSR)");
|
|
1704
|
-
if (hasAuthCookie) {
|
|
1705
|
-
console.log("[InsForge:Client] Switching to SecureSessionStorage");
|
|
1706
|
-
this.tokenManager.setStrategy(new SecureSessionStorage());
|
|
1707
|
-
}
|
|
1708
1538
|
if (config.edgeFunctionToken) {
|
|
1709
1539
|
this.http.setAuthToken(config.edgeFunctionToken);
|
|
1710
1540
|
this.tokenManager.saveSession({
|
|
@@ -1713,32 +1543,15 @@ var InsForgeClient = class {
|
|
|
1713
1543
|
// Will be populated by getCurrentUser()
|
|
1714
1544
|
});
|
|
1715
1545
|
}
|
|
1716
|
-
this.http.setRefreshCallback(async () => {
|
|
1717
|
-
console.log("[InsForge:Client] HTTP 401 refresh callback triggered");
|
|
1718
|
-
try {
|
|
1719
|
-
return await this.auth.refreshToken();
|
|
1720
|
-
} catch (e) {
|
|
1721
|
-
console.log("[InsForge:Client] Refresh callback failed:", e);
|
|
1722
|
-
if (this.tokenManager.getStrategyId() === "secure") {
|
|
1723
|
-
console.log("[InsForge:Client] Falling back to LocalSessionStorage");
|
|
1724
|
-
this.auth._switchToLocalStorage();
|
|
1725
|
-
}
|
|
1726
|
-
return null;
|
|
1727
|
-
}
|
|
1728
|
-
});
|
|
1729
1546
|
const existingSession = this.tokenManager.getSession();
|
|
1730
|
-
console.log("[InsForge:Client] existingSession:", !!existingSession, "strategyId:", this.tokenManager.getStrategyId());
|
|
1731
1547
|
if (existingSession?.accessToken) {
|
|
1732
1548
|
this.http.setAuthToken(existingSession.accessToken);
|
|
1733
|
-
} else if (this.tokenManager.getStrategyId() === "secure") {
|
|
1734
|
-
console.log("[InsForge:Client] Secure mode, no session in memory - will refresh on first API call");
|
|
1735
1549
|
}
|
|
1736
1550
|
this.auth = new Auth(this.http, this.tokenManager);
|
|
1737
1551
|
this.database = new Database(this.http, this.tokenManager);
|
|
1738
1552
|
this.storage = new Storage(this.http);
|
|
1739
1553
|
this.ai = new AI(this.http);
|
|
1740
1554
|
this.functions = new Functions(this.http);
|
|
1741
|
-
console.log("[InsForge:Client] SDK initialized");
|
|
1742
1555
|
}
|
|
1743
1556
|
/**
|
|
1744
1557
|
* Get the underlying HTTP client for custom requests
|
|
@@ -1752,12 +1565,6 @@ var InsForgeClient = class {
|
|
|
1752
1565
|
getHttpClient() {
|
|
1753
1566
|
return this.http;
|
|
1754
1567
|
}
|
|
1755
|
-
/**
|
|
1756
|
-
* Get the current storage strategy identifier
|
|
1757
|
-
*/
|
|
1758
|
-
getStorageStrategy() {
|
|
1759
|
-
return this.tokenManager.getStrategyId();
|
|
1760
|
-
}
|
|
1761
1568
|
/**
|
|
1762
1569
|
* Future modules will be added here:
|
|
1763
1570
|
* - database: Database operations
|
|
@@ -1781,8 +1588,6 @@ export {
|
|
|
1781
1588
|
HttpClient,
|
|
1782
1589
|
InsForgeClient,
|
|
1783
1590
|
InsForgeError,
|
|
1784
|
-
LocalSessionStorage,
|
|
1785
|
-
SecureSessionStorage,
|
|
1786
1591
|
Storage,
|
|
1787
1592
|
StorageBucket,
|
|
1788
1593
|
TokenManager,
|