@proveanything/smartlinks-auth-ui 0.1.19 → 0.1.21
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/components/SmartlinksAuthUI.d.ts.map +1 -1
- package/dist/context/AuthContext.d.ts +10 -8
- package/dist/context/AuthContext.d.ts.map +1 -1
- package/dist/index.esm.js +345 -67
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +345 -67
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +32 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/tokenStorage.d.ts +3 -0
- package/dist/utils/tokenStorage.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -11128,6 +11128,7 @@ const TOKEN_KEY = 'token';
|
|
|
11128
11128
|
const USER_KEY = 'user';
|
|
11129
11129
|
const ACCOUNT_DATA_KEY = 'account_data';
|
|
11130
11130
|
const ACCOUNT_INFO_KEY = 'account_info';
|
|
11131
|
+
const CONTACT_ID_KEY = 'contact_id';
|
|
11131
11132
|
const ACCOUNT_INFO_TTL = 5 * 60 * 1000; // 5 minutes default
|
|
11132
11133
|
/**
|
|
11133
11134
|
* Token Storage Layer
|
|
@@ -11178,6 +11179,7 @@ const tokenStorage = {
|
|
|
11178
11179
|
await this.clearUser();
|
|
11179
11180
|
await this.clearAccountData();
|
|
11180
11181
|
await this.clearAccountInfo();
|
|
11182
|
+
await this.clearContactId();
|
|
11181
11183
|
},
|
|
11182
11184
|
async saveAccountData(data) {
|
|
11183
11185
|
const storage = await getStorage();
|
|
@@ -11215,20 +11217,69 @@ const tokenStorage = {
|
|
|
11215
11217
|
const storage = await getStorage();
|
|
11216
11218
|
await storage.removeItem(ACCOUNT_INFO_KEY);
|
|
11217
11219
|
},
|
|
11220
|
+
async saveContactId(contactId) {
|
|
11221
|
+
const storage = await getStorage();
|
|
11222
|
+
await storage.setItem(CONTACT_ID_KEY, contactId);
|
|
11223
|
+
},
|
|
11224
|
+
async getContactId() {
|
|
11225
|
+
const storage = await getStorage();
|
|
11226
|
+
return await storage.getItem(CONTACT_ID_KEY);
|
|
11227
|
+
},
|
|
11228
|
+
async clearContactId() {
|
|
11229
|
+
const storage = await getStorage();
|
|
11230
|
+
await storage.removeItem(CONTACT_ID_KEY);
|
|
11231
|
+
},
|
|
11218
11232
|
};
|
|
11219
11233
|
|
|
11220
11234
|
const AuthContext = createContext(undefined);
|
|
11221
|
-
const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 * 1000, preloadAccountInfo = false
|
|
11235
|
+
const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 * 1000, preloadAccountInfo = false,
|
|
11236
|
+
// Contact & Interaction features
|
|
11237
|
+
collectionId, enableContactSync, enableInteractionTracking, interactionAppId, interactionConfig, }) => {
|
|
11222
11238
|
const [user, setUser] = useState(null);
|
|
11223
11239
|
const [token, setToken] = useState(null);
|
|
11224
11240
|
const [accountData, setAccountData] = useState(null);
|
|
11225
11241
|
const [accountInfo, setAccountInfo] = useState(null);
|
|
11226
11242
|
const [isLoading, setIsLoading] = useState(true);
|
|
11243
|
+
const [isVerified, setIsVerified] = useState(false);
|
|
11244
|
+
const [isOnline, setIsOnline] = useState(typeof navigator !== 'undefined' ? navigator.onLine : true);
|
|
11245
|
+
// Contact state
|
|
11246
|
+
const [contact, setContact] = useState(null);
|
|
11247
|
+
const [contactId, setContactId] = useState(null);
|
|
11227
11248
|
const callbacksRef = useRef(new Set());
|
|
11228
|
-
// Initialization guard to prevent concurrent runs (NOT persisted across remounts)
|
|
11229
11249
|
const initializingRef = useRef(false);
|
|
11250
|
+
const pendingVerificationRef = useRef(false);
|
|
11251
|
+
// Default to enabled if collectionId is provided
|
|
11252
|
+
const shouldSyncContacts = enableContactSync ?? !!collectionId;
|
|
11253
|
+
const shouldTrackInteractions = enableInteractionTracking ?? !!collectionId;
|
|
11254
|
+
// Helper to detect if an error is a network error vs auth error
|
|
11255
|
+
const isNetworkError = useCallback((error) => {
|
|
11256
|
+
if (!error)
|
|
11257
|
+
return false;
|
|
11258
|
+
const errorMessage = error?.message?.toLowerCase() || '';
|
|
11259
|
+
const errorName = error?.name?.toLowerCase() || '';
|
|
11260
|
+
if (errorName === 'typeerror' && errorMessage.includes('fetch'))
|
|
11261
|
+
return true;
|
|
11262
|
+
if (errorMessage.includes('network') || errorMessage.includes('offline'))
|
|
11263
|
+
return true;
|
|
11264
|
+
if (errorMessage.includes('failed to fetch'))
|
|
11265
|
+
return true;
|
|
11266
|
+
if (errorMessage.includes('net::err_'))
|
|
11267
|
+
return true;
|
|
11268
|
+
if (errorMessage.includes('timeout'))
|
|
11269
|
+
return true;
|
|
11270
|
+
if (errorMessage.includes('dns'))
|
|
11271
|
+
return true;
|
|
11272
|
+
if (error?.code === 'ENOTFOUND' || error?.code === 'ETIMEDOUT')
|
|
11273
|
+
return true;
|
|
11274
|
+
const status = error?.status || error?.response?.status;
|
|
11275
|
+
if (status === 401 || status === 403)
|
|
11276
|
+
return false;
|
|
11277
|
+
if (typeof navigator !== 'undefined' && !navigator.onLine)
|
|
11278
|
+
return true;
|
|
11279
|
+
return false;
|
|
11280
|
+
}, []);
|
|
11230
11281
|
// Notify all subscribers of auth state changes
|
|
11231
|
-
const notifyAuthStateChange = useCallback((type, currentUser, currentToken, currentAccountData, currentAccountInfo) => {
|
|
11282
|
+
const notifyAuthStateChange = useCallback((type, currentUser, currentToken, currentAccountData, currentAccountInfo, verified, currentContact, currentContactId) => {
|
|
11232
11283
|
callbacksRef.current.forEach(callback => {
|
|
11233
11284
|
try {
|
|
11234
11285
|
callback({
|
|
@@ -11236,7 +11287,10 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
|
|
|
11236
11287
|
user: currentUser,
|
|
11237
11288
|
token: currentToken,
|
|
11238
11289
|
accountData: currentAccountData,
|
|
11239
|
-
accountInfo: currentAccountInfo
|
|
11290
|
+
accountInfo: currentAccountInfo,
|
|
11291
|
+
isVerified: verified,
|
|
11292
|
+
contact: currentContact,
|
|
11293
|
+
contactId: currentContactId,
|
|
11240
11294
|
});
|
|
11241
11295
|
}
|
|
11242
11296
|
catch (error) {
|
|
@@ -11244,9 +11298,128 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
|
|
|
11244
11298
|
}
|
|
11245
11299
|
});
|
|
11246
11300
|
}, []);
|
|
11247
|
-
//
|
|
11301
|
+
// Sync contact to Smartlinks (non-blocking)
|
|
11302
|
+
const syncContact = useCallback(async (authUser, customFields) => {
|
|
11303
|
+
if (!collectionId || !shouldSyncContacts) {
|
|
11304
|
+
console.log('[AuthContext] Contact sync skipped: no collectionId or disabled');
|
|
11305
|
+
return null;
|
|
11306
|
+
}
|
|
11307
|
+
try {
|
|
11308
|
+
console.log('[AuthContext] Syncing contact for user:', authUser.uid);
|
|
11309
|
+
const result = await smartlinks.contact.publicUpsert(collectionId, {
|
|
11310
|
+
userId: authUser.uid,
|
|
11311
|
+
email: authUser.email,
|
|
11312
|
+
displayName: authUser.displayName,
|
|
11313
|
+
phone: authUser.phoneNumber,
|
|
11314
|
+
customFields: customFields || {},
|
|
11315
|
+
source: 'authkit',
|
|
11316
|
+
});
|
|
11317
|
+
console.log('[AuthContext] Contact synced:', result.contactId);
|
|
11318
|
+
// Store contact ID locally
|
|
11319
|
+
if (!proxyMode) {
|
|
11320
|
+
await tokenStorage.saveContactId(result.contactId);
|
|
11321
|
+
}
|
|
11322
|
+
setContactId(result.contactId);
|
|
11323
|
+
// Fetch full contact to get customFields
|
|
11324
|
+
try {
|
|
11325
|
+
const fullContact = await smartlinks.contact.lookup(collectionId, {
|
|
11326
|
+
email: authUser.email
|
|
11327
|
+
});
|
|
11328
|
+
setContact(fullContact);
|
|
11329
|
+
notifyAuthStateChange('CONTACT_SYNCED', authUser, token, accountData, accountInfo, isVerified, fullContact, result.contactId);
|
|
11330
|
+
}
|
|
11331
|
+
catch (lookupErr) {
|
|
11332
|
+
console.warn('[AuthContext] Failed to lookup full contact:', lookupErr);
|
|
11333
|
+
}
|
|
11334
|
+
return result.contactId;
|
|
11335
|
+
}
|
|
11336
|
+
catch (err) {
|
|
11337
|
+
console.warn('[AuthContext] Contact sync failed (non-blocking):', err);
|
|
11338
|
+
return null;
|
|
11339
|
+
}
|
|
11340
|
+
}, [collectionId, shouldSyncContacts, proxyMode, token, accountData, accountInfo, isVerified, notifyAuthStateChange]);
|
|
11341
|
+
// Track interaction event (non-blocking)
|
|
11342
|
+
const trackInteraction = useCallback(async (eventType, userId, currentContactId, metadata) => {
|
|
11343
|
+
if (!collectionId || !shouldTrackInteractions) {
|
|
11344
|
+
console.log('[AuthContext] Interaction tracking skipped: no collectionId or disabled');
|
|
11345
|
+
return;
|
|
11346
|
+
}
|
|
11347
|
+
const interactionIdMap = {
|
|
11348
|
+
login: interactionConfig?.login || 'authkit.login',
|
|
11349
|
+
logout: interactionConfig?.logout || 'authkit.logout',
|
|
11350
|
+
signup: interactionConfig?.signup || 'authkit.signup',
|
|
11351
|
+
session_restore: interactionConfig?.sessionRestore,
|
|
11352
|
+
};
|
|
11353
|
+
const interactionId = interactionIdMap[eventType];
|
|
11354
|
+
if (!interactionId) {
|
|
11355
|
+
console.log(`[AuthContext] No interaction ID for ${eventType}, skipping`);
|
|
11356
|
+
return;
|
|
11357
|
+
}
|
|
11358
|
+
try {
|
|
11359
|
+
console.log(`[AuthContext] Tracking interaction: ${interactionId}`);
|
|
11360
|
+
await smartlinks.interactions.submitPublicEvent(collectionId, {
|
|
11361
|
+
collectionId,
|
|
11362
|
+
interactionId,
|
|
11363
|
+
userId,
|
|
11364
|
+
contactId: currentContactId || undefined,
|
|
11365
|
+
appId: interactionAppId,
|
|
11366
|
+
eventType,
|
|
11367
|
+
outcome: 'completed',
|
|
11368
|
+
metadata: {
|
|
11369
|
+
...metadata,
|
|
11370
|
+
timestamp: new Date().toISOString(),
|
|
11371
|
+
source: 'authkit',
|
|
11372
|
+
},
|
|
11373
|
+
});
|
|
11374
|
+
console.log(`[AuthContext] Tracked interaction: ${interactionId}`);
|
|
11375
|
+
notifyAuthStateChange('INTERACTION_TRACKED', user, token, accountData, accountInfo, isVerified, contact, contactId);
|
|
11376
|
+
}
|
|
11377
|
+
catch (err) {
|
|
11378
|
+
console.warn('[AuthContext] Interaction tracking failed (non-blocking):', err);
|
|
11379
|
+
}
|
|
11380
|
+
}, [collectionId, shouldTrackInteractions, interactionAppId, interactionConfig, user, token, accountData, accountInfo, isVerified, contact, contactId, notifyAuthStateChange]);
|
|
11381
|
+
// Get contact (with optional refresh)
|
|
11382
|
+
const getContact = useCallback(async (forceRefresh = false) => {
|
|
11383
|
+
if (!collectionId) {
|
|
11384
|
+
console.log('[AuthContext] getContact: no collectionId');
|
|
11385
|
+
return null;
|
|
11386
|
+
}
|
|
11387
|
+
// Need either email or userId to lookup contact
|
|
11388
|
+
if (!user?.email && !user?.uid) {
|
|
11389
|
+
console.log('[AuthContext] getContact: no user email or uid');
|
|
11390
|
+
return null;
|
|
11391
|
+
}
|
|
11392
|
+
if (contact && !forceRefresh) {
|
|
11393
|
+
return contact;
|
|
11394
|
+
}
|
|
11395
|
+
try {
|
|
11396
|
+
// Prefer email lookup, fallback to userId for phone-only users
|
|
11397
|
+
const lookupParams = user.email
|
|
11398
|
+
? { email: user.email }
|
|
11399
|
+
: { userId: user.uid };
|
|
11400
|
+
console.log('[AuthContext] Fetching contact with:', lookupParams);
|
|
11401
|
+
const result = await smartlinks.contact.lookup(collectionId, lookupParams);
|
|
11402
|
+
setContact(result);
|
|
11403
|
+
setContactId(result.contactId);
|
|
11404
|
+
return result;
|
|
11405
|
+
}
|
|
11406
|
+
catch (err) {
|
|
11407
|
+
console.warn('[AuthContext] Failed to get contact:', err);
|
|
11408
|
+
return null;
|
|
11409
|
+
}
|
|
11410
|
+
}, [collectionId, user, contact]);
|
|
11411
|
+
// Update contact custom fields
|
|
11412
|
+
const updateContactCustomFields = useCallback(async (customFields) => {
|
|
11413
|
+
if (!collectionId || !contactId) {
|
|
11414
|
+
throw new Error('No contact to update. Ensure collectionId is provided and user is synced.');
|
|
11415
|
+
}
|
|
11416
|
+
console.log('[AuthContext] Updating contact custom fields:', contactId);
|
|
11417
|
+
const updated = await smartlinks.contact.update(collectionId, contactId, { customFields });
|
|
11418
|
+
setContact(updated);
|
|
11419
|
+
return updated;
|
|
11420
|
+
}, [collectionId, contactId]);
|
|
11421
|
+
// Initialize auth state
|
|
11248
11422
|
useEffect(() => {
|
|
11249
|
-
// Prevent concurrent initialization only
|
|
11250
11423
|
if (initializingRef.current) {
|
|
11251
11424
|
console.log('[AuthContext] Skipping initialization - already in progress');
|
|
11252
11425
|
return;
|
|
@@ -11260,12 +11433,9 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
|
|
|
11260
11433
|
console.log('[AuthContext] Proxy mode: checking for existing session via auth.getAccount()');
|
|
11261
11434
|
try {
|
|
11262
11435
|
const accountResponse = await smartlinks.auth.getAccount();
|
|
11263
|
-
// auth.getAccount() always returns a response object, but uid will be
|
|
11264
|
-
// empty/undefined if no user is logged in
|
|
11265
11436
|
const accountAny = accountResponse;
|
|
11266
11437
|
const hasValidSession = accountAny?.uid && accountAny.uid.length > 0;
|
|
11267
11438
|
if (hasValidSession && isMounted) {
|
|
11268
|
-
// User is logged in with valid account
|
|
11269
11439
|
const userFromAccount = {
|
|
11270
11440
|
uid: accountAny.uid,
|
|
11271
11441
|
email: accountAny?.email,
|
|
@@ -11275,8 +11445,11 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
|
|
|
11275
11445
|
setUser(userFromAccount);
|
|
11276
11446
|
setAccountData(accountResponse);
|
|
11277
11447
|
setAccountInfo(accountResponse);
|
|
11448
|
+
setIsVerified(true);
|
|
11278
11449
|
console.log('[AuthContext] Proxy mode: initialized from parent account, uid:', accountAny.uid);
|
|
11279
|
-
notifyAuthStateChange('LOGIN', userFromAccount, null, accountResponse, accountResponse);
|
|
11450
|
+
notifyAuthStateChange('LOGIN', userFromAccount, null, accountResponse, accountResponse, true);
|
|
11451
|
+
// Sync contact in background (proxy mode)
|
|
11452
|
+
syncContact(userFromAccount, accountResponse);
|
|
11280
11453
|
}
|
|
11281
11454
|
else if (isMounted) {
|
|
11282
11455
|
console.log('[AuthContext] Proxy mode: no valid session (no uid), awaiting login');
|
|
@@ -11291,27 +11464,56 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
|
|
|
11291
11464
|
}
|
|
11292
11465
|
return;
|
|
11293
11466
|
}
|
|
11294
|
-
// STANDALONE MODE:
|
|
11467
|
+
// STANDALONE MODE: Optimistic restoration with background verification
|
|
11295
11468
|
const storedToken = await tokenStorage.getToken();
|
|
11296
11469
|
const storedUser = await tokenStorage.getUser();
|
|
11297
11470
|
const storedAccountData = await tokenStorage.getAccountData();
|
|
11471
|
+
const storedContactId = await tokenStorage.getContactId();
|
|
11298
11472
|
if (storedToken && storedUser) {
|
|
11299
|
-
|
|
11473
|
+
if (isMounted) {
|
|
11474
|
+
setToken(storedToken.token);
|
|
11475
|
+
setUser(storedUser);
|
|
11476
|
+
setAccountData(storedAccountData);
|
|
11477
|
+
if (storedContactId) {
|
|
11478
|
+
setContactId(storedContactId);
|
|
11479
|
+
}
|
|
11480
|
+
console.log('[AuthContext] Session restored optimistically (pending verification)');
|
|
11481
|
+
notifyAuthStateChange('SESSION_RESTORED_OFFLINE', storedUser, storedToken.token, storedAccountData || null, null, false, null, storedContactId);
|
|
11482
|
+
}
|
|
11483
|
+
// BACKGROUND: Verify token
|
|
11300
11484
|
try {
|
|
11301
|
-
console.log('[AuthContext] Verifying stored token...');
|
|
11485
|
+
console.log('[AuthContext] Verifying stored token in background...');
|
|
11302
11486
|
await smartlinks.auth.verifyToken(storedToken.token);
|
|
11303
|
-
// Only set state if verification succeeded and component still mounted
|
|
11304
11487
|
if (isMounted) {
|
|
11305
|
-
|
|
11306
|
-
|
|
11307
|
-
|
|
11308
|
-
|
|
11488
|
+
setIsVerified(true);
|
|
11489
|
+
pendingVerificationRef.current = false;
|
|
11490
|
+
console.log('[AuthContext] Session verified successfully');
|
|
11491
|
+
notifyAuthStateChange('SESSION_VERIFIED', storedUser, storedToken.token, storedAccountData || null, null, true, null, storedContactId);
|
|
11492
|
+
// Track session restore interaction (optional)
|
|
11493
|
+
if (interactionConfig?.sessionRestore) {
|
|
11494
|
+
trackInteraction('session_restore', storedUser.uid, storedContactId);
|
|
11495
|
+
}
|
|
11309
11496
|
}
|
|
11310
11497
|
}
|
|
11311
11498
|
catch (err) {
|
|
11312
|
-
|
|
11313
|
-
|
|
11314
|
-
|
|
11499
|
+
if (isNetworkError(err)) {
|
|
11500
|
+
console.warn('[AuthContext] Network error during verification, will retry on reconnect:', err);
|
|
11501
|
+
pendingVerificationRef.current = true;
|
|
11502
|
+
}
|
|
11503
|
+
else {
|
|
11504
|
+
console.warn('[AuthContext] Token verification failed (auth error), clearing credentials:', err);
|
|
11505
|
+
if (isMounted) {
|
|
11506
|
+
setToken(null);
|
|
11507
|
+
setUser(null);
|
|
11508
|
+
setAccountData(null);
|
|
11509
|
+
setContactId(null);
|
|
11510
|
+
setContact(null);
|
|
11511
|
+
setIsVerified(false);
|
|
11512
|
+
pendingVerificationRef.current = false;
|
|
11513
|
+
}
|
|
11514
|
+
await tokenStorage.clearAll();
|
|
11515
|
+
notifyAuthStateChange('LOGOUT', null, null, null, null, false);
|
|
11516
|
+
}
|
|
11315
11517
|
}
|
|
11316
11518
|
}
|
|
11317
11519
|
// Load cached account info if available
|
|
@@ -11333,18 +11535,16 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
|
|
|
11333
11535
|
}
|
|
11334
11536
|
};
|
|
11335
11537
|
initializeAuth();
|
|
11336
|
-
// Cleanup for hot reload
|
|
11337
11538
|
return () => {
|
|
11338
11539
|
isMounted = false;
|
|
11339
11540
|
};
|
|
11340
|
-
}, [proxyMode, notifyAuthStateChange]);
|
|
11541
|
+
}, [proxyMode, notifyAuthStateChange, isNetworkError, syncContact, trackInteraction, interactionConfig?.sessionRestore]);
|
|
11341
11542
|
// Listen for parent auth state changes (proxy mode only)
|
|
11342
11543
|
useEffect(() => {
|
|
11343
11544
|
if (!proxyMode)
|
|
11344
11545
|
return;
|
|
11345
11546
|
console.log('[AuthContext] Proxy mode: setting up parent message listener');
|
|
11346
11547
|
const handleParentMessage = (event) => {
|
|
11347
|
-
// Handle auth state pushed from parent
|
|
11348
11548
|
if (event.data?.type === 'smartlinks:authkit:state') {
|
|
11349
11549
|
const { user: parentUser, accountData: parentAccountData, authenticated } = event.data.payload || {};
|
|
11350
11550
|
console.log('[AuthContext] Proxy mode: received state from parent:', { authenticated });
|
|
@@ -11358,15 +11558,20 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
|
|
|
11358
11558
|
setUser(userObj);
|
|
11359
11559
|
setAccountData(parentAccountData || null);
|
|
11360
11560
|
setAccountInfo(parentAccountData || null);
|
|
11361
|
-
|
|
11561
|
+
setIsVerified(true);
|
|
11562
|
+
notifyAuthStateChange('CROSS_TAB_SYNC', userObj, null, parentAccountData || null, parentAccountData || null, true);
|
|
11563
|
+
// Sync contact on cross-tab state
|
|
11564
|
+
syncContact(userObj, parentAccountData);
|
|
11362
11565
|
}
|
|
11363
11566
|
else {
|
|
11364
|
-
// Parent indicates no session / logged out
|
|
11365
11567
|
setUser(null);
|
|
11366
11568
|
setToken(null);
|
|
11367
11569
|
setAccountData(null);
|
|
11368
11570
|
setAccountInfo(null);
|
|
11369
|
-
|
|
11571
|
+
setContact(null);
|
|
11572
|
+
setContactId(null);
|
|
11573
|
+
setIsVerified(false);
|
|
11574
|
+
notifyAuthStateChange('LOGOUT', null, null, null, null, false);
|
|
11370
11575
|
}
|
|
11371
11576
|
}
|
|
11372
11577
|
};
|
|
@@ -11375,54 +11580,60 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
|
|
|
11375
11580
|
console.log('[AuthContext] Proxy mode: cleaning up parent message listener');
|
|
11376
11581
|
window.removeEventListener('message', handleParentMessage);
|
|
11377
11582
|
};
|
|
11378
|
-
}, [proxyMode, notifyAuthStateChange]);
|
|
11583
|
+
}, [proxyMode, notifyAuthStateChange, syncContact]);
|
|
11379
11584
|
// Cross-tab synchronization - standalone mode only
|
|
11380
11585
|
useEffect(() => {
|
|
11381
11586
|
if (proxyMode)
|
|
11382
|
-
return;
|
|
11587
|
+
return;
|
|
11383
11588
|
console.log('[AuthContext] Setting up cross-tab synchronization');
|
|
11384
11589
|
const unsubscribe = onStorageChange(async (event) => {
|
|
11385
11590
|
console.log('[AuthContext] Cross-tab storage event:', event.type, event.key);
|
|
11386
11591
|
try {
|
|
11387
11592
|
if (event.type === 'clear') {
|
|
11388
|
-
// Another tab cleared all storage (logout)
|
|
11389
11593
|
console.log('[AuthContext] Detected logout in another tab');
|
|
11390
11594
|
setToken(null);
|
|
11391
11595
|
setUser(null);
|
|
11392
11596
|
setAccountData(null);
|
|
11393
11597
|
setAccountInfo(null);
|
|
11598
|
+
setContact(null);
|
|
11599
|
+
setContactId(null);
|
|
11600
|
+
setIsVerified(false);
|
|
11394
11601
|
smartlinks.auth.logout();
|
|
11395
|
-
notifyAuthStateChange('CROSS_TAB_SYNC', null, null, null);
|
|
11602
|
+
notifyAuthStateChange('CROSS_TAB_SYNC', null, null, null, null, false);
|
|
11396
11603
|
}
|
|
11397
11604
|
else if (event.type === 'remove' && (event.key === 'token' || event.key === 'user')) {
|
|
11398
|
-
// Another tab removed token or user (logout)
|
|
11399
11605
|
console.log('[AuthContext] Detected token/user removal in another tab');
|
|
11400
11606
|
setToken(null);
|
|
11401
11607
|
setUser(null);
|
|
11402
11608
|
setAccountData(null);
|
|
11403
11609
|
setAccountInfo(null);
|
|
11610
|
+
setContact(null);
|
|
11611
|
+
setContactId(null);
|
|
11612
|
+
setIsVerified(false);
|
|
11404
11613
|
smartlinks.auth.logout();
|
|
11405
|
-
notifyAuthStateChange('CROSS_TAB_SYNC', null, null, null);
|
|
11614
|
+
notifyAuthStateChange('CROSS_TAB_SYNC', null, null, null, null, false);
|
|
11406
11615
|
}
|
|
11407
11616
|
else if (event.type === 'set' && event.key === 'token') {
|
|
11408
|
-
// Another tab set a new token (login)
|
|
11409
11617
|
console.log('[AuthContext] Detected login in another tab');
|
|
11410
11618
|
const storedToken = await tokenStorage.getToken();
|
|
11411
11619
|
const storedUser = await tokenStorage.getUser();
|
|
11412
11620
|
const storedAccountData = await tokenStorage.getAccountData();
|
|
11621
|
+
const storedContactId = await tokenStorage.getContactId();
|
|
11413
11622
|
if (storedToken && storedUser) {
|
|
11414
11623
|
setToken(storedToken.token);
|
|
11415
11624
|
setUser(storedUser);
|
|
11416
11625
|
setAccountData(storedAccountData);
|
|
11417
|
-
|
|
11626
|
+
if (storedContactId) {
|
|
11627
|
+
setContactId(storedContactId);
|
|
11628
|
+
}
|
|
11629
|
+
setIsVerified(true);
|
|
11418
11630
|
smartlinks.auth.verifyToken(storedToken.token).catch(err => {
|
|
11419
11631
|
console.warn('[AuthContext] Failed to restore bearer token from cross-tab sync:', err);
|
|
11420
11632
|
});
|
|
11421
|
-
notifyAuthStateChange('CROSS_TAB_SYNC', storedUser, storedToken.token, storedAccountData);
|
|
11633
|
+
notifyAuthStateChange('CROSS_TAB_SYNC', storedUser, storedToken.token, storedAccountData, null, true, null, storedContactId);
|
|
11422
11634
|
}
|
|
11423
11635
|
}
|
|
11424
11636
|
else if (event.type === 'set' && event.key === 'account_info') {
|
|
11425
|
-
// Another tab fetched fresh account info
|
|
11426
11637
|
const cached = await tokenStorage.getAccountInfo();
|
|
11427
11638
|
if (cached && !cached.isStale) {
|
|
11428
11639
|
setAccountInfo(cached.data);
|
|
@@ -11439,7 +11650,7 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
|
|
|
11439
11650
|
unsubscribe();
|
|
11440
11651
|
};
|
|
11441
11652
|
}, [proxyMode, notifyAuthStateChange]);
|
|
11442
|
-
const login = useCallback(async (authToken, authUser, authAccountData) => {
|
|
11653
|
+
const login = useCallback(async (authToken, authUser, authAccountData, isNewUser) => {
|
|
11443
11654
|
try {
|
|
11444
11655
|
// Only persist to storage in standalone mode
|
|
11445
11656
|
if (!proxyMode) {
|
|
@@ -11448,7 +11659,6 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
|
|
|
11448
11659
|
if (authAccountData) {
|
|
11449
11660
|
await tokenStorage.saveAccountData(authAccountData);
|
|
11450
11661
|
}
|
|
11451
|
-
// Set bearer token in global Smartlinks SDK via auth.verifyToken
|
|
11452
11662
|
smartlinks.auth.verifyToken(authToken).catch(err => {
|
|
11453
11663
|
console.warn('Failed to set bearer token on login:', err);
|
|
11454
11664
|
});
|
|
@@ -11457,8 +11667,9 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
|
|
|
11457
11667
|
setToken(authToken);
|
|
11458
11668
|
setUser(authUser);
|
|
11459
11669
|
setAccountData(authAccountData || null);
|
|
11670
|
+
setIsVerified(true);
|
|
11671
|
+
pendingVerificationRef.current = false;
|
|
11460
11672
|
// Cross-iframe auth state synchronization
|
|
11461
|
-
// Always notify parent frame of login (both modes, but especially important in proxy mode)
|
|
11462
11673
|
if (iframe.isIframe()) {
|
|
11463
11674
|
console.log('[AuthContext] Notifying parent of login via postMessage');
|
|
11464
11675
|
iframe.sendParentCustom('smartlinks:authkit:login', {
|
|
@@ -11467,7 +11678,13 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
|
|
|
11467
11678
|
accountData: authAccountData || null
|
|
11468
11679
|
});
|
|
11469
11680
|
}
|
|
11470
|
-
notifyAuthStateChange('LOGIN', authUser, authToken, authAccountData || null);
|
|
11681
|
+
notifyAuthStateChange('LOGIN', authUser, authToken, authAccountData || null, null, true);
|
|
11682
|
+
// Sync contact (non-blocking)
|
|
11683
|
+
const newContactId = await syncContact(authUser, authAccountData);
|
|
11684
|
+
// Track interaction (non-blocking)
|
|
11685
|
+
trackInteraction(isNewUser ? 'signup' : 'login', authUser.uid, newContactId, {
|
|
11686
|
+
provider: authUser.email ? 'email' : 'phone',
|
|
11687
|
+
});
|
|
11471
11688
|
// Optionally preload account info on login (standalone mode only)
|
|
11472
11689
|
if (!proxyMode && preloadAccountInfo) {
|
|
11473
11690
|
getAccount(true).catch(error => {
|
|
@@ -11479,8 +11696,10 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
|
|
|
11479
11696
|
console.error('Failed to save auth data to storage:', error);
|
|
11480
11697
|
throw error;
|
|
11481
11698
|
}
|
|
11482
|
-
}, [proxyMode, notifyAuthStateChange, preloadAccountInfo]);
|
|
11699
|
+
}, [proxyMode, notifyAuthStateChange, preloadAccountInfo, syncContact, trackInteraction]);
|
|
11483
11700
|
const logout = useCallback(async () => {
|
|
11701
|
+
const currentUser = user;
|
|
11702
|
+
const currentContactId = contactId;
|
|
11484
11703
|
try {
|
|
11485
11704
|
// Only clear persistent storage in standalone mode
|
|
11486
11705
|
if (!proxyMode) {
|
|
@@ -11492,21 +11711,27 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
|
|
|
11492
11711
|
setUser(null);
|
|
11493
11712
|
setAccountData(null);
|
|
11494
11713
|
setAccountInfo(null);
|
|
11714
|
+
setContact(null);
|
|
11715
|
+
setContactId(null);
|
|
11716
|
+
setIsVerified(false);
|
|
11717
|
+
pendingVerificationRef.current = false;
|
|
11495
11718
|
// Cross-iframe auth state synchronization
|
|
11496
|
-
// Always notify parent frame of logout
|
|
11497
11719
|
if (iframe.isIframe()) {
|
|
11498
11720
|
console.log('[AuthContext] Notifying parent of logout via postMessage');
|
|
11499
11721
|
iframe.sendParentCustom('smartlinks:authkit:logout', {});
|
|
11500
11722
|
}
|
|
11501
|
-
notifyAuthStateChange('LOGOUT', null, null, null);
|
|
11723
|
+
notifyAuthStateChange('LOGOUT', null, null, null, null, false);
|
|
11724
|
+
// Track logout interaction (fire and forget)
|
|
11725
|
+
if (currentUser && collectionId && shouldTrackInteractions) {
|
|
11726
|
+
trackInteraction('logout', currentUser.uid, currentContactId);
|
|
11727
|
+
}
|
|
11502
11728
|
}
|
|
11503
11729
|
catch (error) {
|
|
11504
11730
|
console.error('Failed to clear auth data from storage:', error);
|
|
11505
11731
|
}
|
|
11506
|
-
}, [proxyMode, notifyAuthStateChange]);
|
|
11732
|
+
}, [proxyMode, notifyAuthStateChange, user, contactId, collectionId, shouldTrackInteractions, trackInteraction]);
|
|
11507
11733
|
const getToken = useCallback(async () => {
|
|
11508
11734
|
if (proxyMode) {
|
|
11509
|
-
// In proxy mode, token is managed by parent - return memory state
|
|
11510
11735
|
return token;
|
|
11511
11736
|
}
|
|
11512
11737
|
const storedToken = await tokenStorage.getToken();
|
|
@@ -11515,23 +11740,19 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
|
|
|
11515
11740
|
const refreshToken = useCallback(async () => {
|
|
11516
11741
|
throw new Error('Token refresh must be implemented via your backend API');
|
|
11517
11742
|
}, []);
|
|
11518
|
-
// Get account with intelligent caching (or direct parent fetch in proxy mode)
|
|
11519
11743
|
const getAccount = useCallback(async (forceRefresh = false) => {
|
|
11520
11744
|
try {
|
|
11521
11745
|
if (proxyMode) {
|
|
11522
|
-
// PROXY MODE: Always fetch from parent via proxied API, no local cache
|
|
11523
11746
|
console.log('[AuthContext] Proxy mode: fetching account from parent');
|
|
11524
11747
|
const freshAccountInfo = await smartlinks.auth.getAccount();
|
|
11525
11748
|
setAccountInfo(freshAccountInfo);
|
|
11526
11749
|
setAccountData(freshAccountInfo);
|
|
11527
|
-
notifyAuthStateChange('ACCOUNT_REFRESH', user, token, freshAccountInfo, freshAccountInfo);
|
|
11750
|
+
notifyAuthStateChange('ACCOUNT_REFRESH', user, token, freshAccountInfo, freshAccountInfo, isVerified, contact, contactId);
|
|
11528
11751
|
return freshAccountInfo;
|
|
11529
11752
|
}
|
|
11530
|
-
// STANDALONE MODE: Use caching
|
|
11531
11753
|
if (!token) {
|
|
11532
11754
|
throw new Error('Not authenticated. Please login first.');
|
|
11533
11755
|
}
|
|
11534
|
-
// Check cache unless force refresh
|
|
11535
11756
|
if (!forceRefresh) {
|
|
11536
11757
|
const cached = await tokenStorage.getAccountInfo();
|
|
11537
11758
|
if (cached && !cached.isStale) {
|
|
@@ -11539,18 +11760,15 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
|
|
|
11539
11760
|
return cached.data;
|
|
11540
11761
|
}
|
|
11541
11762
|
}
|
|
11542
|
-
// Fetch fresh data from API
|
|
11543
11763
|
console.log('[AuthContext] Fetching fresh account info from API');
|
|
11544
11764
|
const freshAccountInfo = await smartlinks.auth.getAccount();
|
|
11545
|
-
// Cache the fresh data
|
|
11546
11765
|
await tokenStorage.saveAccountInfo(freshAccountInfo, accountCacheTTL);
|
|
11547
11766
|
setAccountInfo(freshAccountInfo);
|
|
11548
|
-
notifyAuthStateChange('ACCOUNT_REFRESH', user, token, accountData, freshAccountInfo);
|
|
11767
|
+
notifyAuthStateChange('ACCOUNT_REFRESH', user, token, accountData, freshAccountInfo, isVerified, contact, contactId);
|
|
11549
11768
|
return freshAccountInfo;
|
|
11550
11769
|
}
|
|
11551
11770
|
catch (error) {
|
|
11552
11771
|
console.error('[AuthContext] Failed to get account info:', error);
|
|
11553
|
-
// Fallback to stale cache if API fails (standalone mode only)
|
|
11554
11772
|
if (!proxyMode) {
|
|
11555
11773
|
const cached = await tokenStorage.getAccountInfo();
|
|
11556
11774
|
if (cached) {
|
|
@@ -11560,12 +11778,10 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
|
|
|
11560
11778
|
}
|
|
11561
11779
|
throw error;
|
|
11562
11780
|
}
|
|
11563
|
-
}, [proxyMode, token, accountCacheTTL, user, accountData, notifyAuthStateChange]);
|
|
11564
|
-
// Convenience method for explicit refresh
|
|
11781
|
+
}, [proxyMode, token, accountCacheTTL, user, accountData, isVerified, contact, contactId, notifyAuthStateChange]);
|
|
11565
11782
|
const refreshAccount = useCallback(async () => {
|
|
11566
11783
|
return await getAccount(true);
|
|
11567
11784
|
}, [getAccount]);
|
|
11568
|
-
// Clear account cache (no-op in proxy mode)
|
|
11569
11785
|
const clearAccountCache = useCallback(async () => {
|
|
11570
11786
|
if (!proxyMode) {
|
|
11571
11787
|
await tokenStorage.clearAccountInfo();
|
|
@@ -11574,19 +11790,77 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
|
|
|
11574
11790
|
}, [proxyMode]);
|
|
11575
11791
|
const onAuthStateChange = useCallback((callback) => {
|
|
11576
11792
|
callbacksRef.current.add(callback);
|
|
11577
|
-
// Return unsubscribe function
|
|
11578
11793
|
return () => {
|
|
11579
11794
|
callbacksRef.current.delete(callback);
|
|
11580
11795
|
};
|
|
11581
11796
|
}, []);
|
|
11797
|
+
const retryVerification = useCallback(async () => {
|
|
11798
|
+
if (!token || !user) {
|
|
11799
|
+
console.log('[AuthContext] No session to verify');
|
|
11800
|
+
return false;
|
|
11801
|
+
}
|
|
11802
|
+
if (isVerified) {
|
|
11803
|
+
console.log('[AuthContext] Session already verified');
|
|
11804
|
+
return true;
|
|
11805
|
+
}
|
|
11806
|
+
try {
|
|
11807
|
+
console.log('[AuthContext] Retrying session verification...');
|
|
11808
|
+
await smartlinks.auth.verifyToken(token);
|
|
11809
|
+
setIsVerified(true);
|
|
11810
|
+
pendingVerificationRef.current = false;
|
|
11811
|
+
console.log('[AuthContext] Session verified on retry');
|
|
11812
|
+
notifyAuthStateChange('SESSION_VERIFIED', user, token, accountData, accountInfo, true, contact, contactId);
|
|
11813
|
+
return true;
|
|
11814
|
+
}
|
|
11815
|
+
catch (err) {
|
|
11816
|
+
if (isNetworkError(err)) {
|
|
11817
|
+
console.warn('[AuthContext] Network still unavailable, will retry later');
|
|
11818
|
+
return false;
|
|
11819
|
+
}
|
|
11820
|
+
else {
|
|
11821
|
+
console.warn('[AuthContext] Session invalid on retry, logging out');
|
|
11822
|
+
await logout();
|
|
11823
|
+
return false;
|
|
11824
|
+
}
|
|
11825
|
+
}
|
|
11826
|
+
}, [token, user, isVerified, accountData, accountInfo, contact, contactId, notifyAuthStateChange, isNetworkError, logout]);
|
|
11827
|
+
// Online/offline event listener for auto-retry verification
|
|
11828
|
+
useEffect(() => {
|
|
11829
|
+
if (proxyMode)
|
|
11830
|
+
return;
|
|
11831
|
+
const handleOnline = () => {
|
|
11832
|
+
console.log('[AuthContext] Network reconnected');
|
|
11833
|
+
setIsOnline(true);
|
|
11834
|
+
if (pendingVerificationRef.current && token && user) {
|
|
11835
|
+
console.log('[AuthContext] Retrying pending verification after reconnect...');
|
|
11836
|
+
retryVerification();
|
|
11837
|
+
}
|
|
11838
|
+
};
|
|
11839
|
+
const handleOffline = () => {
|
|
11840
|
+
console.log('[AuthContext] Network disconnected');
|
|
11841
|
+
setIsOnline(false);
|
|
11842
|
+
};
|
|
11843
|
+
window.addEventListener('online', handleOnline);
|
|
11844
|
+
window.addEventListener('offline', handleOffline);
|
|
11845
|
+
return () => {
|
|
11846
|
+
window.removeEventListener('online', handleOnline);
|
|
11847
|
+
window.removeEventListener('offline', handleOffline);
|
|
11848
|
+
};
|
|
11849
|
+
}, [proxyMode, token, user, retryVerification]);
|
|
11582
11850
|
const value = {
|
|
11583
11851
|
user,
|
|
11584
11852
|
token,
|
|
11585
11853
|
accountData,
|
|
11586
11854
|
accountInfo,
|
|
11587
11855
|
isAuthenticated: !!user,
|
|
11856
|
+
isVerified,
|
|
11588
11857
|
isLoading,
|
|
11858
|
+
isOnline,
|
|
11589
11859
|
proxyMode,
|
|
11860
|
+
contact,
|
|
11861
|
+
contactId,
|
|
11862
|
+
getContact,
|
|
11863
|
+
updateContactCustomFields,
|
|
11590
11864
|
login,
|
|
11591
11865
|
logout,
|
|
11592
11866
|
getToken,
|
|
@@ -11595,6 +11869,7 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
|
|
|
11595
11869
|
refreshAccount,
|
|
11596
11870
|
clearAccountCache,
|
|
11597
11871
|
onAuthStateChange,
|
|
11872
|
+
retryVerification,
|
|
11598
11873
|
};
|
|
11599
11874
|
return jsx(AuthContext.Provider, { value: value, children: children });
|
|
11600
11875
|
};
|
|
@@ -11873,7 +12148,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
11873
12148
|
const verificationMode = response.emailVerificationMode || config?.emailVerification?.mode || 'verify-then-auto-login';
|
|
11874
12149
|
if ((verificationMode === 'verify-then-auto-login' || verificationMode === 'immediate') && response.token) {
|
|
11875
12150
|
// Auto-login modes: Log the user in immediately if token is provided
|
|
11876
|
-
auth.login(response.token, response.user, response.accountData);
|
|
12151
|
+
auth.login(response.token, response.user, response.accountData, true);
|
|
11877
12152
|
setAuthSuccess(true);
|
|
11878
12153
|
setSuccessMessage('Email verified successfully! You are now logged in.');
|
|
11879
12154
|
onAuthSuccess(response.token, response.user, response.accountData);
|
|
@@ -11916,7 +12191,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
11916
12191
|
const response = await api.verifyMagicLink(token);
|
|
11917
12192
|
// Auto-login with magic link if token is provided
|
|
11918
12193
|
if (response.token) {
|
|
11919
|
-
auth.login(response.token, response.user, response.accountData);
|
|
12194
|
+
auth.login(response.token, response.user, response.accountData, true);
|
|
11920
12195
|
setAuthSuccess(true);
|
|
11921
12196
|
setSuccessMessage('Magic link verified! You are now logged in.');
|
|
11922
12197
|
onAuthSuccess(response.token, response.user, response.accountData);
|
|
@@ -11991,8 +12266,8 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
11991
12266
|
if (mode === 'register') {
|
|
11992
12267
|
// Handle different verification modes
|
|
11993
12268
|
if (verificationMode === 'immediate' && response.token) {
|
|
11994
|
-
// Immediate mode: Log in right away if token is provided
|
|
11995
|
-
auth.login(response.token, response.user, response.accountData);
|
|
12269
|
+
// Immediate mode: Log in right away if token is provided (isNewUser=true for registration)
|
|
12270
|
+
auth.login(response.token, response.user, response.accountData, true);
|
|
11996
12271
|
setAuthSuccess(true);
|
|
11997
12272
|
const deadline = response.emailVerificationDeadline
|
|
11998
12273
|
? new Date(response.emailVerificationDeadline).toLocaleString()
|
|
@@ -12028,7 +12303,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
12028
12303
|
if (response.requiresEmailVerification) {
|
|
12029
12304
|
throw new Error('Please verify your email before logging in. Check your inbox for the verification link.');
|
|
12030
12305
|
}
|
|
12031
|
-
auth.login(response.token, response.user, response.accountData);
|
|
12306
|
+
auth.login(response.token, response.user, response.accountData, false);
|
|
12032
12307
|
setAuthSuccess(true);
|
|
12033
12308
|
setSuccessMessage('Login successful!');
|
|
12034
12309
|
onAuthSuccess(response.token, response.user, response.accountData);
|
|
@@ -12188,7 +12463,8 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
12188
12463
|
tokenType: 'access_token',
|
|
12189
12464
|
});
|
|
12190
12465
|
if (authResponse.token) {
|
|
12191
|
-
|
|
12466
|
+
// Google OAuth can be login or signup - use isNewUser flag from backend if available
|
|
12467
|
+
auth.login(authResponse.token, authResponse.user, authResponse.accountData, authResponse.isNewUser);
|
|
12192
12468
|
setAuthSuccess(true);
|
|
12193
12469
|
setSuccessMessage('Google login successful!');
|
|
12194
12470
|
onAuthSuccess(authResponse.token, authResponse.user, authResponse.accountData);
|
|
@@ -12237,7 +12513,8 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
12237
12513
|
const idToken = response.credential;
|
|
12238
12514
|
const authResponse = await api.loginWithGoogle(idToken);
|
|
12239
12515
|
if (authResponse.token) {
|
|
12240
|
-
|
|
12516
|
+
// Google OAuth can be login or signup - use isNewUser flag from backend if available
|
|
12517
|
+
auth.login(authResponse.token, authResponse.user, authResponse.accountData, authResponse.isNewUser);
|
|
12241
12518
|
setAuthSuccess(true);
|
|
12242
12519
|
setSuccessMessage('Google login successful!');
|
|
12243
12520
|
onAuthSuccess(authResponse.token, authResponse.user, authResponse.accountData);
|
|
@@ -12298,7 +12575,8 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
12298
12575
|
const response = await api.verifyPhoneCode(phoneNumber, verificationCode);
|
|
12299
12576
|
// Update auth context with account data if token is provided
|
|
12300
12577
|
if (response.token) {
|
|
12301
|
-
auth
|
|
12578
|
+
// Phone auth can be login or signup - use isNewUser flag from backend if available
|
|
12579
|
+
auth.login(response.token, response.user, response.accountData, response.isNewUser);
|
|
12302
12580
|
onAuthSuccess(response.token, response.user, response.accountData);
|
|
12303
12581
|
if (redirectUrl) {
|
|
12304
12582
|
window.location.href = redirectUrl;
|