@proveanything/smartlinks-auth-ui 0.1.20 → 0.1.22

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
@@ -10722,14 +10722,18 @@ class AuthAPI {
10722
10722
  });
10723
10723
  }
10724
10724
  async fetchConfig() {
10725
+ console.log('[AuthAPI] 📋 fetchConfig called with clientId:', this.clientId);
10725
10726
  this.log.log('fetchConfig called with clientId:', this.clientId);
10726
10727
  try {
10728
+ console.log('[AuthAPI] 🌐 Calling smartlinks.authKit.load() - this uses current SDK config (including proxyMode)');
10727
10729
  this.log.log('Calling smartlinks.authKit.load...');
10728
10730
  const result = await smartlinks__namespace.authKit.load(this.clientId);
10731
+ console.log('[AuthAPI] ✅ smartlinks.authKit.load returned:', result);
10729
10732
  this.log.log('smartlinks.authKit.load returned:', result);
10730
10733
  return result;
10731
10734
  }
10732
10735
  catch (error) {
10736
+ console.log('[AuthAPI] ❌ Failed to fetch UI config:', error);
10733
10737
  this.log.warn('Failed to fetch UI config, using defaults:', error);
10734
10738
  return {
10735
10739
  branding: {
@@ -11148,6 +11152,7 @@ const TOKEN_KEY = 'token';
11148
11152
  const USER_KEY = 'user';
11149
11153
  const ACCOUNT_DATA_KEY = 'account_data';
11150
11154
  const ACCOUNT_INFO_KEY = 'account_info';
11155
+ const CONTACT_ID_KEY = 'contact_id';
11151
11156
  const ACCOUNT_INFO_TTL = 5 * 60 * 1000; // 5 minutes default
11152
11157
  /**
11153
11158
  * Token Storage Layer
@@ -11198,6 +11203,7 @@ const tokenStorage = {
11198
11203
  await this.clearUser();
11199
11204
  await this.clearAccountData();
11200
11205
  await this.clearAccountInfo();
11206
+ await this.clearContactId();
11201
11207
  },
11202
11208
  async saveAccountData(data) {
11203
11209
  const storage = await getStorage();
@@ -11235,20 +11241,69 @@ const tokenStorage = {
11235
11241
  const storage = await getStorage();
11236
11242
  await storage.removeItem(ACCOUNT_INFO_KEY);
11237
11243
  },
11244
+ async saveContactId(contactId) {
11245
+ const storage = await getStorage();
11246
+ await storage.setItem(CONTACT_ID_KEY, contactId);
11247
+ },
11248
+ async getContactId() {
11249
+ const storage = await getStorage();
11250
+ return await storage.getItem(CONTACT_ID_KEY);
11251
+ },
11252
+ async clearContactId() {
11253
+ const storage = await getStorage();
11254
+ await storage.removeItem(CONTACT_ID_KEY);
11255
+ },
11238
11256
  };
11239
11257
 
11240
11258
  const AuthContext = React.createContext(undefined);
11241
- const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 * 1000, preloadAccountInfo = false }) => {
11259
+ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 * 1000, preloadAccountInfo = false,
11260
+ // Contact & Interaction features
11261
+ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, interactionConfig, }) => {
11242
11262
  const [user, setUser] = React.useState(null);
11243
11263
  const [token, setToken] = React.useState(null);
11244
11264
  const [accountData, setAccountData] = React.useState(null);
11245
11265
  const [accountInfo, setAccountInfo] = React.useState(null);
11246
11266
  const [isLoading, setIsLoading] = React.useState(true);
11267
+ const [isVerified, setIsVerified] = React.useState(false);
11268
+ const [isOnline, setIsOnline] = React.useState(typeof navigator !== 'undefined' ? navigator.onLine : true);
11269
+ // Contact state
11270
+ const [contact, setContact] = React.useState(null);
11271
+ const [contactId, setContactId] = React.useState(null);
11247
11272
  const callbacksRef = React.useRef(new Set());
11248
- // Initialization guard to prevent concurrent runs (NOT persisted across remounts)
11249
11273
  const initializingRef = React.useRef(false);
11274
+ const pendingVerificationRef = React.useRef(false);
11275
+ // Default to enabled if collectionId is provided
11276
+ const shouldSyncContacts = enableContactSync ?? !!collectionId;
11277
+ const shouldTrackInteractions = enableInteractionTracking ?? !!collectionId;
11278
+ // Helper to detect if an error is a network error vs auth error
11279
+ const isNetworkError = React.useCallback((error) => {
11280
+ if (!error)
11281
+ return false;
11282
+ const errorMessage = error?.message?.toLowerCase() || '';
11283
+ const errorName = error?.name?.toLowerCase() || '';
11284
+ if (errorName === 'typeerror' && errorMessage.includes('fetch'))
11285
+ return true;
11286
+ if (errorMessage.includes('network') || errorMessage.includes('offline'))
11287
+ return true;
11288
+ if (errorMessage.includes('failed to fetch'))
11289
+ return true;
11290
+ if (errorMessage.includes('net::err_'))
11291
+ return true;
11292
+ if (errorMessage.includes('timeout'))
11293
+ return true;
11294
+ if (errorMessage.includes('dns'))
11295
+ return true;
11296
+ if (error?.code === 'ENOTFOUND' || error?.code === 'ETIMEDOUT')
11297
+ return true;
11298
+ const status = error?.status || error?.response?.status;
11299
+ if (status === 401 || status === 403)
11300
+ return false;
11301
+ if (typeof navigator !== 'undefined' && !navigator.onLine)
11302
+ return true;
11303
+ return false;
11304
+ }, []);
11250
11305
  // Notify all subscribers of auth state changes
11251
- const notifyAuthStateChange = React.useCallback((type, currentUser, currentToken, currentAccountData, currentAccountInfo) => {
11306
+ const notifyAuthStateChange = React.useCallback((type, currentUser, currentToken, currentAccountData, currentAccountInfo, verified, currentContact, currentContactId) => {
11252
11307
  callbacksRef.current.forEach(callback => {
11253
11308
  try {
11254
11309
  callback({
@@ -11256,7 +11311,10 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
11256
11311
  user: currentUser,
11257
11312
  token: currentToken,
11258
11313
  accountData: currentAccountData,
11259
- accountInfo: currentAccountInfo
11314
+ accountInfo: currentAccountInfo,
11315
+ isVerified: verified,
11316
+ contact: currentContact,
11317
+ contactId: currentContactId,
11260
11318
  });
11261
11319
  }
11262
11320
  catch (error) {
@@ -11264,9 +11322,128 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
11264
11322
  }
11265
11323
  });
11266
11324
  }, []);
11267
- // Initialize auth state - different behavior for proxy mode vs standalone mode
11325
+ // Sync contact to Smartlinks (non-blocking)
11326
+ const syncContact = React.useCallback(async (authUser, customFields) => {
11327
+ if (!collectionId || !shouldSyncContacts) {
11328
+ console.log('[AuthContext] Contact sync skipped: no collectionId or disabled');
11329
+ return null;
11330
+ }
11331
+ try {
11332
+ console.log('[AuthContext] Syncing contact for user:', authUser.uid);
11333
+ const result = await smartlinks__namespace.contact.publicUpsert(collectionId, {
11334
+ userId: authUser.uid,
11335
+ email: authUser.email,
11336
+ displayName: authUser.displayName,
11337
+ phone: authUser.phoneNumber,
11338
+ customFields: customFields || {},
11339
+ source: 'authkit',
11340
+ });
11341
+ console.log('[AuthContext] Contact synced:', result.contactId);
11342
+ // Store contact ID locally
11343
+ if (!proxyMode) {
11344
+ await tokenStorage.saveContactId(result.contactId);
11345
+ }
11346
+ setContactId(result.contactId);
11347
+ // Fetch full contact to get customFields
11348
+ try {
11349
+ const fullContact = await smartlinks__namespace.contact.lookup(collectionId, {
11350
+ email: authUser.email
11351
+ });
11352
+ setContact(fullContact);
11353
+ notifyAuthStateChange('CONTACT_SYNCED', authUser, token, accountData, accountInfo, isVerified, fullContact, result.contactId);
11354
+ }
11355
+ catch (lookupErr) {
11356
+ console.warn('[AuthContext] Failed to lookup full contact:', lookupErr);
11357
+ }
11358
+ return result.contactId;
11359
+ }
11360
+ catch (err) {
11361
+ console.warn('[AuthContext] Contact sync failed (non-blocking):', err);
11362
+ return null;
11363
+ }
11364
+ }, [collectionId, shouldSyncContacts, proxyMode, token, accountData, accountInfo, isVerified, notifyAuthStateChange]);
11365
+ // Track interaction event (non-blocking)
11366
+ const trackInteraction = React.useCallback(async (eventType, userId, currentContactId, metadata) => {
11367
+ if (!collectionId || !shouldTrackInteractions) {
11368
+ console.log('[AuthContext] Interaction tracking skipped: no collectionId or disabled');
11369
+ return;
11370
+ }
11371
+ const interactionIdMap = {
11372
+ login: interactionConfig?.login || 'authkit.login',
11373
+ logout: interactionConfig?.logout || 'authkit.logout',
11374
+ signup: interactionConfig?.signup || 'authkit.signup',
11375
+ session_restore: interactionConfig?.sessionRestore,
11376
+ };
11377
+ const interactionId = interactionIdMap[eventType];
11378
+ if (!interactionId) {
11379
+ console.log(`[AuthContext] No interaction ID for ${eventType}, skipping`);
11380
+ return;
11381
+ }
11382
+ try {
11383
+ console.log(`[AuthContext] Tracking interaction: ${interactionId}`);
11384
+ await smartlinks__namespace.interactions.submitPublicEvent(collectionId, {
11385
+ collectionId,
11386
+ interactionId,
11387
+ userId,
11388
+ contactId: currentContactId || undefined,
11389
+ appId: interactionAppId,
11390
+ eventType,
11391
+ outcome: 'completed',
11392
+ metadata: {
11393
+ ...metadata,
11394
+ timestamp: new Date().toISOString(),
11395
+ source: 'authkit',
11396
+ },
11397
+ });
11398
+ console.log(`[AuthContext] Tracked interaction: ${interactionId}`);
11399
+ notifyAuthStateChange('INTERACTION_TRACKED', user, token, accountData, accountInfo, isVerified, contact, contactId);
11400
+ }
11401
+ catch (err) {
11402
+ console.warn('[AuthContext] Interaction tracking failed (non-blocking):', err);
11403
+ }
11404
+ }, [collectionId, shouldTrackInteractions, interactionAppId, interactionConfig, user, token, accountData, accountInfo, isVerified, contact, contactId, notifyAuthStateChange]);
11405
+ // Get contact (with optional refresh)
11406
+ const getContact = React.useCallback(async (forceRefresh = false) => {
11407
+ if (!collectionId) {
11408
+ console.log('[AuthContext] getContact: no collectionId');
11409
+ return null;
11410
+ }
11411
+ // Need either email or userId to lookup contact
11412
+ if (!user?.email && !user?.uid) {
11413
+ console.log('[AuthContext] getContact: no user email or uid');
11414
+ return null;
11415
+ }
11416
+ if (contact && !forceRefresh) {
11417
+ return contact;
11418
+ }
11419
+ try {
11420
+ // Prefer email lookup, fallback to userId for phone-only users
11421
+ const lookupParams = user.email
11422
+ ? { email: user.email }
11423
+ : { userId: user.uid };
11424
+ console.log('[AuthContext] Fetching contact with:', lookupParams);
11425
+ const result = await smartlinks__namespace.contact.lookup(collectionId, lookupParams);
11426
+ setContact(result);
11427
+ setContactId(result.contactId);
11428
+ return result;
11429
+ }
11430
+ catch (err) {
11431
+ console.warn('[AuthContext] Failed to get contact:', err);
11432
+ return null;
11433
+ }
11434
+ }, [collectionId, user, contact]);
11435
+ // Update contact custom fields
11436
+ const updateContactCustomFields = React.useCallback(async (customFields) => {
11437
+ if (!collectionId || !contactId) {
11438
+ throw new Error('No contact to update. Ensure collectionId is provided and user is synced.');
11439
+ }
11440
+ console.log('[AuthContext] Updating contact custom fields:', contactId);
11441
+ const updated = await smartlinks__namespace.contact.update(collectionId, contactId, { customFields });
11442
+ setContact(updated);
11443
+ return updated;
11444
+ }, [collectionId, contactId]);
11445
+ // Initialize auth state
11268
11446
  React.useEffect(() => {
11269
- // Prevent concurrent initialization only
11270
11447
  if (initializingRef.current) {
11271
11448
  console.log('[AuthContext] Skipping initialization - already in progress');
11272
11449
  return;
@@ -11280,12 +11457,9 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
11280
11457
  console.log('[AuthContext] Proxy mode: checking for existing session via auth.getAccount()');
11281
11458
  try {
11282
11459
  const accountResponse = await smartlinks__namespace.auth.getAccount();
11283
- // auth.getAccount() always returns a response object, but uid will be
11284
- // empty/undefined if no user is logged in
11285
11460
  const accountAny = accountResponse;
11286
11461
  const hasValidSession = accountAny?.uid && accountAny.uid.length > 0;
11287
11462
  if (hasValidSession && isMounted) {
11288
- // User is logged in with valid account
11289
11463
  const userFromAccount = {
11290
11464
  uid: accountAny.uid,
11291
11465
  email: accountAny?.email,
@@ -11295,8 +11469,11 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
11295
11469
  setUser(userFromAccount);
11296
11470
  setAccountData(accountResponse);
11297
11471
  setAccountInfo(accountResponse);
11472
+ setIsVerified(true);
11298
11473
  console.log('[AuthContext] Proxy mode: initialized from parent account, uid:', accountAny.uid);
11299
- notifyAuthStateChange('LOGIN', userFromAccount, null, accountResponse, accountResponse);
11474
+ notifyAuthStateChange('LOGIN', userFromAccount, null, accountResponse, accountResponse, true);
11475
+ // Sync contact in background (proxy mode)
11476
+ syncContact(userFromAccount, accountResponse);
11300
11477
  }
11301
11478
  else if (isMounted) {
11302
11479
  console.log('[AuthContext] Proxy mode: no valid session (no uid), awaiting login');
@@ -11311,29 +11488,56 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
11311
11488
  }
11312
11489
  return;
11313
11490
  }
11314
- // STANDALONE MODE: Load from persistent storage
11491
+ // STANDALONE MODE: Optimistic restoration with background verification
11315
11492
  const storedToken = await tokenStorage.getToken();
11316
11493
  const storedUser = await tokenStorage.getUser();
11317
11494
  const storedAccountData = await tokenStorage.getAccountData();
11495
+ const storedContactId = await tokenStorage.getContactId();
11318
11496
  if (storedToken && storedUser) {
11319
- // Verify token FIRST before setting state
11497
+ if (isMounted) {
11498
+ setToken(storedToken.token);
11499
+ setUser(storedUser);
11500
+ setAccountData(storedAccountData);
11501
+ if (storedContactId) {
11502
+ setContactId(storedContactId);
11503
+ }
11504
+ console.log('[AuthContext] Session restored optimistically (pending verification)');
11505
+ notifyAuthStateChange('SESSION_RESTORED_OFFLINE', storedUser, storedToken.token, storedAccountData || null, null, false, null, storedContactId);
11506
+ }
11507
+ // BACKGROUND: Verify token
11320
11508
  try {
11321
- console.log('[AuthContext] Verifying stored token...');
11509
+ console.log('[AuthContext] Verifying stored token in background...');
11322
11510
  await smartlinks__namespace.auth.verifyToken(storedToken.token);
11323
- // Only set state if verification succeeded and component still mounted
11324
11511
  if (isMounted) {
11325
- setToken(storedToken.token);
11326
- setUser(storedUser);
11327
- setAccountData(storedAccountData);
11328
- console.log('[AuthContext] Session restored successfully');
11329
- // Notify subscribers that session was restored (critical for app to know we're logged in)
11330
- notifyAuthStateChange('LOGIN', storedUser, storedToken.token, storedAccountData || null);
11512
+ setIsVerified(true);
11513
+ pendingVerificationRef.current = false;
11514
+ console.log('[AuthContext] Session verified successfully');
11515
+ notifyAuthStateChange('SESSION_VERIFIED', storedUser, storedToken.token, storedAccountData || null, null, true, null, storedContactId);
11516
+ // Track session restore interaction (optional)
11517
+ if (interactionConfig?.sessionRestore) {
11518
+ trackInteraction('session_restore', storedUser.uid, storedContactId);
11519
+ }
11331
11520
  }
11332
11521
  }
11333
11522
  catch (err) {
11334
- console.warn('[AuthContext] Token verification failed, clearing stored credentials:', err);
11335
- await tokenStorage.clearAll();
11336
- // Don't set user state - leave as logged out
11523
+ if (isNetworkError(err)) {
11524
+ console.warn('[AuthContext] Network error during verification, will retry on reconnect:', err);
11525
+ pendingVerificationRef.current = true;
11526
+ }
11527
+ else {
11528
+ console.warn('[AuthContext] Token verification failed (auth error), clearing credentials:', err);
11529
+ if (isMounted) {
11530
+ setToken(null);
11531
+ setUser(null);
11532
+ setAccountData(null);
11533
+ setContactId(null);
11534
+ setContact(null);
11535
+ setIsVerified(false);
11536
+ pendingVerificationRef.current = false;
11537
+ }
11538
+ await tokenStorage.clearAll();
11539
+ notifyAuthStateChange('LOGOUT', null, null, null, null, false);
11540
+ }
11337
11541
  }
11338
11542
  }
11339
11543
  // Load cached account info if available
@@ -11355,18 +11559,16 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
11355
11559
  }
11356
11560
  };
11357
11561
  initializeAuth();
11358
- // Cleanup for hot reload
11359
11562
  return () => {
11360
11563
  isMounted = false;
11361
11564
  };
11362
- }, [proxyMode, notifyAuthStateChange]);
11565
+ }, [proxyMode, notifyAuthStateChange, isNetworkError, syncContact, trackInteraction, interactionConfig?.sessionRestore]);
11363
11566
  // Listen for parent auth state changes (proxy mode only)
11364
11567
  React.useEffect(() => {
11365
11568
  if (!proxyMode)
11366
11569
  return;
11367
11570
  console.log('[AuthContext] Proxy mode: setting up parent message listener');
11368
11571
  const handleParentMessage = (event) => {
11369
- // Handle auth state pushed from parent
11370
11572
  if (event.data?.type === 'smartlinks:authkit:state') {
11371
11573
  const { user: parentUser, accountData: parentAccountData, authenticated } = event.data.payload || {};
11372
11574
  console.log('[AuthContext] Proxy mode: received state from parent:', { authenticated });
@@ -11380,15 +11582,20 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
11380
11582
  setUser(userObj);
11381
11583
  setAccountData(parentAccountData || null);
11382
11584
  setAccountInfo(parentAccountData || null);
11383
- notifyAuthStateChange('CROSS_TAB_SYNC', userObj, null, parentAccountData || null, parentAccountData || null);
11585
+ setIsVerified(true);
11586
+ notifyAuthStateChange('CROSS_TAB_SYNC', userObj, null, parentAccountData || null, parentAccountData || null, true);
11587
+ // Sync contact on cross-tab state
11588
+ syncContact(userObj, parentAccountData);
11384
11589
  }
11385
11590
  else {
11386
- // Parent indicates no session / logged out
11387
11591
  setUser(null);
11388
11592
  setToken(null);
11389
11593
  setAccountData(null);
11390
11594
  setAccountInfo(null);
11391
- notifyAuthStateChange('LOGOUT', null, null, null, null);
11595
+ setContact(null);
11596
+ setContactId(null);
11597
+ setIsVerified(false);
11598
+ notifyAuthStateChange('LOGOUT', null, null, null, null, false);
11392
11599
  }
11393
11600
  }
11394
11601
  };
@@ -11397,54 +11604,60 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
11397
11604
  console.log('[AuthContext] Proxy mode: cleaning up parent message listener');
11398
11605
  window.removeEventListener('message', handleParentMessage);
11399
11606
  };
11400
- }, [proxyMode, notifyAuthStateChange]);
11607
+ }, [proxyMode, notifyAuthStateChange, syncContact]);
11401
11608
  // Cross-tab synchronization - standalone mode only
11402
11609
  React.useEffect(() => {
11403
11610
  if (proxyMode)
11404
- return; // Skip cross-tab sync in proxy mode
11611
+ return;
11405
11612
  console.log('[AuthContext] Setting up cross-tab synchronization');
11406
11613
  const unsubscribe = onStorageChange(async (event) => {
11407
11614
  console.log('[AuthContext] Cross-tab storage event:', event.type, event.key);
11408
11615
  try {
11409
11616
  if (event.type === 'clear') {
11410
- // Another tab cleared all storage (logout)
11411
11617
  console.log('[AuthContext] Detected logout in another tab');
11412
11618
  setToken(null);
11413
11619
  setUser(null);
11414
11620
  setAccountData(null);
11415
11621
  setAccountInfo(null);
11622
+ setContact(null);
11623
+ setContactId(null);
11624
+ setIsVerified(false);
11416
11625
  smartlinks__namespace.auth.logout();
11417
- notifyAuthStateChange('CROSS_TAB_SYNC', null, null, null);
11626
+ notifyAuthStateChange('CROSS_TAB_SYNC', null, null, null, null, false);
11418
11627
  }
11419
11628
  else if (event.type === 'remove' && (event.key === 'token' || event.key === 'user')) {
11420
- // Another tab removed token or user (logout)
11421
11629
  console.log('[AuthContext] Detected token/user removal in another tab');
11422
11630
  setToken(null);
11423
11631
  setUser(null);
11424
11632
  setAccountData(null);
11425
11633
  setAccountInfo(null);
11634
+ setContact(null);
11635
+ setContactId(null);
11636
+ setIsVerified(false);
11426
11637
  smartlinks__namespace.auth.logout();
11427
- notifyAuthStateChange('CROSS_TAB_SYNC', null, null, null);
11638
+ notifyAuthStateChange('CROSS_TAB_SYNC', null, null, null, null, false);
11428
11639
  }
11429
11640
  else if (event.type === 'set' && event.key === 'token') {
11430
- // Another tab set a new token (login)
11431
11641
  console.log('[AuthContext] Detected login in another tab');
11432
11642
  const storedToken = await tokenStorage.getToken();
11433
11643
  const storedUser = await tokenStorage.getUser();
11434
11644
  const storedAccountData = await tokenStorage.getAccountData();
11645
+ const storedContactId = await tokenStorage.getContactId();
11435
11646
  if (storedToken && storedUser) {
11436
11647
  setToken(storedToken.token);
11437
11648
  setUser(storedUser);
11438
11649
  setAccountData(storedAccountData);
11439
- // Set bearer token in global Smartlinks SDK
11650
+ if (storedContactId) {
11651
+ setContactId(storedContactId);
11652
+ }
11653
+ setIsVerified(true);
11440
11654
  smartlinks__namespace.auth.verifyToken(storedToken.token).catch(err => {
11441
11655
  console.warn('[AuthContext] Failed to restore bearer token from cross-tab sync:', err);
11442
11656
  });
11443
- notifyAuthStateChange('CROSS_TAB_SYNC', storedUser, storedToken.token, storedAccountData);
11657
+ notifyAuthStateChange('CROSS_TAB_SYNC', storedUser, storedToken.token, storedAccountData, null, true, null, storedContactId);
11444
11658
  }
11445
11659
  }
11446
11660
  else if (event.type === 'set' && event.key === 'account_info') {
11447
- // Another tab fetched fresh account info
11448
11661
  const cached = await tokenStorage.getAccountInfo();
11449
11662
  if (cached && !cached.isStale) {
11450
11663
  setAccountInfo(cached.data);
@@ -11461,7 +11674,7 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
11461
11674
  unsubscribe();
11462
11675
  };
11463
11676
  }, [proxyMode, notifyAuthStateChange]);
11464
- const login = React.useCallback(async (authToken, authUser, authAccountData) => {
11677
+ const login = React.useCallback(async (authToken, authUser, authAccountData, isNewUser) => {
11465
11678
  try {
11466
11679
  // Only persist to storage in standalone mode
11467
11680
  if (!proxyMode) {
@@ -11470,7 +11683,6 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
11470
11683
  if (authAccountData) {
11471
11684
  await tokenStorage.saveAccountData(authAccountData);
11472
11685
  }
11473
- // Set bearer token in global Smartlinks SDK via auth.verifyToken
11474
11686
  smartlinks__namespace.auth.verifyToken(authToken).catch(err => {
11475
11687
  console.warn('Failed to set bearer token on login:', err);
11476
11688
  });
@@ -11479,8 +11691,9 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
11479
11691
  setToken(authToken);
11480
11692
  setUser(authUser);
11481
11693
  setAccountData(authAccountData || null);
11694
+ setIsVerified(true);
11695
+ pendingVerificationRef.current = false;
11482
11696
  // Cross-iframe auth state synchronization
11483
- // Always notify parent frame of login (both modes, but especially important in proxy mode)
11484
11697
  if (smartlinks.iframe.isIframe()) {
11485
11698
  console.log('[AuthContext] Notifying parent of login via postMessage');
11486
11699
  smartlinks.iframe.sendParentCustom('smartlinks:authkit:login', {
@@ -11489,7 +11702,13 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
11489
11702
  accountData: authAccountData || null
11490
11703
  });
11491
11704
  }
11492
- notifyAuthStateChange('LOGIN', authUser, authToken, authAccountData || null);
11705
+ notifyAuthStateChange('LOGIN', authUser, authToken, authAccountData || null, null, true);
11706
+ // Sync contact (non-blocking)
11707
+ const newContactId = await syncContact(authUser, authAccountData);
11708
+ // Track interaction (non-blocking)
11709
+ trackInteraction(isNewUser ? 'signup' : 'login', authUser.uid, newContactId, {
11710
+ provider: authUser.email ? 'email' : 'phone',
11711
+ });
11493
11712
  // Optionally preload account info on login (standalone mode only)
11494
11713
  if (!proxyMode && preloadAccountInfo) {
11495
11714
  getAccount(true).catch(error => {
@@ -11501,8 +11720,10 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
11501
11720
  console.error('Failed to save auth data to storage:', error);
11502
11721
  throw error;
11503
11722
  }
11504
- }, [proxyMode, notifyAuthStateChange, preloadAccountInfo]);
11723
+ }, [proxyMode, notifyAuthStateChange, preloadAccountInfo, syncContact, trackInteraction]);
11505
11724
  const logout = React.useCallback(async () => {
11725
+ const currentUser = user;
11726
+ const currentContactId = contactId;
11506
11727
  try {
11507
11728
  // Only clear persistent storage in standalone mode
11508
11729
  if (!proxyMode) {
@@ -11514,21 +11735,27 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
11514
11735
  setUser(null);
11515
11736
  setAccountData(null);
11516
11737
  setAccountInfo(null);
11738
+ setContact(null);
11739
+ setContactId(null);
11740
+ setIsVerified(false);
11741
+ pendingVerificationRef.current = false;
11517
11742
  // Cross-iframe auth state synchronization
11518
- // Always notify parent frame of logout
11519
11743
  if (smartlinks.iframe.isIframe()) {
11520
11744
  console.log('[AuthContext] Notifying parent of logout via postMessage');
11521
11745
  smartlinks.iframe.sendParentCustom('smartlinks:authkit:logout', {});
11522
11746
  }
11523
- notifyAuthStateChange('LOGOUT', null, null, null);
11747
+ notifyAuthStateChange('LOGOUT', null, null, null, null, false);
11748
+ // Track logout interaction (fire and forget)
11749
+ if (currentUser && collectionId && shouldTrackInteractions) {
11750
+ trackInteraction('logout', currentUser.uid, currentContactId);
11751
+ }
11524
11752
  }
11525
11753
  catch (error) {
11526
11754
  console.error('Failed to clear auth data from storage:', error);
11527
11755
  }
11528
- }, [proxyMode, notifyAuthStateChange]);
11756
+ }, [proxyMode, notifyAuthStateChange, user, contactId, collectionId, shouldTrackInteractions, trackInteraction]);
11529
11757
  const getToken = React.useCallback(async () => {
11530
11758
  if (proxyMode) {
11531
- // In proxy mode, token is managed by parent - return memory state
11532
11759
  return token;
11533
11760
  }
11534
11761
  const storedToken = await tokenStorage.getToken();
@@ -11537,23 +11764,19 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
11537
11764
  const refreshToken = React.useCallback(async () => {
11538
11765
  throw new Error('Token refresh must be implemented via your backend API');
11539
11766
  }, []);
11540
- // Get account with intelligent caching (or direct parent fetch in proxy mode)
11541
11767
  const getAccount = React.useCallback(async (forceRefresh = false) => {
11542
11768
  try {
11543
11769
  if (proxyMode) {
11544
- // PROXY MODE: Always fetch from parent via proxied API, no local cache
11545
11770
  console.log('[AuthContext] Proxy mode: fetching account from parent');
11546
11771
  const freshAccountInfo = await smartlinks__namespace.auth.getAccount();
11547
11772
  setAccountInfo(freshAccountInfo);
11548
11773
  setAccountData(freshAccountInfo);
11549
- notifyAuthStateChange('ACCOUNT_REFRESH', user, token, freshAccountInfo, freshAccountInfo);
11774
+ notifyAuthStateChange('ACCOUNT_REFRESH', user, token, freshAccountInfo, freshAccountInfo, isVerified, contact, contactId);
11550
11775
  return freshAccountInfo;
11551
11776
  }
11552
- // STANDALONE MODE: Use caching
11553
11777
  if (!token) {
11554
11778
  throw new Error('Not authenticated. Please login first.');
11555
11779
  }
11556
- // Check cache unless force refresh
11557
11780
  if (!forceRefresh) {
11558
11781
  const cached = await tokenStorage.getAccountInfo();
11559
11782
  if (cached && !cached.isStale) {
@@ -11561,18 +11784,15 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
11561
11784
  return cached.data;
11562
11785
  }
11563
11786
  }
11564
- // Fetch fresh data from API
11565
11787
  console.log('[AuthContext] Fetching fresh account info from API');
11566
11788
  const freshAccountInfo = await smartlinks__namespace.auth.getAccount();
11567
- // Cache the fresh data
11568
11789
  await tokenStorage.saveAccountInfo(freshAccountInfo, accountCacheTTL);
11569
11790
  setAccountInfo(freshAccountInfo);
11570
- notifyAuthStateChange('ACCOUNT_REFRESH', user, token, accountData, freshAccountInfo);
11791
+ notifyAuthStateChange('ACCOUNT_REFRESH', user, token, accountData, freshAccountInfo, isVerified, contact, contactId);
11571
11792
  return freshAccountInfo;
11572
11793
  }
11573
11794
  catch (error) {
11574
11795
  console.error('[AuthContext] Failed to get account info:', error);
11575
- // Fallback to stale cache if API fails (standalone mode only)
11576
11796
  if (!proxyMode) {
11577
11797
  const cached = await tokenStorage.getAccountInfo();
11578
11798
  if (cached) {
@@ -11582,12 +11802,10 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
11582
11802
  }
11583
11803
  throw error;
11584
11804
  }
11585
- }, [proxyMode, token, accountCacheTTL, user, accountData, notifyAuthStateChange]);
11586
- // Convenience method for explicit refresh
11805
+ }, [proxyMode, token, accountCacheTTL, user, accountData, isVerified, contact, contactId, notifyAuthStateChange]);
11587
11806
  const refreshAccount = React.useCallback(async () => {
11588
11807
  return await getAccount(true);
11589
11808
  }, [getAccount]);
11590
- // Clear account cache (no-op in proxy mode)
11591
11809
  const clearAccountCache = React.useCallback(async () => {
11592
11810
  if (!proxyMode) {
11593
11811
  await tokenStorage.clearAccountInfo();
@@ -11596,19 +11814,77 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
11596
11814
  }, [proxyMode]);
11597
11815
  const onAuthStateChange = React.useCallback((callback) => {
11598
11816
  callbacksRef.current.add(callback);
11599
- // Return unsubscribe function
11600
11817
  return () => {
11601
11818
  callbacksRef.current.delete(callback);
11602
11819
  };
11603
11820
  }, []);
11821
+ const retryVerification = React.useCallback(async () => {
11822
+ if (!token || !user) {
11823
+ console.log('[AuthContext] No session to verify');
11824
+ return false;
11825
+ }
11826
+ if (isVerified) {
11827
+ console.log('[AuthContext] Session already verified');
11828
+ return true;
11829
+ }
11830
+ try {
11831
+ console.log('[AuthContext] Retrying session verification...');
11832
+ await smartlinks__namespace.auth.verifyToken(token);
11833
+ setIsVerified(true);
11834
+ pendingVerificationRef.current = false;
11835
+ console.log('[AuthContext] Session verified on retry');
11836
+ notifyAuthStateChange('SESSION_VERIFIED', user, token, accountData, accountInfo, true, contact, contactId);
11837
+ return true;
11838
+ }
11839
+ catch (err) {
11840
+ if (isNetworkError(err)) {
11841
+ console.warn('[AuthContext] Network still unavailable, will retry later');
11842
+ return false;
11843
+ }
11844
+ else {
11845
+ console.warn('[AuthContext] Session invalid on retry, logging out');
11846
+ await logout();
11847
+ return false;
11848
+ }
11849
+ }
11850
+ }, [token, user, isVerified, accountData, accountInfo, contact, contactId, notifyAuthStateChange, isNetworkError, logout]);
11851
+ // Online/offline event listener for auto-retry verification
11852
+ React.useEffect(() => {
11853
+ if (proxyMode)
11854
+ return;
11855
+ const handleOnline = () => {
11856
+ console.log('[AuthContext] Network reconnected');
11857
+ setIsOnline(true);
11858
+ if (pendingVerificationRef.current && token && user) {
11859
+ console.log('[AuthContext] Retrying pending verification after reconnect...');
11860
+ retryVerification();
11861
+ }
11862
+ };
11863
+ const handleOffline = () => {
11864
+ console.log('[AuthContext] Network disconnected');
11865
+ setIsOnline(false);
11866
+ };
11867
+ window.addEventListener('online', handleOnline);
11868
+ window.addEventListener('offline', handleOffline);
11869
+ return () => {
11870
+ window.removeEventListener('online', handleOnline);
11871
+ window.removeEventListener('offline', handleOffline);
11872
+ };
11873
+ }, [proxyMode, token, user, retryVerification]);
11604
11874
  const value = {
11605
11875
  user,
11606
11876
  token,
11607
11877
  accountData,
11608
11878
  accountInfo,
11609
11879
  isAuthenticated: !!user,
11880
+ isVerified,
11610
11881
  isLoading,
11882
+ isOnline,
11611
11883
  proxyMode,
11884
+ contact,
11885
+ contactId,
11886
+ getContact,
11887
+ updateContactCustomFields,
11612
11888
  login,
11613
11889
  logout,
11614
11890
  getToken,
@@ -11617,6 +11893,7 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
11617
11893
  refreshAccount,
11618
11894
  clearAccountCache,
11619
11895
  onAuthStateChange,
11896
+ retryVerification,
11620
11897
  };
11621
11898
  return jsxRuntime.jsx(AuthContext.Provider, { value: value, children: children });
11622
11899
  };
@@ -11734,10 +12011,21 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
11734
12011
  // Reinitialize Smartlinks SDK when apiEndpoint or proxyMode changes
11735
12012
  // IMPORTANT: Preserve bearer token during reinitialization
11736
12013
  React.useEffect(() => {
12014
+ console.log('[SmartlinksAuthUI] 🔧 SDK INIT useEffect triggered', {
12015
+ apiEndpoint,
12016
+ proxyMode,
12017
+ hasLogger: !!logger,
12018
+ timestamp: new Date().toISOString()
12019
+ });
11737
12020
  log.log('SDK reinitialize useEffect triggered', { apiEndpoint, proxyMode });
11738
12021
  setSdkReady(false); // Mark SDK as not ready during reinitialization
11739
12022
  const reinitializeWithToken = async () => {
11740
12023
  if (apiEndpoint) {
12024
+ console.log('[SmartlinksAuthUI] 🔧 Reinitializing SDK with:', {
12025
+ baseURL: apiEndpoint,
12026
+ proxyMode: proxyMode,
12027
+ ngrokSkipBrowserWarning: true
12028
+ });
11741
12029
  log.log('Reinitializing SDK with baseURL:', apiEndpoint, 'proxyMode:', proxyMode);
11742
12030
  // Get current token before reinitializing (only in standalone mode)
11743
12031
  const currentToken = !proxyMode ? await auth.getToken() : null;
@@ -11747,6 +12035,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
11747
12035
  ngrokSkipBrowserWarning: true,
11748
12036
  logger: logger, // Pass logger to SDK for verbose SDK logging
11749
12037
  });
12038
+ console.log('[SmartlinksAuthUI] ✅ SDK reinitialized, proxyMode:', proxyMode);
11750
12039
  log.log('SDK reinitialized successfully');
11751
12040
  // Restore bearer token after reinitialization using auth.verifyToken (standalone mode only)
11752
12041
  if (currentToken && !proxyMode) {
@@ -11755,14 +12044,17 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
11755
12044
  });
11756
12045
  }
11757
12046
  // Mark SDK as ready
12047
+ console.log('[SmartlinksAuthUI] ✅ Setting sdkReady=true (with apiEndpoint)');
11758
12048
  setSdkReady(true);
11759
12049
  }
11760
12050
  else if (proxyMode) {
11761
12051
  // In proxy mode without custom endpoint, SDK should already be initialized by parent
12052
+ console.log('[SmartlinksAuthUI] ⚠️ Proxy mode WITHOUT apiEndpoint - expecting SDK already initialized by parent');
11762
12053
  log.log('Proxy mode without apiEndpoint, SDK already initialized by parent');
11763
12054
  setSdkReady(true);
11764
12055
  }
11765
12056
  else {
12057
+ console.log('[SmartlinksAuthUI] ℹ️ No apiEndpoint, no proxyMode - SDK already initialized by App');
11766
12058
  log.log('No apiEndpoint, SDK already initialized by App');
11767
12059
  // SDK was initialized by App component, mark as ready
11768
12060
  setSdkReady(true);
@@ -11781,6 +12073,14 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
11781
12073
  };
11782
12074
  // Fetch UI configuration
11783
12075
  React.useEffect(() => {
12076
+ console.log('[SmartlinksAuthUI] 📋 CONFIG FETCH useEffect triggered', {
12077
+ skipConfigFetch,
12078
+ clientId,
12079
+ apiEndpoint,
12080
+ sdkReady,
12081
+ proxyMode,
12082
+ timestamp: new Date().toISOString()
12083
+ });
11784
12084
  log.log('Config fetch useEffect triggered', {
11785
12085
  skipConfigFetch,
11786
12086
  clientId,
@@ -11791,10 +12091,12 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
11791
12091
  });
11792
12092
  // Wait for SDK to be ready before fetching config
11793
12093
  if (!sdkReady) {
12094
+ console.log('[SmartlinksAuthUI] ⏳ SDK not ready yet, waiting before config fetch...');
11794
12095
  log.log('SDK not ready yet, waiting...');
11795
12096
  return;
11796
12097
  }
11797
12098
  if (skipConfigFetch) {
12099
+ console.log('[SmartlinksAuthUI] ⏭️ Skipping config fetch - skipConfigFetch is true');
11798
12100
  log.log('Skipping config fetch - skipConfigFetch is true');
11799
12101
  setConfig(customization || {});
11800
12102
  return;
@@ -11802,6 +12104,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
11802
12104
  const fetchConfig = async () => {
11803
12105
  // If no clientId provided, use default config immediately without API call
11804
12106
  if (!clientId) {
12107
+ console.log('[SmartlinksAuthUI] ⚠️ No clientId provided, using default config');
11805
12108
  log.log('No clientId provided, using default config');
11806
12109
  const defaultConfig = {
11807
12110
  ...DEFAULT_AUTH_CONFIG,
@@ -11820,21 +12123,28 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
11820
12123
  const age = Date.now() - timestamp;
11821
12124
  // Use cache if less than 1 hour old
11822
12125
  if (age < 3600000) {
12126
+ console.log('[SmartlinksAuthUI] 📦 Using cached config (age:', Math.round(age / 1000), 'seconds)');
11823
12127
  setConfig({ ...cachedConfig, ...customization });
11824
12128
  setConfigLoading(false);
11825
12129
  // Fetch in background to update cache
12130
+ console.log('[SmartlinksAuthUI] 🔄 Background refresh of config via SDK...');
11826
12131
  api.fetchConfig().then(freshConfig => {
12132
+ console.log('[SmartlinksAuthUI] ✅ Background config refresh complete');
11827
12133
  localStorage.setItem(cacheKey, JSON.stringify({
11828
12134
  config: freshConfig,
11829
12135
  timestamp: Date.now()
11830
12136
  }));
12137
+ }).catch(err => {
12138
+ console.log('[SmartlinksAuthUI] ❌ Background config refresh failed:', err);
11831
12139
  });
11832
12140
  return;
11833
12141
  }
11834
12142
  }
11835
12143
  // Fetch from API
12144
+ console.log('[SmartlinksAuthUI] 🌐 Fetching config via SDK for clientId:', clientId, 'proxyMode:', proxyMode);
11836
12145
  log.log('Fetching config from API for clientId:', clientId);
11837
12146
  const fetchedConfig = await api.fetchConfig();
12147
+ console.log('[SmartlinksAuthUI] ✅ Config fetched successfully:', fetchedConfig);
11838
12148
  log.log('Received config:', fetchedConfig);
11839
12149
  // Merge with customization props (props take precedence)
11840
12150
  const mergedConfig = { ...fetchedConfig, ...customization };
@@ -11846,6 +12156,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
11846
12156
  }));
11847
12157
  }
11848
12158
  catch (err) {
12159
+ console.log('[SmartlinksAuthUI] ❌ Config fetch failed:', err);
11849
12160
  log.error('Failed to fetch config:', err);
11850
12161
  setConfig(customization || {});
11851
12162
  }
@@ -11854,7 +12165,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
11854
12165
  }
11855
12166
  };
11856
12167
  fetchConfig();
11857
- }, [apiEndpoint, clientId, customization, skipConfigFetch, sdkReady, log]);
12168
+ }, [apiEndpoint, clientId, customization, skipConfigFetch, sdkReady, proxyMode, log]);
11858
12169
  // Reset showEmailForm when mode changes away from login/register
11859
12170
  React.useEffect(() => {
11860
12171
  if (mode !== 'login' && mode !== 'register') {
@@ -11895,7 +12206,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
11895
12206
  const verificationMode = response.emailVerificationMode || config?.emailVerification?.mode || 'verify-then-auto-login';
11896
12207
  if ((verificationMode === 'verify-then-auto-login' || verificationMode === 'immediate') && response.token) {
11897
12208
  // Auto-login modes: Log the user in immediately if token is provided
11898
- auth.login(response.token, response.user, response.accountData);
12209
+ auth.login(response.token, response.user, response.accountData, true);
11899
12210
  setAuthSuccess(true);
11900
12211
  setSuccessMessage('Email verified successfully! You are now logged in.');
11901
12212
  onAuthSuccess(response.token, response.user, response.accountData);
@@ -11938,7 +12249,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
11938
12249
  const response = await api.verifyMagicLink(token);
11939
12250
  // Auto-login with magic link if token is provided
11940
12251
  if (response.token) {
11941
- auth.login(response.token, response.user, response.accountData);
12252
+ auth.login(response.token, response.user, response.accountData, true);
11942
12253
  setAuthSuccess(true);
11943
12254
  setSuccessMessage('Magic link verified! You are now logged in.');
11944
12255
  onAuthSuccess(response.token, response.user, response.accountData);
@@ -12013,8 +12324,8 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
12013
12324
  if (mode === 'register') {
12014
12325
  // Handle different verification modes
12015
12326
  if (verificationMode === 'immediate' && response.token) {
12016
- // Immediate mode: Log in right away if token is provided
12017
- auth.login(response.token, response.user, response.accountData);
12327
+ // Immediate mode: Log in right away if token is provided (isNewUser=true for registration)
12328
+ auth.login(response.token, response.user, response.accountData, true);
12018
12329
  setAuthSuccess(true);
12019
12330
  const deadline = response.emailVerificationDeadline
12020
12331
  ? new Date(response.emailVerificationDeadline).toLocaleString()
@@ -12050,7 +12361,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
12050
12361
  if (response.requiresEmailVerification) {
12051
12362
  throw new Error('Please verify your email before logging in. Check your inbox for the verification link.');
12052
12363
  }
12053
- auth.login(response.token, response.user, response.accountData);
12364
+ auth.login(response.token, response.user, response.accountData, false);
12054
12365
  setAuthSuccess(true);
12055
12366
  setSuccessMessage('Login successful!');
12056
12367
  onAuthSuccess(response.token, response.user, response.accountData);
@@ -12210,7 +12521,8 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
12210
12521
  tokenType: 'access_token',
12211
12522
  });
12212
12523
  if (authResponse.token) {
12213
- auth.login(authResponse.token, authResponse.user, authResponse.accountData);
12524
+ // Google OAuth can be login or signup - use isNewUser flag from backend if available
12525
+ auth.login(authResponse.token, authResponse.user, authResponse.accountData, authResponse.isNewUser);
12214
12526
  setAuthSuccess(true);
12215
12527
  setSuccessMessage('Google login successful!');
12216
12528
  onAuthSuccess(authResponse.token, authResponse.user, authResponse.accountData);
@@ -12259,7 +12571,8 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
12259
12571
  const idToken = response.credential;
12260
12572
  const authResponse = await api.loginWithGoogle(idToken);
12261
12573
  if (authResponse.token) {
12262
- auth.login(authResponse.token, authResponse.user, authResponse.accountData);
12574
+ // Google OAuth can be login or signup - use isNewUser flag from backend if available
12575
+ auth.login(authResponse.token, authResponse.user, authResponse.accountData, authResponse.isNewUser);
12263
12576
  setAuthSuccess(true);
12264
12577
  setSuccessMessage('Google login successful!');
12265
12578
  onAuthSuccess(authResponse.token, authResponse.user, authResponse.accountData);
@@ -12320,7 +12633,8 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
12320
12633
  const response = await api.verifyPhoneCode(phoneNumber, verificationCode);
12321
12634
  // Update auth context with account data if token is provided
12322
12635
  if (response.token) {
12323
- auth.login(response.token, response.user, response.accountData);
12636
+ // Phone auth can be login or signup - use isNewUser flag from backend if available
12637
+ auth.login(response.token, response.user, response.accountData, response.isNewUser);
12324
12638
  onAuthSuccess(response.token, response.user, response.accountData);
12325
12639
  if (redirectUrl) {
12326
12640
  window.location.href = redirectUrl;