@tiquo/dom-package 1.0.0 → 1.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 +76 -6
- package/dist/index.d.mts +64 -29
- package/dist/index.d.ts +64 -29
- package/dist/index.js +274 -103
- package/dist/index.mjs +274 -103
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -34,12 +34,32 @@ var TiquoAuthError = class extends Error {
|
|
|
34
34
|
this.name = "TiquoAuthError";
|
|
35
35
|
}
|
|
36
36
|
};
|
|
37
|
+
function decodeJWT(token) {
|
|
38
|
+
try {
|
|
39
|
+
const parts = token.split(".");
|
|
40
|
+
if (parts.length !== 3) return null;
|
|
41
|
+
const payload = parts[1];
|
|
42
|
+
const decoded = atob(payload.replace(/-/g, "+").replace(/_/g, "/"));
|
|
43
|
+
return JSON.parse(decoded);
|
|
44
|
+
} catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function isTokenExpired(token, bufferSeconds = 300) {
|
|
49
|
+
const payload = decodeJWT(token);
|
|
50
|
+
if (!payload) return true;
|
|
51
|
+
const expiresAt = payload.exp * 1e3;
|
|
52
|
+
const bufferMs = bufferSeconds * 1e3;
|
|
53
|
+
return Date.now() > expiresAt - bufferMs;
|
|
54
|
+
}
|
|
37
55
|
var TiquoAuth = class {
|
|
38
56
|
constructor(config) {
|
|
39
|
-
this.
|
|
57
|
+
this.accessToken = null;
|
|
58
|
+
this.refreshToken = null;
|
|
40
59
|
this.session = null;
|
|
41
60
|
this.listeners = /* @__PURE__ */ new Set();
|
|
42
61
|
this.refreshTimer = null;
|
|
62
|
+
this.isRefreshing = false;
|
|
43
63
|
// Multi-tab sync
|
|
44
64
|
this.broadcastChannel = null;
|
|
45
65
|
this.isProcessingTabSync = false;
|
|
@@ -57,14 +77,17 @@ var TiquoAuth = class {
|
|
|
57
77
|
apiEndpoint: config.apiEndpoint || "https://edge.tiquo.app",
|
|
58
78
|
storagePrefix: config.storagePrefix || "tiquo_auth_",
|
|
59
79
|
debug: config.debug || false,
|
|
60
|
-
enableTabSync: config.enableTabSync !== false
|
|
80
|
+
enableTabSync: config.enableTabSync !== false,
|
|
61
81
|
// Default true
|
|
82
|
+
accessToken: config.accessToken,
|
|
83
|
+
refreshToken: config.refreshToken
|
|
62
84
|
};
|
|
63
85
|
this.tabId = this.generateTabId();
|
|
64
86
|
if (this.config.enableTabSync) {
|
|
65
87
|
this.initTabSync();
|
|
66
88
|
}
|
|
67
|
-
this.
|
|
89
|
+
this.checkForInjectedTokens();
|
|
90
|
+
this.restoreTokens();
|
|
68
91
|
}
|
|
69
92
|
// ============================================
|
|
70
93
|
// PUBLIC METHODS
|
|
@@ -109,13 +132,16 @@ var TiquoAuth = class {
|
|
|
109
132
|
throw new TiquoAuthError(error.error || "Invalid OTP", "OTP_VERIFY_FAILED", response.status);
|
|
110
133
|
}
|
|
111
134
|
const result = await response.json();
|
|
112
|
-
this.
|
|
113
|
-
this.
|
|
135
|
+
this.accessToken = result.accessToken;
|
|
136
|
+
this.refreshToken = result.refreshToken;
|
|
137
|
+
this.saveTokens();
|
|
114
138
|
await this.refreshSession();
|
|
115
139
|
this.broadcastTabSync("LOGIN");
|
|
116
140
|
return {
|
|
117
141
|
success: true,
|
|
118
|
-
|
|
142
|
+
accessToken: result.accessToken,
|
|
143
|
+
refreshToken: result.refreshToken,
|
|
144
|
+
expiresIn: result.expiresIn,
|
|
119
145
|
expiresAt: result.expiresAt,
|
|
120
146
|
isNewUser: result.isNewUser,
|
|
121
147
|
hasCustomer: result.hasCustomer
|
|
@@ -125,10 +151,11 @@ var TiquoAuth = class {
|
|
|
125
151
|
* Get the current authenticated user and customer data
|
|
126
152
|
*/
|
|
127
153
|
async getUser() {
|
|
128
|
-
if (!this.
|
|
154
|
+
if (!this.accessToken) {
|
|
129
155
|
return null;
|
|
130
156
|
}
|
|
131
|
-
|
|
157
|
+
await this.refreshTokenIfNeeded();
|
|
158
|
+
if (this.session) {
|
|
132
159
|
return this.session;
|
|
133
160
|
}
|
|
134
161
|
return this.refreshSession();
|
|
@@ -137,60 +164,57 @@ var TiquoAuth = class {
|
|
|
137
164
|
* Check if user is currently authenticated
|
|
138
165
|
*/
|
|
139
166
|
isAuthenticated() {
|
|
140
|
-
return !!this.
|
|
167
|
+
return !!this.accessToken && !isTokenExpired(this.accessToken, 0);
|
|
141
168
|
}
|
|
142
169
|
/**
|
|
143
170
|
* Update the authenticated customer's profile
|
|
144
171
|
* Only allows updating the logged-in customer's own data
|
|
145
172
|
*/
|
|
146
173
|
async updateProfile(updates) {
|
|
147
|
-
|
|
148
|
-
throw new TiquoAuthError("Not authenticated", "NOT_AUTHENTICATED");
|
|
149
|
-
}
|
|
174
|
+
await this.ensureValidToken();
|
|
150
175
|
this.log("Updating customer profile:", updates);
|
|
151
|
-
const response = await this.request("/api/
|
|
176
|
+
const response = await this.request("/api/client/v1/profile", {
|
|
152
177
|
method: "PATCH",
|
|
153
|
-
body: JSON.stringify(
|
|
154
|
-
publicKey: this.config.publicKey,
|
|
155
|
-
sessionToken: this.sessionToken,
|
|
156
|
-
updates
|
|
157
|
-
})
|
|
178
|
+
body: JSON.stringify(updates)
|
|
158
179
|
});
|
|
159
180
|
if (!response.ok) {
|
|
160
181
|
const error = await response.json().catch(() => ({ error: "Failed to update profile" }));
|
|
161
182
|
throw new TiquoAuthError(error.error || "Failed to update profile", "PROFILE_UPDATE_FAILED", response.status);
|
|
162
183
|
}
|
|
163
184
|
const result = await response.json();
|
|
164
|
-
if (this.session && result.customer) {
|
|
185
|
+
if (this.session && result.data?.customer) {
|
|
165
186
|
this.session = {
|
|
166
187
|
...this.session,
|
|
167
188
|
customer: {
|
|
168
|
-
id: result.customer.id,
|
|
169
|
-
firstName: result.customer.firstName,
|
|
170
|
-
lastName: result.customer.lastName,
|
|
171
|
-
displayName: result.customer.displayName,
|
|
172
|
-
customerNumber: result.customer.customerNumber,
|
|
173
|
-
email: result.customer.email,
|
|
174
|
-
phone: result.customer.phone
|
|
189
|
+
id: result.data.customer.id,
|
|
190
|
+
firstName: result.data.customer.firstName,
|
|
191
|
+
lastName: result.data.customer.lastName,
|
|
192
|
+
displayName: result.data.customer.displayName,
|
|
193
|
+
customerNumber: result.data.customer.customerNumber,
|
|
194
|
+
email: result.data.customer.email,
|
|
195
|
+
phone: result.data.customer.phone
|
|
175
196
|
}
|
|
176
197
|
};
|
|
177
198
|
this.notifyListeners();
|
|
178
199
|
this.broadcastTabSync("SESSION_UPDATE");
|
|
179
200
|
}
|
|
180
|
-
return
|
|
201
|
+
return {
|
|
202
|
+
success: true,
|
|
203
|
+
customer: result.data?.customer
|
|
204
|
+
};
|
|
181
205
|
}
|
|
182
206
|
/**
|
|
183
207
|
* Log out the current user
|
|
184
208
|
*/
|
|
185
209
|
async logout() {
|
|
186
210
|
this.log("Logging out");
|
|
187
|
-
if (this.
|
|
211
|
+
if (this.refreshToken) {
|
|
188
212
|
try {
|
|
189
213
|
await this.request("/api/auth-dom/logout", {
|
|
190
214
|
method: "POST",
|
|
191
215
|
body: JSON.stringify({
|
|
192
216
|
publicKey: this.config.publicKey,
|
|
193
|
-
|
|
217
|
+
refreshToken: this.refreshToken
|
|
194
218
|
})
|
|
195
219
|
});
|
|
196
220
|
} catch (error) {
|
|
@@ -198,7 +222,7 @@ var TiquoAuth = class {
|
|
|
198
222
|
}
|
|
199
223
|
}
|
|
200
224
|
this.broadcastTabSync("LOGOUT");
|
|
201
|
-
this.
|
|
225
|
+
this.clearTokens();
|
|
202
226
|
}
|
|
203
227
|
/**
|
|
204
228
|
* Subscribe to authentication state changes
|
|
@@ -215,12 +239,9 @@ var TiquoAuth = class {
|
|
|
215
239
|
* Only returns orders for the logged-in customer
|
|
216
240
|
*/
|
|
217
241
|
async getOrders(options) {
|
|
218
|
-
|
|
219
|
-
throw new TiquoAuthError("Not authenticated", "NOT_AUTHENTICATED");
|
|
220
|
-
}
|
|
242
|
+
await this.ensureValidToken();
|
|
221
243
|
this.log("Fetching customer orders:", options);
|
|
222
|
-
const url = new URL(`${this.config.apiEndpoint}/api/
|
|
223
|
-
url.searchParams.set("publicKey", this.config.publicKey);
|
|
244
|
+
const url = new URL(`${this.config.apiEndpoint}/api/client/v1/orders`);
|
|
224
245
|
if (options?.limit) {
|
|
225
246
|
url.searchParams.set("limit", options.limit.toString());
|
|
226
247
|
}
|
|
@@ -233,7 +254,7 @@ var TiquoAuth = class {
|
|
|
233
254
|
const response = await fetch(url.toString(), {
|
|
234
255
|
method: "GET",
|
|
235
256
|
headers: {
|
|
236
|
-
"Authorization": `Bearer ${this.
|
|
257
|
+
"Authorization": `Bearer ${this.accessToken}`
|
|
237
258
|
},
|
|
238
259
|
credentials: "include"
|
|
239
260
|
});
|
|
@@ -241,19 +262,17 @@ var TiquoAuth = class {
|
|
|
241
262
|
const error = await response.json().catch(() => ({ error: "Failed to get orders" }));
|
|
242
263
|
throw new TiquoAuthError(error.error || "Failed to get orders", "GET_ORDERS_FAILED", response.status);
|
|
243
264
|
}
|
|
244
|
-
|
|
265
|
+
const result = await response.json();
|
|
266
|
+
return result.data || { orders: [], hasMore: false };
|
|
245
267
|
}
|
|
246
268
|
/**
|
|
247
269
|
* Get the authenticated customer's booking history
|
|
248
270
|
* Only returns bookings for the logged-in customer
|
|
249
271
|
*/
|
|
250
272
|
async getBookings(options) {
|
|
251
|
-
|
|
252
|
-
throw new TiquoAuthError("Not authenticated", "NOT_AUTHENTICATED");
|
|
253
|
-
}
|
|
273
|
+
await this.ensureValidToken();
|
|
254
274
|
this.log("Fetching customer bookings:", options);
|
|
255
|
-
const url = new URL(`${this.config.apiEndpoint}/api/
|
|
256
|
-
url.searchParams.set("publicKey", this.config.publicKey);
|
|
275
|
+
const url = new URL(`${this.config.apiEndpoint}/api/client/v1/bookings`);
|
|
257
276
|
if (options?.limit) {
|
|
258
277
|
url.searchParams.set("limit", options.limit.toString());
|
|
259
278
|
}
|
|
@@ -269,7 +288,7 @@ var TiquoAuth = class {
|
|
|
269
288
|
const response = await fetch(url.toString(), {
|
|
270
289
|
method: "GET",
|
|
271
290
|
headers: {
|
|
272
|
-
"Authorization": `Bearer ${this.
|
|
291
|
+
"Authorization": `Bearer ${this.accessToken}`
|
|
273
292
|
},
|
|
274
293
|
credentials: "include"
|
|
275
294
|
});
|
|
@@ -277,19 +296,17 @@ var TiquoAuth = class {
|
|
|
277
296
|
const error = await response.json().catch(() => ({ error: "Failed to get bookings" }));
|
|
278
297
|
throw new TiquoAuthError(error.error || "Failed to get bookings", "GET_BOOKINGS_FAILED", response.status);
|
|
279
298
|
}
|
|
280
|
-
|
|
299
|
+
const result = await response.json();
|
|
300
|
+
return result.data || { bookings: [], hasMore: false };
|
|
281
301
|
}
|
|
282
302
|
/**
|
|
283
303
|
* Get the authenticated customer's enquiry history
|
|
284
304
|
* Only returns enquiries for the logged-in customer
|
|
285
305
|
*/
|
|
286
306
|
async getEnquiries(options) {
|
|
287
|
-
|
|
288
|
-
throw new TiquoAuthError("Not authenticated", "NOT_AUTHENTICATED");
|
|
289
|
-
}
|
|
307
|
+
await this.ensureValidToken();
|
|
290
308
|
this.log("Fetching customer enquiries:", options);
|
|
291
|
-
const url = new URL(`${this.config.apiEndpoint}/api/
|
|
292
|
-
url.searchParams.set("publicKey", this.config.publicKey);
|
|
309
|
+
const url = new URL(`${this.config.apiEndpoint}/api/client/v1/enquiries`);
|
|
293
310
|
if (options?.limit) {
|
|
294
311
|
url.searchParams.set("limit", options.limit.toString());
|
|
295
312
|
}
|
|
@@ -302,7 +319,7 @@ var TiquoAuth = class {
|
|
|
302
319
|
const response = await fetch(url.toString(), {
|
|
303
320
|
method: "GET",
|
|
304
321
|
headers: {
|
|
305
|
-
"Authorization": `Bearer ${this.
|
|
322
|
+
"Authorization": `Bearer ${this.accessToken}`
|
|
306
323
|
},
|
|
307
324
|
credentials: "include"
|
|
308
325
|
});
|
|
@@ -310,21 +327,21 @@ var TiquoAuth = class {
|
|
|
310
327
|
const error = await response.json().catch(() => ({ error: "Failed to get enquiries" }));
|
|
311
328
|
throw new TiquoAuthError(error.error || "Failed to get enquiries", "GET_ENQUIRIES_FAILED", response.status);
|
|
312
329
|
}
|
|
313
|
-
|
|
330
|
+
const result = await response.json();
|
|
331
|
+
return result.data || { enquiries: [], hasMore: false };
|
|
314
332
|
}
|
|
315
333
|
/**
|
|
316
334
|
* Generate a short-lived token for customer flow iframe authentication
|
|
317
335
|
*/
|
|
318
336
|
async getIframeToken(customerFlowId) {
|
|
319
|
-
|
|
320
|
-
throw new TiquoAuthError("Not authenticated", "NOT_AUTHENTICATED");
|
|
321
|
-
}
|
|
337
|
+
await this.ensureValidToken();
|
|
322
338
|
this.log("Generating iframe token");
|
|
323
339
|
const response = await this.request("/api/auth-dom/iframe-token", {
|
|
324
340
|
method: "POST",
|
|
325
341
|
body: JSON.stringify({
|
|
326
342
|
publicKey: this.config.publicKey,
|
|
327
|
-
sessionToken: this.
|
|
343
|
+
sessionToken: this.accessToken,
|
|
344
|
+
// Use access token for backward compatibility
|
|
328
345
|
customerFlowId
|
|
329
346
|
})
|
|
330
347
|
});
|
|
@@ -347,7 +364,7 @@ var TiquoAuth = class {
|
|
|
347
364
|
throw new TiquoAuthError("Container element not found", "CONTAINER_NOT_FOUND");
|
|
348
365
|
}
|
|
349
366
|
let authToken;
|
|
350
|
-
if (this.
|
|
367
|
+
if (this.accessToken) {
|
|
351
368
|
try {
|
|
352
369
|
const { token } = await this.getIframeToken();
|
|
353
370
|
authToken = token;
|
|
@@ -377,22 +394,71 @@ var TiquoAuth = class {
|
|
|
377
394
|
return iframe;
|
|
378
395
|
}
|
|
379
396
|
/**
|
|
380
|
-
* Get the current
|
|
397
|
+
* Get the current access token (for advanced use cases)
|
|
398
|
+
*/
|
|
399
|
+
getAccessToken() {
|
|
400
|
+
return this.accessToken;
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Get the current refresh token (for advanced use cases)
|
|
381
404
|
*/
|
|
382
|
-
|
|
383
|
-
return this.
|
|
405
|
+
getRefreshToken() {
|
|
406
|
+
return this.refreshToken;
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Initialize with external tokens (for WebView integration)
|
|
410
|
+
*/
|
|
411
|
+
initWithTokens(accessToken, refreshToken) {
|
|
412
|
+
this.log("Initializing with external tokens");
|
|
413
|
+
this.accessToken = accessToken;
|
|
414
|
+
this.refreshToken = refreshToken || null;
|
|
415
|
+
this.saveTokens();
|
|
416
|
+
this.refreshSession();
|
|
417
|
+
this.broadcastTabSync("LOGIN");
|
|
384
418
|
}
|
|
385
419
|
// ============================================
|
|
386
420
|
// PRIVATE METHODS
|
|
387
421
|
// ============================================
|
|
422
|
+
/**
|
|
423
|
+
* Check for tokens injected by native apps (WebView integration)
|
|
424
|
+
*/
|
|
425
|
+
checkForInjectedTokens() {
|
|
426
|
+
if (typeof window === "undefined") return;
|
|
427
|
+
if (this.config.accessToken) {
|
|
428
|
+
this.log("Found token in config");
|
|
429
|
+
this.accessToken = this.config.accessToken;
|
|
430
|
+
this.refreshToken = this.config.refreshToken || null;
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
if (window.__TIQUO_INIT_TOKEN__?.accessToken) {
|
|
434
|
+
this.log("Found token in window.__TIQUO_INIT_TOKEN__");
|
|
435
|
+
this.accessToken = window.__TIQUO_INIT_TOKEN__.accessToken;
|
|
436
|
+
this.refreshToken = window.__TIQUO_INIT_TOKEN__.refreshToken || null;
|
|
437
|
+
delete window.__TIQUO_INIT_TOKEN__;
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
if (window.location.hash) {
|
|
441
|
+
const params = new URLSearchParams(window.location.hash.slice(1));
|
|
442
|
+
const accessToken = params.get("access_token");
|
|
443
|
+
if (accessToken) {
|
|
444
|
+
this.log("Found token in URL fragment");
|
|
445
|
+
this.accessToken = accessToken;
|
|
446
|
+
this.refreshToken = params.get("refresh_token");
|
|
447
|
+
if (window.history?.replaceState) {
|
|
448
|
+
window.history.replaceState(null, "", window.location.pathname + window.location.search);
|
|
449
|
+
}
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
388
454
|
async request(path, options) {
|
|
389
455
|
const url = `${this.config.apiEndpoint}${path}`;
|
|
390
456
|
const headers = {
|
|
391
457
|
"Content-Type": "application/json",
|
|
392
458
|
...options.headers
|
|
393
459
|
};
|
|
394
|
-
if (this.
|
|
395
|
-
headers["Authorization"] = `Bearer ${this.
|
|
460
|
+
if (this.accessToken) {
|
|
461
|
+
headers["Authorization"] = `Bearer ${this.accessToken}`;
|
|
396
462
|
}
|
|
397
463
|
return fetch(url, {
|
|
398
464
|
...options,
|
|
@@ -400,73 +466,168 @@ var TiquoAuth = class {
|
|
|
400
466
|
credentials: "include"
|
|
401
467
|
});
|
|
402
468
|
}
|
|
469
|
+
/**
|
|
470
|
+
* Ensure we have a valid access token, refreshing if necessary
|
|
471
|
+
*/
|
|
472
|
+
async ensureValidToken() {
|
|
473
|
+
if (!this.accessToken) {
|
|
474
|
+
throw new TiquoAuthError("Not authenticated", "NOT_AUTHENTICATED");
|
|
475
|
+
}
|
|
476
|
+
await this.refreshTokenIfNeeded();
|
|
477
|
+
if (!this.accessToken) {
|
|
478
|
+
throw new TiquoAuthError("Not authenticated", "NOT_AUTHENTICATED");
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Refresh the access token if it's expired or about to expire
|
|
483
|
+
*/
|
|
484
|
+
async refreshTokenIfNeeded() {
|
|
485
|
+
if (!this.accessToken || !this.refreshToken) {
|
|
486
|
+
return false;
|
|
487
|
+
}
|
|
488
|
+
if (!isTokenExpired(this.accessToken, 300)) {
|
|
489
|
+
return true;
|
|
490
|
+
}
|
|
491
|
+
return this.performTokenRefresh();
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Perform the actual token refresh
|
|
495
|
+
*/
|
|
496
|
+
async performTokenRefresh() {
|
|
497
|
+
if (this.isRefreshing) {
|
|
498
|
+
return new Promise((resolve) => {
|
|
499
|
+
const checkInterval = setInterval(() => {
|
|
500
|
+
if (!this.isRefreshing) {
|
|
501
|
+
clearInterval(checkInterval);
|
|
502
|
+
resolve(!!this.accessToken);
|
|
503
|
+
}
|
|
504
|
+
}, 100);
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
if (!this.refreshToken) {
|
|
508
|
+
return false;
|
|
509
|
+
}
|
|
510
|
+
this.isRefreshing = true;
|
|
511
|
+
this.log("Refreshing access token");
|
|
512
|
+
try {
|
|
513
|
+
const response = await fetch(`${this.config.apiEndpoint}/api/client/v1/refresh`, {
|
|
514
|
+
method: "POST",
|
|
515
|
+
headers: {
|
|
516
|
+
"Content-Type": "application/json"
|
|
517
|
+
},
|
|
518
|
+
body: JSON.stringify({
|
|
519
|
+
refresh_token: this.refreshToken
|
|
520
|
+
}),
|
|
521
|
+
credentials: "include"
|
|
522
|
+
});
|
|
523
|
+
if (!response.ok) {
|
|
524
|
+
this.log("Token refresh failed, clearing session");
|
|
525
|
+
this.clearTokens();
|
|
526
|
+
return false;
|
|
527
|
+
}
|
|
528
|
+
const result = await response.json();
|
|
529
|
+
if (result.success && result.data) {
|
|
530
|
+
this.accessToken = result.data.access_token;
|
|
531
|
+
this.refreshToken = result.data.refresh_token;
|
|
532
|
+
this.saveTokens();
|
|
533
|
+
this.scheduleRefresh();
|
|
534
|
+
this.broadcastTabSync("TOKEN_REFRESH");
|
|
535
|
+
this.log("Token refreshed successfully");
|
|
536
|
+
return true;
|
|
537
|
+
}
|
|
538
|
+
return false;
|
|
539
|
+
} catch (error) {
|
|
540
|
+
this.log("Token refresh error:", error);
|
|
541
|
+
return false;
|
|
542
|
+
} finally {
|
|
543
|
+
this.isRefreshing = false;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
403
546
|
async refreshSession() {
|
|
404
|
-
if (!this.
|
|
547
|
+
if (!this.accessToken) {
|
|
405
548
|
return null;
|
|
406
549
|
}
|
|
407
550
|
try {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
const response = await fetch(url.toString(), {
|
|
551
|
+
await this.refreshTokenIfNeeded();
|
|
552
|
+
const response = await fetch(`${this.config.apiEndpoint}/api/client/v1/profile`, {
|
|
411
553
|
method: "GET",
|
|
412
554
|
headers: {
|
|
413
|
-
"Authorization": `Bearer ${this.
|
|
555
|
+
"Authorization": `Bearer ${this.accessToken}`
|
|
414
556
|
},
|
|
415
557
|
credentials: "include"
|
|
416
558
|
});
|
|
417
559
|
if (!response.ok) {
|
|
418
560
|
this.log("Session invalid, clearing");
|
|
419
|
-
this.
|
|
561
|
+
this.clearTokens();
|
|
420
562
|
return null;
|
|
421
563
|
}
|
|
422
|
-
const
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
564
|
+
const result = await response.json();
|
|
565
|
+
if (result.success && result.data) {
|
|
566
|
+
const payload = decodeJWT(this.accessToken);
|
|
567
|
+
const expiresAt = payload?.exp ? payload.exp * 1e3 : Date.now() + 36e5;
|
|
568
|
+
this.session = {
|
|
569
|
+
user: result.data.user,
|
|
570
|
+
customer: result.data.customer,
|
|
571
|
+
expiresAt
|
|
572
|
+
};
|
|
573
|
+
this.notifyListeners();
|
|
574
|
+
this.scheduleRefresh();
|
|
575
|
+
return this.session;
|
|
576
|
+
}
|
|
577
|
+
return null;
|
|
431
578
|
} catch (error) {
|
|
432
579
|
this.log("Session refresh error:", error);
|
|
433
580
|
return null;
|
|
434
581
|
}
|
|
435
582
|
}
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
583
|
+
saveTokens() {
|
|
584
|
+
try {
|
|
585
|
+
if (this.accessToken) {
|
|
439
586
|
localStorage.setItem(
|
|
440
|
-
`${this.config.storagePrefix}
|
|
441
|
-
this.
|
|
587
|
+
`${this.config.storagePrefix}access_token`,
|
|
588
|
+
this.accessToken
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
if (this.refreshToken) {
|
|
592
|
+
localStorage.setItem(
|
|
593
|
+
`${this.config.storagePrefix}refresh_token`,
|
|
594
|
+
this.refreshToken
|
|
442
595
|
);
|
|
443
|
-
} catch (error) {
|
|
444
|
-
this.log("Failed to save session to storage:", error);
|
|
445
596
|
}
|
|
597
|
+
} catch (error) {
|
|
598
|
+
this.log("Failed to save tokens to storage:", error);
|
|
446
599
|
}
|
|
447
600
|
}
|
|
448
|
-
|
|
601
|
+
restoreTokens() {
|
|
602
|
+
if (this.accessToken) {
|
|
603
|
+
this.refreshSession();
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
449
606
|
try {
|
|
450
|
-
const
|
|
451
|
-
|
|
452
|
-
|
|
607
|
+
const accessToken = localStorage.getItem(`${this.config.storagePrefix}access_token`);
|
|
608
|
+
const refreshToken = localStorage.getItem(`${this.config.storagePrefix}refresh_token`);
|
|
609
|
+
if (accessToken) {
|
|
610
|
+
this.accessToken = accessToken;
|
|
611
|
+
this.refreshToken = refreshToken;
|
|
453
612
|
this.refreshSession();
|
|
454
613
|
}
|
|
455
614
|
} catch (error) {
|
|
456
|
-
this.log("Failed to restore
|
|
615
|
+
this.log("Failed to restore tokens from storage:", error);
|
|
457
616
|
}
|
|
458
617
|
}
|
|
459
|
-
|
|
460
|
-
this.
|
|
618
|
+
clearTokens() {
|
|
619
|
+
this.accessToken = null;
|
|
620
|
+
this.refreshToken = null;
|
|
461
621
|
this.session = null;
|
|
462
622
|
if (this.refreshTimer) {
|
|
463
623
|
clearTimeout(this.refreshTimer);
|
|
464
624
|
this.refreshTimer = null;
|
|
465
625
|
}
|
|
466
626
|
try {
|
|
467
|
-
localStorage.removeItem(`${this.config.storagePrefix}
|
|
627
|
+
localStorage.removeItem(`${this.config.storagePrefix}access_token`);
|
|
628
|
+
localStorage.removeItem(`${this.config.storagePrefix}refresh_token`);
|
|
468
629
|
} catch (error) {
|
|
469
|
-
this.log("Failed to clear
|
|
630
|
+
this.log("Failed to clear tokens from storage:", error);
|
|
470
631
|
}
|
|
471
632
|
this.notifyListeners();
|
|
472
633
|
}
|
|
@@ -483,11 +644,14 @@ var TiquoAuth = class {
|
|
|
483
644
|
if (this.refreshTimer) {
|
|
484
645
|
clearTimeout(this.refreshTimer);
|
|
485
646
|
}
|
|
486
|
-
if (!this.
|
|
487
|
-
const
|
|
647
|
+
if (!this.accessToken) return;
|
|
648
|
+
const payload = decodeJWT(this.accessToken);
|
|
649
|
+
if (!payload) return;
|
|
650
|
+
const expiresAt = payload.exp * 1e3;
|
|
651
|
+
const refreshIn = expiresAt - Date.now() - 5 * 60 * 1e3;
|
|
488
652
|
if (refreshIn > 0) {
|
|
489
653
|
this.refreshTimer = setTimeout(() => {
|
|
490
|
-
this.
|
|
654
|
+
this.performTokenRefresh();
|
|
491
655
|
}, refreshIn);
|
|
492
656
|
}
|
|
493
657
|
}
|
|
@@ -524,7 +688,8 @@ var TiquoAuth = class {
|
|
|
524
688
|
type,
|
|
525
689
|
tabId: this.tabId,
|
|
526
690
|
timestamp: Date.now(),
|
|
527
|
-
|
|
691
|
+
accessToken: type === "LOGOUT" ? null : this.accessToken,
|
|
692
|
+
refreshToken: type === "LOGOUT" ? null : this.refreshToken,
|
|
528
693
|
session: type === "LOGOUT" ? null : this.session
|
|
529
694
|
};
|
|
530
695
|
try {
|
|
@@ -542,24 +707,30 @@ var TiquoAuth = class {
|
|
|
542
707
|
try {
|
|
543
708
|
switch (message.type) {
|
|
544
709
|
case "LOGIN":
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
this.
|
|
548
|
-
this.
|
|
710
|
+
case "TOKEN_REFRESH":
|
|
711
|
+
if (message.accessToken) {
|
|
712
|
+
this.accessToken = message.accessToken;
|
|
713
|
+
this.refreshToken = message.refreshToken || null;
|
|
714
|
+
if (message.session) {
|
|
715
|
+
this.session = message.session;
|
|
716
|
+
}
|
|
717
|
+
this.saveTokens();
|
|
549
718
|
this.scheduleRefresh();
|
|
550
719
|
this.notifyListeners();
|
|
551
|
-
this.log("
|
|
720
|
+
this.log("Tokens synced from another tab");
|
|
552
721
|
}
|
|
553
722
|
break;
|
|
554
723
|
case "LOGOUT":
|
|
555
|
-
this.
|
|
724
|
+
this.accessToken = null;
|
|
725
|
+
this.refreshToken = null;
|
|
556
726
|
this.session = null;
|
|
557
727
|
if (this.refreshTimer) {
|
|
558
728
|
clearTimeout(this.refreshTimer);
|
|
559
729
|
this.refreshTimer = null;
|
|
560
730
|
}
|
|
561
731
|
try {
|
|
562
|
-
localStorage.removeItem(`${this.config.storagePrefix}
|
|
732
|
+
localStorage.removeItem(`${this.config.storagePrefix}access_token`);
|
|
733
|
+
localStorage.removeItem(`${this.config.storagePrefix}refresh_token`);
|
|
563
734
|
} catch {
|
|
564
735
|
}
|
|
565
736
|
this.notifyListeners();
|