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