@tiquo/dom-package 1.0.0 → 1.1.0

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.js CHANGED
@@ -22,10 +22,154 @@ var index_exports = {};
22
22
  __export(index_exports, {
23
23
  TiquoAuth: () => TiquoAuth,
24
24
  TiquoAuthError: () => TiquoAuthError,
25
+ addCustomerUserId: () => addCustomerUserId,
26
+ clearCachedEmail: () => clearCachedEmail,
25
27
  default: () => index_default,
28
+ getCustomerUserIds: () => getCustomerUserIds,
29
+ getPrefilledEmailFromCookie: () => getPrefilledEmailFromCookie,
30
+ isClerkAuthenticated: () => isClerkAuthenticated,
31
+ trackCustomerPresence: () => trackCustomerPresence,
26
32
  useTiquoAuth: () => useTiquoAuth
27
33
  });
28
34
  module.exports = __toCommonJS(index_exports);
35
+
36
+ // src/customer-cookie.ts
37
+ var COOKIE_NAME = "tiquo_customer_user_ids";
38
+ var COOKIE_MAX_AGE = 31536e3;
39
+ function isClerkAuthenticated() {
40
+ if (typeof document === "undefined") return false;
41
+ return document.cookie.includes("__clerk_db_jwt") || document.cookie.includes("__session");
42
+ }
43
+ function getCustomerUserIds() {
44
+ if (typeof document === "undefined") return [];
45
+ const match = document.cookie.match(new RegExp(`${COOKIE_NAME}=([^;]+)`));
46
+ if (match) {
47
+ try {
48
+ const decoded = decodeURIComponent(match[1]);
49
+ const parsed = JSON.parse(decoded);
50
+ if (Array.isArray(parsed)) {
51
+ return parsed.filter((id) => typeof id === "string");
52
+ }
53
+ } catch {
54
+ return [];
55
+ }
56
+ }
57
+ return [];
58
+ }
59
+ function addCustomerUserId(userId) {
60
+ if (typeof document === "undefined") return;
61
+ if (typeof window === "undefined") return;
62
+ if (isClerkAuthenticated()) {
63
+ console.log("[Customer Cookie] Skipping - Clerk user detected");
64
+ return;
65
+ }
66
+ if (!userId || typeof userId !== "string") {
67
+ console.warn("[Customer Cookie] Invalid userId provided");
68
+ return;
69
+ }
70
+ const existing = getCustomerUserIds();
71
+ if (existing.includes(userId)) {
72
+ console.log("[Customer Cookie] User ID already in cookie");
73
+ return;
74
+ }
75
+ existing.push(userId);
76
+ const hostname = window.location.hostname;
77
+ const isProduction = hostname.endsWith(".tiquo.app") || hostname === "tiquo.app";
78
+ const cookieParts = [
79
+ `${COOKIE_NAME}=${encodeURIComponent(JSON.stringify(existing))}`,
80
+ "path=/",
81
+ `max-age=${COOKIE_MAX_AGE}`,
82
+ "SameSite=Lax"
83
+ ];
84
+ if (isProduction) {
85
+ cookieParts.push("domain=.tiquo.app");
86
+ cookieParts.push("Secure");
87
+ }
88
+ document.cookie = cookieParts.join("; ");
89
+ console.log("[Customer Cookie] Added user ID to cookie:", userId);
90
+ }
91
+ var CONVEX_SITE_URL = "https://edge.tiquo.app";
92
+ var cachedEmailData = null;
93
+ var EMAIL_CACHE_TTL = 5 * 60 * 1e3;
94
+ async function getPrefilledEmailFromCookie(organizationId) {
95
+ if (typeof window === "undefined") {
96
+ return null;
97
+ }
98
+ if (isClerkAuthenticated()) {
99
+ return null;
100
+ }
101
+ const userIds = getCustomerUserIds();
102
+ if (userIds.length === 0) {
103
+ return null;
104
+ }
105
+ if (cachedEmailData && cachedEmailData.organizationId === organizationId && Date.now() - cachedEmailData.fetchedAt < EMAIL_CACHE_TTL) {
106
+ return {
107
+ email: cachedEmailData.email,
108
+ firstName: cachedEmailData.firstName,
109
+ lastName: cachedEmailData.lastName
110
+ };
111
+ }
112
+ try {
113
+ const response = await fetch(`${CONVEX_SITE_URL}/api/customer-cookie-email`, {
114
+ method: "POST",
115
+ headers: { "Content-Type": "application/json" },
116
+ body: JSON.stringify({ userIds, organizationId })
117
+ });
118
+ if (response.ok) {
119
+ const data = await response.json();
120
+ if (data.success && data.email) {
121
+ cachedEmailData = {
122
+ organizationId,
123
+ email: data.email,
124
+ firstName: data.firstName,
125
+ lastName: data.lastName,
126
+ fetchedAt: Date.now()
127
+ };
128
+ return {
129
+ email: data.email,
130
+ firstName: data.firstName,
131
+ lastName: data.lastName
132
+ };
133
+ }
134
+ }
135
+ return null;
136
+ } catch (error) {
137
+ console.error("[Customer Cookie] Failed to get prefilled email:", error);
138
+ return null;
139
+ }
140
+ }
141
+ function clearCachedEmail() {
142
+ cachedEmailData = null;
143
+ }
144
+ async function trackCustomerPresence(organizationId) {
145
+ if (typeof window === "undefined") {
146
+ return { success: false, error: "Not in browser environment" };
147
+ }
148
+ if (isClerkAuthenticated()) {
149
+ return { success: false, error: "Clerk user detected" };
150
+ }
151
+ const userIds = getCustomerUserIds();
152
+ if (userIds.length === 0) {
153
+ return { success: false, error: "No user IDs in cookie" };
154
+ }
155
+ try {
156
+ const response = await fetch(`${CONVEX_SITE_URL}/api/customer-presence`, {
157
+ method: "POST",
158
+ headers: { "Content-Type": "application/json" },
159
+ body: JSON.stringify({ userIds, organizationId })
160
+ });
161
+ if (response.ok) {
162
+ const data = await response.json();
163
+ return data;
164
+ }
165
+ return { success: false, error: "Failed to track presence" };
166
+ } catch (error) {
167
+ console.error("[Customer Cookie] Failed to track presence:", error);
168
+ return { success: false, error: "Network error" };
169
+ }
170
+ }
171
+
172
+ // src/index.ts
29
173
  var TiquoAuthError = class extends Error {
30
174
  constructor(message, code, statusCode) {
31
175
  super(message);
@@ -34,12 +178,32 @@ var TiquoAuthError = class extends Error {
34
178
  this.name = "TiquoAuthError";
35
179
  }
36
180
  };
181
+ function decodeJWT(token) {
182
+ try {
183
+ const parts = token.split(".");
184
+ if (parts.length !== 3) return null;
185
+ const payload = parts[1];
186
+ const decoded = atob(payload.replace(/-/g, "+").replace(/_/g, "/"));
187
+ return JSON.parse(decoded);
188
+ } catch {
189
+ return null;
190
+ }
191
+ }
192
+ function isTokenExpired(token, bufferSeconds = 300) {
193
+ const payload = decodeJWT(token);
194
+ if (!payload) return true;
195
+ const expiresAt = payload.exp * 1e3;
196
+ const bufferMs = bufferSeconds * 1e3;
197
+ return Date.now() > expiresAt - bufferMs;
198
+ }
37
199
  var TiquoAuth = class {
38
200
  constructor(config) {
39
- this.sessionToken = null;
201
+ this.accessToken = null;
202
+ this.refreshToken = null;
40
203
  this.session = null;
41
204
  this.listeners = /* @__PURE__ */ new Set();
42
205
  this.refreshTimer = null;
206
+ this.isRefreshing = false;
43
207
  // Multi-tab sync
44
208
  this.broadcastChannel = null;
45
209
  this.isProcessingTabSync = false;
@@ -57,14 +221,17 @@ var TiquoAuth = class {
57
221
  apiEndpoint: config.apiEndpoint || "https://edge.tiquo.app",
58
222
  storagePrefix: config.storagePrefix || "tiquo_auth_",
59
223
  debug: config.debug || false,
60
- enableTabSync: config.enableTabSync !== false
224
+ enableTabSync: config.enableTabSync !== false,
61
225
  // Default true
226
+ accessToken: config.accessToken,
227
+ refreshToken: config.refreshToken
62
228
  };
63
229
  this.tabId = this.generateTabId();
64
230
  if (this.config.enableTabSync) {
65
231
  this.initTabSync();
66
232
  }
67
- this.restoreSession();
233
+ this.checkForInjectedTokens();
234
+ this.restoreTokens();
68
235
  }
69
236
  // ============================================
70
237
  // PUBLIC METHODS
@@ -109,13 +276,19 @@ var TiquoAuth = class {
109
276
  throw new TiquoAuthError(error.error || "Invalid OTP", "OTP_VERIFY_FAILED", response.status);
110
277
  }
111
278
  const result = await response.json();
112
- this.sessionToken = result.sessionToken;
113
- this.saveSession();
279
+ this.accessToken = result.accessToken;
280
+ this.refreshToken = result.refreshToken;
281
+ this.saveTokens();
114
282
  await this.refreshSession();
283
+ if (this.session?.user?.id) {
284
+ addCustomerUserId(this.session.user.id);
285
+ }
115
286
  this.broadcastTabSync("LOGIN");
116
287
  return {
117
288
  success: true,
118
- sessionToken: result.sessionToken,
289
+ accessToken: result.accessToken,
290
+ refreshToken: result.refreshToken,
291
+ expiresIn: result.expiresIn,
119
292
  expiresAt: result.expiresAt,
120
293
  isNewUser: result.isNewUser,
121
294
  hasCustomer: result.hasCustomer
@@ -125,10 +298,11 @@ var TiquoAuth = class {
125
298
  * Get the current authenticated user and customer data
126
299
  */
127
300
  async getUser() {
128
- if (!this.sessionToken) {
301
+ if (!this.accessToken) {
129
302
  return null;
130
303
  }
131
- if (this.session && this.session.expiresAt > Date.now()) {
304
+ await this.refreshTokenIfNeeded();
305
+ if (this.session) {
132
306
  return this.session;
133
307
  }
134
308
  return this.refreshSession();
@@ -137,60 +311,49 @@ var TiquoAuth = class {
137
311
  * Check if user is currently authenticated
138
312
  */
139
313
  isAuthenticated() {
140
- return !!this.sessionToken && (!this.session || this.session.expiresAt > Date.now());
314
+ return !!this.accessToken && !isTokenExpired(this.accessToken, 0);
141
315
  }
142
316
  /**
143
317
  * Update the authenticated customer's profile
144
318
  * Only allows updating the logged-in customer's own data
145
319
  */
146
320
  async updateProfile(updates) {
147
- if (!this.sessionToken) {
148
- throw new TiquoAuthError("Not authenticated", "NOT_AUTHENTICATED");
149
- }
321
+ await this.ensureValidToken();
150
322
  this.log("Updating customer profile:", updates);
151
- const response = await this.request("/api/auth-dom/profile", {
323
+ const response = await this.request("/api/client/v1/profile", {
152
324
  method: "PATCH",
153
- body: JSON.stringify({
154
- publicKey: this.config.publicKey,
155
- sessionToken: this.sessionToken,
156
- updates
157
- })
325
+ body: JSON.stringify(updates)
158
326
  });
159
327
  if (!response.ok) {
160
328
  const error = await response.json().catch(() => ({ error: "Failed to update profile" }));
161
329
  throw new TiquoAuthError(error.error || "Failed to update profile", "PROFILE_UPDATE_FAILED", response.status);
162
330
  }
163
331
  const result = await response.json();
164
- if (this.session && result.customer) {
332
+ if (this.session && result.data?.customer) {
165
333
  this.session = {
166
334
  ...this.session,
167
- 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
175
- }
335
+ customer: result.data.customer
176
336
  };
177
337
  this.notifyListeners();
178
338
  this.broadcastTabSync("SESSION_UPDATE");
179
339
  }
180
- return result;
340
+ return {
341
+ success: true,
342
+ customer: result.data?.customer
343
+ };
181
344
  }
182
345
  /**
183
346
  * Log out the current user
184
347
  */
185
348
  async logout() {
186
349
  this.log("Logging out");
187
- if (this.sessionToken) {
350
+ if (this.refreshToken) {
188
351
  try {
189
352
  await this.request("/api/auth-dom/logout", {
190
353
  method: "POST",
191
354
  body: JSON.stringify({
192
355
  publicKey: this.config.publicKey,
193
- sessionToken: this.sessionToken
356
+ refreshToken: this.refreshToken
194
357
  })
195
358
  });
196
359
  } catch (error) {
@@ -198,7 +361,7 @@ var TiquoAuth = class {
198
361
  }
199
362
  }
200
363
  this.broadcastTabSync("LOGOUT");
201
- this.clearSession();
364
+ this.clearTokens();
202
365
  }
203
366
  /**
204
367
  * Subscribe to authentication state changes
@@ -215,12 +378,9 @@ var TiquoAuth = class {
215
378
  * Only returns orders for the logged-in customer
216
379
  */
217
380
  async getOrders(options) {
218
- if (!this.sessionToken) {
219
- throw new TiquoAuthError("Not authenticated", "NOT_AUTHENTICATED");
220
- }
381
+ await this.ensureValidToken();
221
382
  this.log("Fetching customer orders:", options);
222
- const url = new URL(`${this.config.apiEndpoint}/api/auth-dom/orders`);
223
- url.searchParams.set("publicKey", this.config.publicKey);
383
+ const url = new URL(`${this.config.apiEndpoint}/api/client/v1/orders`);
224
384
  if (options?.limit) {
225
385
  url.searchParams.set("limit", options.limit.toString());
226
386
  }
@@ -233,7 +393,7 @@ var TiquoAuth = class {
233
393
  const response = await fetch(url.toString(), {
234
394
  method: "GET",
235
395
  headers: {
236
- "Authorization": `Bearer ${this.sessionToken}`
396
+ "Authorization": `Bearer ${this.accessToken}`
237
397
  },
238
398
  credentials: "include"
239
399
  });
@@ -241,19 +401,17 @@ var TiquoAuth = class {
241
401
  const error = await response.json().catch(() => ({ error: "Failed to get orders" }));
242
402
  throw new TiquoAuthError(error.error || "Failed to get orders", "GET_ORDERS_FAILED", response.status);
243
403
  }
244
- return response.json();
404
+ const result = await response.json();
405
+ return result.data || { orders: [], hasMore: false };
245
406
  }
246
407
  /**
247
408
  * Get the authenticated customer's booking history
248
409
  * Only returns bookings for the logged-in customer
249
410
  */
250
411
  async getBookings(options) {
251
- if (!this.sessionToken) {
252
- throw new TiquoAuthError("Not authenticated", "NOT_AUTHENTICATED");
253
- }
412
+ await this.ensureValidToken();
254
413
  this.log("Fetching customer bookings:", options);
255
- const url = new URL(`${this.config.apiEndpoint}/api/auth-dom/bookings`);
256
- url.searchParams.set("publicKey", this.config.publicKey);
414
+ const url = new URL(`${this.config.apiEndpoint}/api/client/v1/bookings`);
257
415
  if (options?.limit) {
258
416
  url.searchParams.set("limit", options.limit.toString());
259
417
  }
@@ -269,7 +427,7 @@ var TiquoAuth = class {
269
427
  const response = await fetch(url.toString(), {
270
428
  method: "GET",
271
429
  headers: {
272
- "Authorization": `Bearer ${this.sessionToken}`
430
+ "Authorization": `Bearer ${this.accessToken}`
273
431
  },
274
432
  credentials: "include"
275
433
  });
@@ -277,19 +435,17 @@ var TiquoAuth = class {
277
435
  const error = await response.json().catch(() => ({ error: "Failed to get bookings" }));
278
436
  throw new TiquoAuthError(error.error || "Failed to get bookings", "GET_BOOKINGS_FAILED", response.status);
279
437
  }
280
- return response.json();
438
+ const result = await response.json();
439
+ return result.data || { bookings: [], hasMore: false };
281
440
  }
282
441
  /**
283
442
  * Get the authenticated customer's enquiry history
284
443
  * Only returns enquiries for the logged-in customer
285
444
  */
286
445
  async getEnquiries(options) {
287
- if (!this.sessionToken) {
288
- throw new TiquoAuthError("Not authenticated", "NOT_AUTHENTICATED");
289
- }
446
+ await this.ensureValidToken();
290
447
  this.log("Fetching customer enquiries:", options);
291
- const url = new URL(`${this.config.apiEndpoint}/api/auth-dom/enquiries`);
292
- url.searchParams.set("publicKey", this.config.publicKey);
448
+ const url = new URL(`${this.config.apiEndpoint}/api/client/v1/enquiries`);
293
449
  if (options?.limit) {
294
450
  url.searchParams.set("limit", options.limit.toString());
295
451
  }
@@ -302,7 +458,7 @@ var TiquoAuth = class {
302
458
  const response = await fetch(url.toString(), {
303
459
  method: "GET",
304
460
  headers: {
305
- "Authorization": `Bearer ${this.sessionToken}`
461
+ "Authorization": `Bearer ${this.accessToken}`
306
462
  },
307
463
  credentials: "include"
308
464
  });
@@ -310,21 +466,20 @@ var TiquoAuth = class {
310
466
  const error = await response.json().catch(() => ({ error: "Failed to get enquiries" }));
311
467
  throw new TiquoAuthError(error.error || "Failed to get enquiries", "GET_ENQUIRIES_FAILED", response.status);
312
468
  }
313
- return response.json();
469
+ const result = await response.json();
470
+ return result.data || { enquiries: [], hasMore: false };
314
471
  }
315
472
  /**
316
473
  * Generate a short-lived token for customer flow iframe authentication
317
474
  */
318
475
  async getIframeToken(customerFlowId) {
319
- if (!this.sessionToken) {
320
- throw new TiquoAuthError("Not authenticated", "NOT_AUTHENTICATED");
321
- }
476
+ await this.ensureValidToken();
322
477
  this.log("Generating iframe token");
323
478
  const response = await this.request("/api/auth-dom/iframe-token", {
324
479
  method: "POST",
325
480
  body: JSON.stringify({
326
481
  publicKey: this.config.publicKey,
327
- sessionToken: this.sessionToken,
482
+ sessionToken: this.accessToken,
328
483
  customerFlowId
329
484
  })
330
485
  });
@@ -347,7 +502,7 @@ var TiquoAuth = class {
347
502
  throw new TiquoAuthError("Container element not found", "CONTAINER_NOT_FOUND");
348
503
  }
349
504
  let authToken;
350
- if (this.sessionToken) {
505
+ if (this.accessToken) {
351
506
  try {
352
507
  const { token } = await this.getIframeToken();
353
508
  authToken = token;
@@ -377,22 +532,71 @@ var TiquoAuth = class {
377
532
  return iframe;
378
533
  }
379
534
  /**
380
- * Get the current session token (for advanced use cases)
535
+ * Get the current access token (for advanced use cases)
536
+ */
537
+ getAccessToken() {
538
+ return this.accessToken;
539
+ }
540
+ /**
541
+ * Get the current refresh token (for advanced use cases)
381
542
  */
382
- getSessionToken() {
383
- return this.sessionToken;
543
+ getRefreshToken() {
544
+ return this.refreshToken;
545
+ }
546
+ /**
547
+ * Initialize with external tokens (for WebView integration)
548
+ */
549
+ initWithTokens(accessToken, refreshToken) {
550
+ this.log("Initializing with external tokens");
551
+ this.accessToken = accessToken;
552
+ this.refreshToken = refreshToken || null;
553
+ this.saveTokens();
554
+ this.refreshSession();
555
+ this.broadcastTabSync("LOGIN");
384
556
  }
385
557
  // ============================================
386
558
  // PRIVATE METHODS
387
559
  // ============================================
560
+ /**
561
+ * Check for tokens injected by native apps (WebView integration)
562
+ */
563
+ checkForInjectedTokens() {
564
+ if (typeof window === "undefined") return;
565
+ if (this.config.accessToken) {
566
+ this.log("Found token in config");
567
+ this.accessToken = this.config.accessToken;
568
+ this.refreshToken = this.config.refreshToken || null;
569
+ return;
570
+ }
571
+ if (window.__TIQUO_INIT_TOKEN__?.accessToken) {
572
+ this.log("Found token in window.__TIQUO_INIT_TOKEN__");
573
+ this.accessToken = window.__TIQUO_INIT_TOKEN__.accessToken;
574
+ this.refreshToken = window.__TIQUO_INIT_TOKEN__.refreshToken || null;
575
+ delete window.__TIQUO_INIT_TOKEN__;
576
+ return;
577
+ }
578
+ if (window.location.hash) {
579
+ const params = new URLSearchParams(window.location.hash.slice(1));
580
+ const accessToken = params.get("access_token");
581
+ if (accessToken) {
582
+ this.log("Found token in URL fragment");
583
+ this.accessToken = accessToken;
584
+ this.refreshToken = params.get("refresh_token");
585
+ if (window.history?.replaceState) {
586
+ window.history.replaceState(null, "", window.location.pathname + window.location.search);
587
+ }
588
+ return;
589
+ }
590
+ }
591
+ }
388
592
  async request(path, options) {
389
593
  const url = `${this.config.apiEndpoint}${path}`;
390
594
  const headers = {
391
595
  "Content-Type": "application/json",
392
596
  ...options.headers
393
597
  };
394
- if (this.sessionToken && !(typeof options.body === "string" && options.body.includes("sessionToken"))) {
395
- headers["Authorization"] = `Bearer ${this.sessionToken}`;
598
+ if (this.accessToken) {
599
+ headers["Authorization"] = `Bearer ${this.accessToken}`;
396
600
  }
397
601
  return fetch(url, {
398
602
  ...options,
@@ -400,73 +604,171 @@ var TiquoAuth = class {
400
604
  credentials: "include"
401
605
  });
402
606
  }
607
+ /**
608
+ * Ensure we have a valid access token, refreshing if necessary
609
+ */
610
+ async ensureValidToken() {
611
+ if (!this.accessToken) {
612
+ throw new TiquoAuthError("Not authenticated", "NOT_AUTHENTICATED");
613
+ }
614
+ await this.refreshTokenIfNeeded();
615
+ if (!this.accessToken) {
616
+ throw new TiquoAuthError("Not authenticated", "NOT_AUTHENTICATED");
617
+ }
618
+ }
619
+ /**
620
+ * Refresh the access token if it's expired or about to expire
621
+ */
622
+ async refreshTokenIfNeeded() {
623
+ if (!this.accessToken || !this.refreshToken) {
624
+ return false;
625
+ }
626
+ if (!isTokenExpired(this.accessToken, 300)) {
627
+ return true;
628
+ }
629
+ return this.performTokenRefresh();
630
+ }
631
+ /**
632
+ * Perform the actual token refresh
633
+ */
634
+ async performTokenRefresh() {
635
+ if (this.isRefreshing) {
636
+ return new Promise((resolve) => {
637
+ const checkInterval = setInterval(() => {
638
+ if (!this.isRefreshing) {
639
+ clearInterval(checkInterval);
640
+ resolve(!!this.accessToken);
641
+ }
642
+ }, 100);
643
+ });
644
+ }
645
+ if (!this.refreshToken) {
646
+ return false;
647
+ }
648
+ this.isRefreshing = true;
649
+ this.log("Refreshing access token");
650
+ try {
651
+ const response = await fetch(`${this.config.apiEndpoint}/api/client/v1/refresh`, {
652
+ method: "POST",
653
+ headers: {
654
+ "Content-Type": "application/json"
655
+ },
656
+ body: JSON.stringify({
657
+ refresh_token: this.refreshToken
658
+ }),
659
+ credentials: "include"
660
+ });
661
+ if (!response.ok) {
662
+ this.log("Token refresh failed, clearing session");
663
+ this.clearTokens();
664
+ return false;
665
+ }
666
+ const result = await response.json();
667
+ if (result.success && result.data) {
668
+ this.accessToken = result.data.access_token;
669
+ this.refreshToken = result.data.refresh_token;
670
+ this.saveTokens();
671
+ this.scheduleRefresh();
672
+ this.broadcastTabSync("TOKEN_REFRESH");
673
+ this.log("Token refreshed successfully");
674
+ return true;
675
+ }
676
+ return false;
677
+ } catch (error) {
678
+ this.log("Token refresh error:", error);
679
+ return false;
680
+ } finally {
681
+ this.isRefreshing = false;
682
+ }
683
+ }
403
684
  async refreshSession() {
404
- if (!this.sessionToken) {
685
+ if (!this.accessToken) {
405
686
  return null;
406
687
  }
407
688
  try {
408
- const url = new URL(`${this.config.apiEndpoint}/api/auth-dom/session`);
409
- url.searchParams.set("publicKey", this.config.publicKey);
410
- const response = await fetch(url.toString(), {
689
+ await this.refreshTokenIfNeeded();
690
+ const response = await fetch(`${this.config.apiEndpoint}/api/client/v1/profile`, {
411
691
  method: "GET",
412
692
  headers: {
413
- "Authorization": `Bearer ${this.sessionToken}`
693
+ "Authorization": `Bearer ${this.accessToken}`
414
694
  },
415
695
  credentials: "include"
416
696
  });
417
697
  if (!response.ok) {
418
698
  this.log("Session invalid, clearing");
419
- this.clearSession();
699
+ this.clearTokens();
420
700
  return null;
421
701
  }
422
- const data = await response.json();
423
- this.session = {
424
- user: data.user,
425
- customer: data.customer,
426
- expiresAt: data.session.expiresAt
427
- };
428
- this.notifyListeners();
429
- this.scheduleRefresh();
430
- return this.session;
702
+ const result = await response.json();
703
+ if (result.success && result.data) {
704
+ const payload = decodeJWT(this.accessToken);
705
+ const expiresAt = payload?.exp ? payload.exp * 1e3 : Date.now() + 36e5;
706
+ this.session = {
707
+ user: result.data.user,
708
+ customer: result.data.customer,
709
+ expiresAt
710
+ };
711
+ if (this.session.user?.id) {
712
+ addCustomerUserId(this.session.user.id);
713
+ }
714
+ this.notifyListeners();
715
+ this.scheduleRefresh();
716
+ return this.session;
717
+ }
718
+ return null;
431
719
  } catch (error) {
432
720
  this.log("Session refresh error:", error);
433
721
  return null;
434
722
  }
435
723
  }
436
- saveSession() {
437
- if (this.sessionToken) {
438
- try {
724
+ saveTokens() {
725
+ try {
726
+ if (this.accessToken) {
439
727
  localStorage.setItem(
440
- `${this.config.storagePrefix}session`,
441
- this.sessionToken
728
+ `${this.config.storagePrefix}access_token`,
729
+ this.accessToken
442
730
  );
443
- } catch (error) {
444
- this.log("Failed to save session to storage:", error);
445
731
  }
732
+ if (this.refreshToken) {
733
+ localStorage.setItem(
734
+ `${this.config.storagePrefix}refresh_token`,
735
+ this.refreshToken
736
+ );
737
+ }
738
+ } catch (error) {
739
+ this.log("Failed to save tokens to storage:", error);
446
740
  }
447
741
  }
448
- restoreSession() {
742
+ restoreTokens() {
743
+ if (this.accessToken) {
744
+ this.refreshSession();
745
+ return;
746
+ }
449
747
  try {
450
- const token = localStorage.getItem(`${this.config.storagePrefix}session`);
451
- if (token) {
452
- this.sessionToken = token;
748
+ const accessToken = localStorage.getItem(`${this.config.storagePrefix}access_token`);
749
+ const refreshToken = localStorage.getItem(`${this.config.storagePrefix}refresh_token`);
750
+ if (accessToken) {
751
+ this.accessToken = accessToken;
752
+ this.refreshToken = refreshToken;
453
753
  this.refreshSession();
454
754
  }
455
755
  } catch (error) {
456
- this.log("Failed to restore session from storage:", error);
756
+ this.log("Failed to restore tokens from storage:", error);
457
757
  }
458
758
  }
459
- clearSession() {
460
- this.sessionToken = null;
759
+ clearTokens() {
760
+ this.accessToken = null;
761
+ this.refreshToken = null;
461
762
  this.session = null;
462
763
  if (this.refreshTimer) {
463
764
  clearTimeout(this.refreshTimer);
464
765
  this.refreshTimer = null;
465
766
  }
466
767
  try {
467
- localStorage.removeItem(`${this.config.storagePrefix}session`);
768
+ localStorage.removeItem(`${this.config.storagePrefix}access_token`);
769
+ localStorage.removeItem(`${this.config.storagePrefix}refresh_token`);
468
770
  } catch (error) {
469
- this.log("Failed to clear session from storage:", error);
771
+ this.log("Failed to clear tokens from storage:", error);
470
772
  }
471
773
  this.notifyListeners();
472
774
  }
@@ -483,11 +785,14 @@ var TiquoAuth = class {
483
785
  if (this.refreshTimer) {
484
786
  clearTimeout(this.refreshTimer);
485
787
  }
486
- if (!this.session) return;
487
- const refreshIn = this.session.expiresAt - Date.now() - 5 * 60 * 1e3;
788
+ if (!this.accessToken) return;
789
+ const payload = decodeJWT(this.accessToken);
790
+ if (!payload) return;
791
+ const expiresAt = payload.exp * 1e3;
792
+ const refreshIn = expiresAt - Date.now() - 5 * 60 * 1e3;
488
793
  if (refreshIn > 0) {
489
794
  this.refreshTimer = setTimeout(() => {
490
- this.refreshSession();
795
+ this.performTokenRefresh();
491
796
  }, refreshIn);
492
797
  }
493
798
  }
@@ -524,7 +829,8 @@ var TiquoAuth = class {
524
829
  type,
525
830
  tabId: this.tabId,
526
831
  timestamp: Date.now(),
527
- sessionToken: type === "LOGOUT" ? null : this.sessionToken,
832
+ accessToken: type === "LOGOUT" ? null : this.accessToken,
833
+ refreshToken: type === "LOGOUT" ? null : this.refreshToken,
528
834
  session: type === "LOGOUT" ? null : this.session
529
835
  };
530
836
  try {
@@ -542,24 +848,30 @@ var TiquoAuth = class {
542
848
  try {
543
849
  switch (message.type) {
544
850
  case "LOGIN":
545
- if (message.sessionToken && message.session) {
546
- this.sessionToken = message.sessionToken;
547
- this.session = message.session;
548
- this.saveSession();
851
+ case "TOKEN_REFRESH":
852
+ if (message.accessToken) {
853
+ this.accessToken = message.accessToken;
854
+ this.refreshToken = message.refreshToken || null;
855
+ if (message.session) {
856
+ this.session = message.session;
857
+ }
858
+ this.saveTokens();
549
859
  this.scheduleRefresh();
550
860
  this.notifyListeners();
551
- this.log("Session synced from another tab (login)");
861
+ this.log("Tokens synced from another tab");
552
862
  }
553
863
  break;
554
864
  case "LOGOUT":
555
- this.sessionToken = null;
865
+ this.accessToken = null;
866
+ this.refreshToken = null;
556
867
  this.session = null;
557
868
  if (this.refreshTimer) {
558
869
  clearTimeout(this.refreshTimer);
559
870
  this.refreshTimer = null;
560
871
  }
561
872
  try {
562
- localStorage.removeItem(`${this.config.storagePrefix}session`);
873
+ localStorage.removeItem(`${this.config.storagePrefix}access_token`);
874
+ localStorage.removeItem(`${this.config.storagePrefix}refresh_token`);
563
875
  } catch {
564
876
  }
565
877
  this.notifyListeners();
@@ -633,5 +945,11 @@ var index_default = TiquoAuth;
633
945
  0 && (module.exports = {
634
946
  TiquoAuth,
635
947
  TiquoAuthError,
948
+ addCustomerUserId,
949
+ clearCachedEmail,
950
+ getCustomerUserIds,
951
+ getPrefilledEmailFromCookie,
952
+ isClerkAuthenticated,
953
+ trackCustomerPresence,
636
954
  useTiquoAuth
637
955
  });