@proveanything/smartlinks-auth-ui 0.1.20 → 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 -69
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +345 -69
- 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,29 +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
|
-
|
|
11309
|
-
//
|
|
11310
|
-
|
|
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
|
+
}
|
|
11311
11496
|
}
|
|
11312
11497
|
}
|
|
11313
11498
|
catch (err) {
|
|
11314
|
-
|
|
11315
|
-
|
|
11316
|
-
|
|
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
|
+
}
|
|
11317
11517
|
}
|
|
11318
11518
|
}
|
|
11319
11519
|
// Load cached account info if available
|
|
@@ -11335,18 +11535,16 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
|
|
|
11335
11535
|
}
|
|
11336
11536
|
};
|
|
11337
11537
|
initializeAuth();
|
|
11338
|
-
// Cleanup for hot reload
|
|
11339
11538
|
return () => {
|
|
11340
11539
|
isMounted = false;
|
|
11341
11540
|
};
|
|
11342
|
-
}, [proxyMode, notifyAuthStateChange]);
|
|
11541
|
+
}, [proxyMode, notifyAuthStateChange, isNetworkError, syncContact, trackInteraction, interactionConfig?.sessionRestore]);
|
|
11343
11542
|
// Listen for parent auth state changes (proxy mode only)
|
|
11344
11543
|
useEffect(() => {
|
|
11345
11544
|
if (!proxyMode)
|
|
11346
11545
|
return;
|
|
11347
11546
|
console.log('[AuthContext] Proxy mode: setting up parent message listener');
|
|
11348
11547
|
const handleParentMessage = (event) => {
|
|
11349
|
-
// Handle auth state pushed from parent
|
|
11350
11548
|
if (event.data?.type === 'smartlinks:authkit:state') {
|
|
11351
11549
|
const { user: parentUser, accountData: parentAccountData, authenticated } = event.data.payload || {};
|
|
11352
11550
|
console.log('[AuthContext] Proxy mode: received state from parent:', { authenticated });
|
|
@@ -11360,15 +11558,20 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
|
|
|
11360
11558
|
setUser(userObj);
|
|
11361
11559
|
setAccountData(parentAccountData || null);
|
|
11362
11560
|
setAccountInfo(parentAccountData || null);
|
|
11363
|
-
|
|
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);
|
|
11364
11565
|
}
|
|
11365
11566
|
else {
|
|
11366
|
-
// Parent indicates no session / logged out
|
|
11367
11567
|
setUser(null);
|
|
11368
11568
|
setToken(null);
|
|
11369
11569
|
setAccountData(null);
|
|
11370
11570
|
setAccountInfo(null);
|
|
11371
|
-
|
|
11571
|
+
setContact(null);
|
|
11572
|
+
setContactId(null);
|
|
11573
|
+
setIsVerified(false);
|
|
11574
|
+
notifyAuthStateChange('LOGOUT', null, null, null, null, false);
|
|
11372
11575
|
}
|
|
11373
11576
|
}
|
|
11374
11577
|
};
|
|
@@ -11377,54 +11580,60 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
|
|
|
11377
11580
|
console.log('[AuthContext] Proxy mode: cleaning up parent message listener');
|
|
11378
11581
|
window.removeEventListener('message', handleParentMessage);
|
|
11379
11582
|
};
|
|
11380
|
-
}, [proxyMode, notifyAuthStateChange]);
|
|
11583
|
+
}, [proxyMode, notifyAuthStateChange, syncContact]);
|
|
11381
11584
|
// Cross-tab synchronization - standalone mode only
|
|
11382
11585
|
useEffect(() => {
|
|
11383
11586
|
if (proxyMode)
|
|
11384
|
-
return;
|
|
11587
|
+
return;
|
|
11385
11588
|
console.log('[AuthContext] Setting up cross-tab synchronization');
|
|
11386
11589
|
const unsubscribe = onStorageChange(async (event) => {
|
|
11387
11590
|
console.log('[AuthContext] Cross-tab storage event:', event.type, event.key);
|
|
11388
11591
|
try {
|
|
11389
11592
|
if (event.type === 'clear') {
|
|
11390
|
-
// Another tab cleared all storage (logout)
|
|
11391
11593
|
console.log('[AuthContext] Detected logout in another tab');
|
|
11392
11594
|
setToken(null);
|
|
11393
11595
|
setUser(null);
|
|
11394
11596
|
setAccountData(null);
|
|
11395
11597
|
setAccountInfo(null);
|
|
11598
|
+
setContact(null);
|
|
11599
|
+
setContactId(null);
|
|
11600
|
+
setIsVerified(false);
|
|
11396
11601
|
smartlinks.auth.logout();
|
|
11397
|
-
notifyAuthStateChange('CROSS_TAB_SYNC', null, null, null);
|
|
11602
|
+
notifyAuthStateChange('CROSS_TAB_SYNC', null, null, null, null, false);
|
|
11398
11603
|
}
|
|
11399
11604
|
else if (event.type === 'remove' && (event.key === 'token' || event.key === 'user')) {
|
|
11400
|
-
// Another tab removed token or user (logout)
|
|
11401
11605
|
console.log('[AuthContext] Detected token/user removal in another tab');
|
|
11402
11606
|
setToken(null);
|
|
11403
11607
|
setUser(null);
|
|
11404
11608
|
setAccountData(null);
|
|
11405
11609
|
setAccountInfo(null);
|
|
11610
|
+
setContact(null);
|
|
11611
|
+
setContactId(null);
|
|
11612
|
+
setIsVerified(false);
|
|
11406
11613
|
smartlinks.auth.logout();
|
|
11407
|
-
notifyAuthStateChange('CROSS_TAB_SYNC', null, null, null);
|
|
11614
|
+
notifyAuthStateChange('CROSS_TAB_SYNC', null, null, null, null, false);
|
|
11408
11615
|
}
|
|
11409
11616
|
else if (event.type === 'set' && event.key === 'token') {
|
|
11410
|
-
// Another tab set a new token (login)
|
|
11411
11617
|
console.log('[AuthContext] Detected login in another tab');
|
|
11412
11618
|
const storedToken = await tokenStorage.getToken();
|
|
11413
11619
|
const storedUser = await tokenStorage.getUser();
|
|
11414
11620
|
const storedAccountData = await tokenStorage.getAccountData();
|
|
11621
|
+
const storedContactId = await tokenStorage.getContactId();
|
|
11415
11622
|
if (storedToken && storedUser) {
|
|
11416
11623
|
setToken(storedToken.token);
|
|
11417
11624
|
setUser(storedUser);
|
|
11418
11625
|
setAccountData(storedAccountData);
|
|
11419
|
-
|
|
11626
|
+
if (storedContactId) {
|
|
11627
|
+
setContactId(storedContactId);
|
|
11628
|
+
}
|
|
11629
|
+
setIsVerified(true);
|
|
11420
11630
|
smartlinks.auth.verifyToken(storedToken.token).catch(err => {
|
|
11421
11631
|
console.warn('[AuthContext] Failed to restore bearer token from cross-tab sync:', err);
|
|
11422
11632
|
});
|
|
11423
|
-
notifyAuthStateChange('CROSS_TAB_SYNC', storedUser, storedToken.token, storedAccountData);
|
|
11633
|
+
notifyAuthStateChange('CROSS_TAB_SYNC', storedUser, storedToken.token, storedAccountData, null, true, null, storedContactId);
|
|
11424
11634
|
}
|
|
11425
11635
|
}
|
|
11426
11636
|
else if (event.type === 'set' && event.key === 'account_info') {
|
|
11427
|
-
// Another tab fetched fresh account info
|
|
11428
11637
|
const cached = await tokenStorage.getAccountInfo();
|
|
11429
11638
|
if (cached && !cached.isStale) {
|
|
11430
11639
|
setAccountInfo(cached.data);
|
|
@@ -11441,7 +11650,7 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
|
|
|
11441
11650
|
unsubscribe();
|
|
11442
11651
|
};
|
|
11443
11652
|
}, [proxyMode, notifyAuthStateChange]);
|
|
11444
|
-
const login = useCallback(async (authToken, authUser, authAccountData) => {
|
|
11653
|
+
const login = useCallback(async (authToken, authUser, authAccountData, isNewUser) => {
|
|
11445
11654
|
try {
|
|
11446
11655
|
// Only persist to storage in standalone mode
|
|
11447
11656
|
if (!proxyMode) {
|
|
@@ -11450,7 +11659,6 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
|
|
|
11450
11659
|
if (authAccountData) {
|
|
11451
11660
|
await tokenStorage.saveAccountData(authAccountData);
|
|
11452
11661
|
}
|
|
11453
|
-
// Set bearer token in global Smartlinks SDK via auth.verifyToken
|
|
11454
11662
|
smartlinks.auth.verifyToken(authToken).catch(err => {
|
|
11455
11663
|
console.warn('Failed to set bearer token on login:', err);
|
|
11456
11664
|
});
|
|
@@ -11459,8 +11667,9 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
|
|
|
11459
11667
|
setToken(authToken);
|
|
11460
11668
|
setUser(authUser);
|
|
11461
11669
|
setAccountData(authAccountData || null);
|
|
11670
|
+
setIsVerified(true);
|
|
11671
|
+
pendingVerificationRef.current = false;
|
|
11462
11672
|
// Cross-iframe auth state synchronization
|
|
11463
|
-
// Always notify parent frame of login (both modes, but especially important in proxy mode)
|
|
11464
11673
|
if (iframe.isIframe()) {
|
|
11465
11674
|
console.log('[AuthContext] Notifying parent of login via postMessage');
|
|
11466
11675
|
iframe.sendParentCustom('smartlinks:authkit:login', {
|
|
@@ -11469,7 +11678,13 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
|
|
|
11469
11678
|
accountData: authAccountData || null
|
|
11470
11679
|
});
|
|
11471
11680
|
}
|
|
11472
|
-
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
|
+
});
|
|
11473
11688
|
// Optionally preload account info on login (standalone mode only)
|
|
11474
11689
|
if (!proxyMode && preloadAccountInfo) {
|
|
11475
11690
|
getAccount(true).catch(error => {
|
|
@@ -11481,8 +11696,10 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
|
|
|
11481
11696
|
console.error('Failed to save auth data to storage:', error);
|
|
11482
11697
|
throw error;
|
|
11483
11698
|
}
|
|
11484
|
-
}, [proxyMode, notifyAuthStateChange, preloadAccountInfo]);
|
|
11699
|
+
}, [proxyMode, notifyAuthStateChange, preloadAccountInfo, syncContact, trackInteraction]);
|
|
11485
11700
|
const logout = useCallback(async () => {
|
|
11701
|
+
const currentUser = user;
|
|
11702
|
+
const currentContactId = contactId;
|
|
11486
11703
|
try {
|
|
11487
11704
|
// Only clear persistent storage in standalone mode
|
|
11488
11705
|
if (!proxyMode) {
|
|
@@ -11494,21 +11711,27 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
|
|
|
11494
11711
|
setUser(null);
|
|
11495
11712
|
setAccountData(null);
|
|
11496
11713
|
setAccountInfo(null);
|
|
11714
|
+
setContact(null);
|
|
11715
|
+
setContactId(null);
|
|
11716
|
+
setIsVerified(false);
|
|
11717
|
+
pendingVerificationRef.current = false;
|
|
11497
11718
|
// Cross-iframe auth state synchronization
|
|
11498
|
-
// Always notify parent frame of logout
|
|
11499
11719
|
if (iframe.isIframe()) {
|
|
11500
11720
|
console.log('[AuthContext] Notifying parent of logout via postMessage');
|
|
11501
11721
|
iframe.sendParentCustom('smartlinks:authkit:logout', {});
|
|
11502
11722
|
}
|
|
11503
|
-
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
|
+
}
|
|
11504
11728
|
}
|
|
11505
11729
|
catch (error) {
|
|
11506
11730
|
console.error('Failed to clear auth data from storage:', error);
|
|
11507
11731
|
}
|
|
11508
|
-
}, [proxyMode, notifyAuthStateChange]);
|
|
11732
|
+
}, [proxyMode, notifyAuthStateChange, user, contactId, collectionId, shouldTrackInteractions, trackInteraction]);
|
|
11509
11733
|
const getToken = useCallback(async () => {
|
|
11510
11734
|
if (proxyMode) {
|
|
11511
|
-
// In proxy mode, token is managed by parent - return memory state
|
|
11512
11735
|
return token;
|
|
11513
11736
|
}
|
|
11514
11737
|
const storedToken = await tokenStorage.getToken();
|
|
@@ -11517,23 +11740,19 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
|
|
|
11517
11740
|
const refreshToken = useCallback(async () => {
|
|
11518
11741
|
throw new Error('Token refresh must be implemented via your backend API');
|
|
11519
11742
|
}, []);
|
|
11520
|
-
// Get account with intelligent caching (or direct parent fetch in proxy mode)
|
|
11521
11743
|
const getAccount = useCallback(async (forceRefresh = false) => {
|
|
11522
11744
|
try {
|
|
11523
11745
|
if (proxyMode) {
|
|
11524
|
-
// PROXY MODE: Always fetch from parent via proxied API, no local cache
|
|
11525
11746
|
console.log('[AuthContext] Proxy mode: fetching account from parent');
|
|
11526
11747
|
const freshAccountInfo = await smartlinks.auth.getAccount();
|
|
11527
11748
|
setAccountInfo(freshAccountInfo);
|
|
11528
11749
|
setAccountData(freshAccountInfo);
|
|
11529
|
-
notifyAuthStateChange('ACCOUNT_REFRESH', user, token, freshAccountInfo, freshAccountInfo);
|
|
11750
|
+
notifyAuthStateChange('ACCOUNT_REFRESH', user, token, freshAccountInfo, freshAccountInfo, isVerified, contact, contactId);
|
|
11530
11751
|
return freshAccountInfo;
|
|
11531
11752
|
}
|
|
11532
|
-
// STANDALONE MODE: Use caching
|
|
11533
11753
|
if (!token) {
|
|
11534
11754
|
throw new Error('Not authenticated. Please login first.');
|
|
11535
11755
|
}
|
|
11536
|
-
// Check cache unless force refresh
|
|
11537
11756
|
if (!forceRefresh) {
|
|
11538
11757
|
const cached = await tokenStorage.getAccountInfo();
|
|
11539
11758
|
if (cached && !cached.isStale) {
|
|
@@ -11541,18 +11760,15 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
|
|
|
11541
11760
|
return cached.data;
|
|
11542
11761
|
}
|
|
11543
11762
|
}
|
|
11544
|
-
// Fetch fresh data from API
|
|
11545
11763
|
console.log('[AuthContext] Fetching fresh account info from API');
|
|
11546
11764
|
const freshAccountInfo = await smartlinks.auth.getAccount();
|
|
11547
|
-
// Cache the fresh data
|
|
11548
11765
|
await tokenStorage.saveAccountInfo(freshAccountInfo, accountCacheTTL);
|
|
11549
11766
|
setAccountInfo(freshAccountInfo);
|
|
11550
|
-
notifyAuthStateChange('ACCOUNT_REFRESH', user, token, accountData, freshAccountInfo);
|
|
11767
|
+
notifyAuthStateChange('ACCOUNT_REFRESH', user, token, accountData, freshAccountInfo, isVerified, contact, contactId);
|
|
11551
11768
|
return freshAccountInfo;
|
|
11552
11769
|
}
|
|
11553
11770
|
catch (error) {
|
|
11554
11771
|
console.error('[AuthContext] Failed to get account info:', error);
|
|
11555
|
-
// Fallback to stale cache if API fails (standalone mode only)
|
|
11556
11772
|
if (!proxyMode) {
|
|
11557
11773
|
const cached = await tokenStorage.getAccountInfo();
|
|
11558
11774
|
if (cached) {
|
|
@@ -11562,12 +11778,10 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
|
|
|
11562
11778
|
}
|
|
11563
11779
|
throw error;
|
|
11564
11780
|
}
|
|
11565
|
-
}, [proxyMode, token, accountCacheTTL, user, accountData, notifyAuthStateChange]);
|
|
11566
|
-
// Convenience method for explicit refresh
|
|
11781
|
+
}, [proxyMode, token, accountCacheTTL, user, accountData, isVerified, contact, contactId, notifyAuthStateChange]);
|
|
11567
11782
|
const refreshAccount = useCallback(async () => {
|
|
11568
11783
|
return await getAccount(true);
|
|
11569
11784
|
}, [getAccount]);
|
|
11570
|
-
// Clear account cache (no-op in proxy mode)
|
|
11571
11785
|
const clearAccountCache = useCallback(async () => {
|
|
11572
11786
|
if (!proxyMode) {
|
|
11573
11787
|
await tokenStorage.clearAccountInfo();
|
|
@@ -11576,19 +11790,77 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
|
|
|
11576
11790
|
}, [proxyMode]);
|
|
11577
11791
|
const onAuthStateChange = useCallback((callback) => {
|
|
11578
11792
|
callbacksRef.current.add(callback);
|
|
11579
|
-
// Return unsubscribe function
|
|
11580
11793
|
return () => {
|
|
11581
11794
|
callbacksRef.current.delete(callback);
|
|
11582
11795
|
};
|
|
11583
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]);
|
|
11584
11850
|
const value = {
|
|
11585
11851
|
user,
|
|
11586
11852
|
token,
|
|
11587
11853
|
accountData,
|
|
11588
11854
|
accountInfo,
|
|
11589
11855
|
isAuthenticated: !!user,
|
|
11856
|
+
isVerified,
|
|
11590
11857
|
isLoading,
|
|
11858
|
+
isOnline,
|
|
11591
11859
|
proxyMode,
|
|
11860
|
+
contact,
|
|
11861
|
+
contactId,
|
|
11862
|
+
getContact,
|
|
11863
|
+
updateContactCustomFields,
|
|
11592
11864
|
login,
|
|
11593
11865
|
logout,
|
|
11594
11866
|
getToken,
|
|
@@ -11597,6 +11869,7 @@ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 *
|
|
|
11597
11869
|
refreshAccount,
|
|
11598
11870
|
clearAccountCache,
|
|
11599
11871
|
onAuthStateChange,
|
|
11872
|
+
retryVerification,
|
|
11600
11873
|
};
|
|
11601
11874
|
return jsx(AuthContext.Provider, { value: value, children: children });
|
|
11602
11875
|
};
|
|
@@ -11875,7 +12148,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
11875
12148
|
const verificationMode = response.emailVerificationMode || config?.emailVerification?.mode || 'verify-then-auto-login';
|
|
11876
12149
|
if ((verificationMode === 'verify-then-auto-login' || verificationMode === 'immediate') && response.token) {
|
|
11877
12150
|
// Auto-login modes: Log the user in immediately if token is provided
|
|
11878
|
-
auth.login(response.token, response.user, response.accountData);
|
|
12151
|
+
auth.login(response.token, response.user, response.accountData, true);
|
|
11879
12152
|
setAuthSuccess(true);
|
|
11880
12153
|
setSuccessMessage('Email verified successfully! You are now logged in.');
|
|
11881
12154
|
onAuthSuccess(response.token, response.user, response.accountData);
|
|
@@ -11918,7 +12191,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
11918
12191
|
const response = await api.verifyMagicLink(token);
|
|
11919
12192
|
// Auto-login with magic link if token is provided
|
|
11920
12193
|
if (response.token) {
|
|
11921
|
-
auth.login(response.token, response.user, response.accountData);
|
|
12194
|
+
auth.login(response.token, response.user, response.accountData, true);
|
|
11922
12195
|
setAuthSuccess(true);
|
|
11923
12196
|
setSuccessMessage('Magic link verified! You are now logged in.');
|
|
11924
12197
|
onAuthSuccess(response.token, response.user, response.accountData);
|
|
@@ -11993,8 +12266,8 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
11993
12266
|
if (mode === 'register') {
|
|
11994
12267
|
// Handle different verification modes
|
|
11995
12268
|
if (verificationMode === 'immediate' && response.token) {
|
|
11996
|
-
// Immediate mode: Log in right away if token is provided
|
|
11997
|
-
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);
|
|
11998
12271
|
setAuthSuccess(true);
|
|
11999
12272
|
const deadline = response.emailVerificationDeadline
|
|
12000
12273
|
? new Date(response.emailVerificationDeadline).toLocaleString()
|
|
@@ -12030,7 +12303,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
12030
12303
|
if (response.requiresEmailVerification) {
|
|
12031
12304
|
throw new Error('Please verify your email before logging in. Check your inbox for the verification link.');
|
|
12032
12305
|
}
|
|
12033
|
-
auth.login(response.token, response.user, response.accountData);
|
|
12306
|
+
auth.login(response.token, response.user, response.accountData, false);
|
|
12034
12307
|
setAuthSuccess(true);
|
|
12035
12308
|
setSuccessMessage('Login successful!');
|
|
12036
12309
|
onAuthSuccess(response.token, response.user, response.accountData);
|
|
@@ -12190,7 +12463,8 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
12190
12463
|
tokenType: 'access_token',
|
|
12191
12464
|
});
|
|
12192
12465
|
if (authResponse.token) {
|
|
12193
|
-
|
|
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);
|
|
12194
12468
|
setAuthSuccess(true);
|
|
12195
12469
|
setSuccessMessage('Google login successful!');
|
|
12196
12470
|
onAuthSuccess(authResponse.token, authResponse.user, authResponse.accountData);
|
|
@@ -12239,7 +12513,8 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
12239
12513
|
const idToken = response.credential;
|
|
12240
12514
|
const authResponse = await api.loginWithGoogle(idToken);
|
|
12241
12515
|
if (authResponse.token) {
|
|
12242
|
-
|
|
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);
|
|
12243
12518
|
setAuthSuccess(true);
|
|
12244
12519
|
setSuccessMessage('Google login successful!');
|
|
12245
12520
|
onAuthSuccess(authResponse.token, authResponse.user, authResponse.accountData);
|
|
@@ -12300,7 +12575,8 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
12300
12575
|
const response = await api.verifyPhoneCode(phoneNumber, verificationCode);
|
|
12301
12576
|
// Update auth context with account data if token is provided
|
|
12302
12577
|
if (response.token) {
|
|
12303
|
-
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);
|
|
12304
12580
|
onAuthSuccess(response.token, response.user, response.accountData);
|
|
12305
12581
|
if (redirectUrl) {
|
|
12306
12582
|
window.location.href = redirectUrl;
|