@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/api.d.ts.map +1 -1
- 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 +384 -70
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +384 -70
- 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.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
|
-
//
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
11326
|
-
|
|
11327
|
-
|
|
11328
|
-
|
|
11329
|
-
//
|
|
11330
|
-
|
|
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
|
-
|
|
11335
|
-
|
|
11336
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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;
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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;
|