@proveanything/smartlinks-auth-ui 0.3.8 → 0.3.10
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.map +1 -1
- package/dist/index.esm.js +58 -409
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +58 -409
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -10913,18 +10913,14 @@ class AuthAPI {
|
|
|
10913
10913
|
});
|
|
10914
10914
|
}
|
|
10915
10915
|
async fetchConfig() {
|
|
10916
|
-
console.log('[AuthAPI] 📋 fetchConfig called with clientId:', this.clientId);
|
|
10917
10916
|
this.log.log('fetchConfig called with clientId:', this.clientId);
|
|
10918
10917
|
try {
|
|
10919
|
-
console.log('[AuthAPI] 🌐 Calling smartlinks.authKit.load() - this uses current SDK config (including proxyMode)');
|
|
10920
10918
|
this.log.log('Calling smartlinks.authKit.load...');
|
|
10921
10919
|
const result = await smartlinks__namespace.authKit.load(this.clientId);
|
|
10922
|
-
console.log('[AuthAPI] ✅ smartlinks.authKit.load returned:', result);
|
|
10923
10920
|
this.log.log('smartlinks.authKit.load returned:', result);
|
|
10924
10921
|
return result;
|
|
10925
10922
|
}
|
|
10926
10923
|
catch (error) {
|
|
10927
|
-
console.log('[AuthAPI] ❌ Failed to fetch UI config:', error);
|
|
10928
10924
|
this.log.warn('Failed to fetch UI config, using defaults:', error);
|
|
10929
10925
|
return {
|
|
10930
10926
|
branding: {
|
|
@@ -11523,12 +11519,9 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
11523
11519
|
}, []);
|
|
11524
11520
|
// Sync contact to Smartlinks (non-blocking)
|
|
11525
11521
|
const syncContact = React.useCallback(async (authUser, customFields) => {
|
|
11526
|
-
if (!collectionId || !shouldSyncContacts)
|
|
11527
|
-
console.log('[AuthContext] Contact sync skipped: no collectionId or disabled');
|
|
11522
|
+
if (!collectionId || !shouldSyncContacts)
|
|
11528
11523
|
return null;
|
|
11529
|
-
}
|
|
11530
11524
|
try {
|
|
11531
|
-
console.log('[AuthContext] Syncing contact for user:', authUser.uid);
|
|
11532
11525
|
const result = await smartlinks__namespace.contact.publicUpsert(collectionId, {
|
|
11533
11526
|
userId: authUser.uid,
|
|
11534
11527
|
email: authUser.email,
|
|
@@ -11537,13 +11530,10 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
11537
11530
|
customFields: customFields || {},
|
|
11538
11531
|
source: 'authkit',
|
|
11539
11532
|
});
|
|
11540
|
-
console.log('[AuthContext] Contact synced:', result.contactId);
|
|
11541
|
-
// Store contact ID locally
|
|
11542
11533
|
if (!proxyMode) {
|
|
11543
11534
|
await tokenStorage.saveContactId(result.contactId);
|
|
11544
11535
|
}
|
|
11545
11536
|
setContactId(result.contactId);
|
|
11546
|
-
// Fetch full contact to get customFields using public endpoint
|
|
11547
11537
|
try {
|
|
11548
11538
|
const myContactResponse = await smartlinks__namespace.contact.publicGetMine(collectionId);
|
|
11549
11539
|
if (myContactResponse?.contact) {
|
|
@@ -11552,12 +11542,11 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
11552
11542
|
}
|
|
11553
11543
|
}
|
|
11554
11544
|
catch (lookupErr) {
|
|
11555
|
-
|
|
11545
|
+
// Non-fatal
|
|
11556
11546
|
}
|
|
11557
11547
|
return result.contactId;
|
|
11558
11548
|
}
|
|
11559
11549
|
catch (err) {
|
|
11560
|
-
console.warn('[AuthContext] Contact sync failed (non-blocking):', err);
|
|
11561
11550
|
return null;
|
|
11562
11551
|
}
|
|
11563
11552
|
}, [collectionId, shouldSyncContacts, proxyMode, token, accountData, accountInfo, isVerified, notifyAuthStateChange]);
|
|
@@ -11565,11 +11554,8 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
11565
11554
|
// IMPORTANT: Only tracks if an explicit interaction ID is configured for the event type.
|
|
11566
11555
|
// Interaction IDs must be pre-created in SmartLinks - you cannot use arbitrary IDs.
|
|
11567
11556
|
const trackInteraction = React.useCallback(async (eventType, userId, currentContactId, metadata) => {
|
|
11568
|
-
if (!collectionId || !shouldTrackInteractions)
|
|
11569
|
-
console.log('[AuthContext] Interaction tracking skipped: no collectionId or disabled');
|
|
11557
|
+
if (!collectionId || !shouldTrackInteractions)
|
|
11570
11558
|
return;
|
|
11571
|
-
}
|
|
11572
|
-
// Only track if explicit interaction ID is configured - no defaults
|
|
11573
11559
|
const interactionIdMap = {
|
|
11574
11560
|
login: interactionConfig?.login,
|
|
11575
11561
|
logout: interactionConfig?.logout,
|
|
@@ -11577,12 +11563,9 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
11577
11563
|
session_restore: interactionConfig?.sessionRestore,
|
|
11578
11564
|
};
|
|
11579
11565
|
const interactionId = interactionIdMap[eventType];
|
|
11580
|
-
if (!interactionId)
|
|
11581
|
-
console.log(`[AuthContext] No interaction ID configured for '${eventType}', skipping (this is normal if not configured)`);
|
|
11566
|
+
if (!interactionId)
|
|
11582
11567
|
return;
|
|
11583
|
-
}
|
|
11584
11568
|
try {
|
|
11585
|
-
console.log(`[AuthContext] Tracking interaction: ${interactionId}`);
|
|
11586
11569
|
await smartlinks__namespace.interactions.submitPublicEvent(collectionId, {
|
|
11587
11570
|
collectionId,
|
|
11588
11571
|
interactionId,
|
|
@@ -11597,11 +11580,10 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
11597
11580
|
source: 'authkit',
|
|
11598
11581
|
},
|
|
11599
11582
|
});
|
|
11600
|
-
console.log(`[AuthContext] Tracked interaction: ${interactionId}`);
|
|
11601
11583
|
notifyAuthStateChange('INTERACTION_TRACKED', user, token, accountData, accountInfo, isVerified, contact, contactId);
|
|
11602
11584
|
}
|
|
11603
11585
|
catch (err) {
|
|
11604
|
-
|
|
11586
|
+
// Non-blocking
|
|
11605
11587
|
}
|
|
11606
11588
|
}, [collectionId, shouldTrackInteractions, interactionAppId, interactionConfig, user, token, accountData, accountInfo, isVerified, contact, contactId, notifyAuthStateChange]);
|
|
11607
11589
|
// Keep refs in sync with latest callbacks (avoids stale closures)
|
|
@@ -11613,20 +11595,13 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
11613
11595
|
}, [trackInteraction]);
|
|
11614
11596
|
// Get contact (with optional refresh)
|
|
11615
11597
|
const getContact = React.useCallback(async (forceRefresh = false) => {
|
|
11616
|
-
if (!collectionId)
|
|
11617
|
-
console.log('[AuthContext] getContact: no collectionId');
|
|
11598
|
+
if (!collectionId)
|
|
11618
11599
|
return null;
|
|
11619
|
-
|
|
11620
|
-
// Need either email or userId to lookup contact
|
|
11621
|
-
if (!user?.email && !user?.uid) {
|
|
11622
|
-
console.log('[AuthContext] getContact: no user email or uid');
|
|
11600
|
+
if (!user?.email && !user?.uid)
|
|
11623
11601
|
return null;
|
|
11624
|
-
|
|
11625
|
-
if (contact && !forceRefresh) {
|
|
11602
|
+
if (contact && !forceRefresh)
|
|
11626
11603
|
return contact;
|
|
11627
|
-
}
|
|
11628
11604
|
try {
|
|
11629
|
-
console.log('[AuthContext] Fetching contact via publicGetMine');
|
|
11630
11605
|
const result = await smartlinks__namespace.contact.publicGetMine(collectionId);
|
|
11631
11606
|
if (result?.contact) {
|
|
11632
11607
|
const contactData = result.contact;
|
|
@@ -11637,7 +11612,6 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
11637
11612
|
return null;
|
|
11638
11613
|
}
|
|
11639
11614
|
catch (err) {
|
|
11640
|
-
console.warn('[AuthContext] Failed to get contact:', err);
|
|
11641
11615
|
return null;
|
|
11642
11616
|
}
|
|
11643
11617
|
}, [collectionId, user, contact]);
|
|
@@ -11646,24 +11620,19 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
11646
11620
|
if (!collectionId || !contactId) {
|
|
11647
11621
|
throw new Error('No contact to update. Ensure collectionId is provided and user is synced.');
|
|
11648
11622
|
}
|
|
11649
|
-
console.log('[AuthContext] Updating contact custom fields:', contactId);
|
|
11650
11623
|
const updated = await smartlinks__namespace.contact.update(collectionId, contactId, { customFields });
|
|
11651
11624
|
setContact(updated);
|
|
11652
11625
|
return updated;
|
|
11653
11626
|
}, [collectionId, contactId]);
|
|
11654
11627
|
// Initialize auth state
|
|
11655
11628
|
React.useEffect(() => {
|
|
11656
|
-
if (initializingRef.current)
|
|
11657
|
-
console.log('[AuthContext] Skipping initialization - already in progress');
|
|
11629
|
+
if (initializingRef.current)
|
|
11658
11630
|
return;
|
|
11659
|
-
}
|
|
11660
11631
|
let isMounted = true;
|
|
11661
11632
|
initializingRef.current = true;
|
|
11662
11633
|
const initializeAuth = async () => {
|
|
11663
11634
|
try {
|
|
11664
11635
|
if (proxyMode) {
|
|
11665
|
-
// PROXY MODE: Check for existing session via parent's auth.getAccount()
|
|
11666
|
-
console.log('[AuthContext] Proxy mode: checking for existing session via auth.getAccount()');
|
|
11667
11636
|
try {
|
|
11668
11637
|
const accountResponse = await smartlinks__namespace.auth.getAccount();
|
|
11669
11638
|
const accountAny = accountResponse;
|
|
@@ -11679,17 +11648,16 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
11679
11648
|
setAccountData(accountResponse);
|
|
11680
11649
|
setAccountInfo(accountResponse);
|
|
11681
11650
|
setIsVerified(true);
|
|
11682
|
-
console.log('[AuthContext] Proxy mode: initialized from parent account, uid:', accountAny.uid);
|
|
11683
11651
|
notifyAuthStateChange('LOGIN', userFromAccount, null, accountResponse, accountResponse, true);
|
|
11684
11652
|
// Sync contact in background (proxy mode) - use ref for stable dependency
|
|
11685
11653
|
syncContactRef.current?.(userFromAccount, accountResponse);
|
|
11686
11654
|
}
|
|
11687
11655
|
else if (isMounted) {
|
|
11688
|
-
|
|
11656
|
+
// No valid session, awaiting login
|
|
11689
11657
|
}
|
|
11690
11658
|
}
|
|
11691
11659
|
catch (error) {
|
|
11692
|
-
|
|
11660
|
+
// auth.getAccount() failed, awaiting login
|
|
11693
11661
|
}
|
|
11694
11662
|
if (isMounted) {
|
|
11695
11663
|
setIsLoading(false);
|
|
@@ -11713,17 +11681,13 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
11713
11681
|
// Set loading to false IMMEDIATELY after optimistic restore
|
|
11714
11682
|
// Don't wait for background verification
|
|
11715
11683
|
setIsLoading(false);
|
|
11716
|
-
console.log('[AuthContext] Session restored optimistically (pending verification)');
|
|
11717
11684
|
notifyAuthStateChange('SESSION_RESTORED_OFFLINE', storedUser, storedToken.token, storedAccountData || null, null, false, null, storedContactId);
|
|
11718
11685
|
}
|
|
11719
|
-
// BACKGROUND: Verify token (non-blocking)
|
|
11720
11686
|
try {
|
|
11721
|
-
console.log('[AuthContext] Verifying stored token in background...');
|
|
11722
11687
|
await smartlinks__namespace.auth.verifyToken(storedToken.token);
|
|
11723
11688
|
if (isMounted) {
|
|
11724
11689
|
setIsVerified(true);
|
|
11725
11690
|
pendingVerificationRef.current = false;
|
|
11726
|
-
console.log('[AuthContext] Session verified successfully');
|
|
11727
11691
|
notifyAuthStateChange('SESSION_VERIFIED', storedUser, storedToken.token, storedAccountData || null, null, true, null, storedContactId);
|
|
11728
11692
|
// Track session restore interaction (optional) - use ref for stable dependency
|
|
11729
11693
|
if (interactionConfig?.sessionRestore) {
|
|
@@ -11733,11 +11697,9 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
11733
11697
|
}
|
|
11734
11698
|
catch (err) {
|
|
11735
11699
|
if (isNetworkError(err)) {
|
|
11736
|
-
console.warn('[AuthContext] Network error during verification, will retry on reconnect:', err);
|
|
11737
11700
|
pendingVerificationRef.current = true;
|
|
11738
11701
|
}
|
|
11739
11702
|
else {
|
|
11740
|
-
console.warn('[AuthContext] Token verification failed (auth error), clearing credentials:', err);
|
|
11741
11703
|
if (isMounted) {
|
|
11742
11704
|
setToken(null);
|
|
11743
11705
|
setUser(null);
|
|
@@ -11779,11 +11741,9 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
11779
11741
|
React.useEffect(() => {
|
|
11780
11742
|
if (!proxyMode)
|
|
11781
11743
|
return;
|
|
11782
|
-
console.log('[AuthContext] Proxy mode: setting up parent message listener');
|
|
11783
11744
|
const handleParentMessage = (event) => {
|
|
11784
11745
|
if (event.data?.type === 'smartlinks:authkit:state') {
|
|
11785
11746
|
const { user: parentUser, accountData: parentAccountData, authenticated } = event.data.payload || {};
|
|
11786
|
-
console.log('[AuthContext] Proxy mode: received state from parent:', { authenticated });
|
|
11787
11747
|
if (authenticated && parentUser) {
|
|
11788
11748
|
const userObj = {
|
|
11789
11749
|
uid: parentUser.uid || parentUser.id,
|
|
@@ -11796,7 +11756,6 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
11796
11756
|
setAccountInfo(parentAccountData || null);
|
|
11797
11757
|
setIsVerified(true);
|
|
11798
11758
|
notifyAuthStateChange('CROSS_TAB_SYNC', userObj, null, parentAccountData || null, parentAccountData || null, true);
|
|
11799
|
-
// Sync contact on cross-tab state - use ref for stable dependency
|
|
11800
11759
|
syncContactRef.current?.(userObj, parentAccountData);
|
|
11801
11760
|
}
|
|
11802
11761
|
else {
|
|
@@ -11813,7 +11772,6 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
11813
11772
|
};
|
|
11814
11773
|
window.addEventListener('message', handleParentMessage);
|
|
11815
11774
|
return () => {
|
|
11816
|
-
console.log('[AuthContext] Proxy mode: cleaning up parent message listener');
|
|
11817
11775
|
window.removeEventListener('message', handleParentMessage);
|
|
11818
11776
|
};
|
|
11819
11777
|
}, [proxyMode, notifyAuthStateChange]);
|
|
@@ -11821,12 +11779,9 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
11821
11779
|
React.useEffect(() => {
|
|
11822
11780
|
if (proxyMode)
|
|
11823
11781
|
return;
|
|
11824
|
-
console.log('[AuthContext] Setting up cross-tab synchronization');
|
|
11825
11782
|
const unsubscribe = onStorageChange(async (event) => {
|
|
11826
|
-
console.log('[AuthContext] Cross-tab storage event:', event.type, event.key);
|
|
11827
11783
|
try {
|
|
11828
11784
|
if (event.type === 'clear') {
|
|
11829
|
-
console.log('[AuthContext] Detected logout in another tab');
|
|
11830
11785
|
setToken(null);
|
|
11831
11786
|
setUser(null);
|
|
11832
11787
|
setAccountData(null);
|
|
@@ -11838,7 +11793,6 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
11838
11793
|
notifyAuthStateChange('CROSS_TAB_SYNC', null, null, null, null, false);
|
|
11839
11794
|
}
|
|
11840
11795
|
else if (event.type === 'remove' && (event.key === 'token' || event.key === 'user')) {
|
|
11841
|
-
console.log('[AuthContext] Detected token/user removal in another tab');
|
|
11842
11796
|
setToken(null);
|
|
11843
11797
|
setUser(null);
|
|
11844
11798
|
setAccountData(null);
|
|
@@ -11850,7 +11804,6 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
11850
11804
|
notifyAuthStateChange('CROSS_TAB_SYNC', null, null, null, null, false);
|
|
11851
11805
|
}
|
|
11852
11806
|
else if (event.type === 'set' && event.key === 'token') {
|
|
11853
|
-
console.log('[AuthContext] Detected login in another tab');
|
|
11854
11807
|
const storedToken = await tokenStorage.getToken();
|
|
11855
11808
|
const storedUser = await tokenStorage.getUser();
|
|
11856
11809
|
const storedAccountData = await tokenStorage.getAccountData();
|
|
@@ -11863,9 +11816,7 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
11863
11816
|
setContactId(storedContactId);
|
|
11864
11817
|
}
|
|
11865
11818
|
setIsVerified(true);
|
|
11866
|
-
smartlinks__namespace.auth.verifyToken(storedToken.token).catch(
|
|
11867
|
-
console.warn('[AuthContext] Failed to restore bearer token from cross-tab sync:', err);
|
|
11868
|
-
});
|
|
11819
|
+
smartlinks__namespace.auth.verifyToken(storedToken.token).catch(() => { });
|
|
11869
11820
|
notifyAuthStateChange('CROSS_TAB_SYNC', storedUser, storedToken.token, storedAccountData, null, true, null, storedContactId);
|
|
11870
11821
|
}
|
|
11871
11822
|
}
|
|
@@ -11873,7 +11824,6 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
11873
11824
|
const cached = await tokenStorage.getAccountInfo();
|
|
11874
11825
|
if (cached && !cached.isStale) {
|
|
11875
11826
|
setAccountInfo(cached.data);
|
|
11876
|
-
console.log('[AuthContext] Account info synced from another tab');
|
|
11877
11827
|
}
|
|
11878
11828
|
}
|
|
11879
11829
|
}
|
|
@@ -11882,7 +11832,6 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
11882
11832
|
}
|
|
11883
11833
|
});
|
|
11884
11834
|
return () => {
|
|
11885
|
-
console.log('[AuthContext] Cleaning up cross-tab synchronization');
|
|
11886
11835
|
unsubscribe();
|
|
11887
11836
|
};
|
|
11888
11837
|
}, [proxyMode, notifyAuthStateChange]);
|
|
@@ -11897,22 +11846,14 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
11897
11846
|
event.data?.messageId === messageId) {
|
|
11898
11847
|
window.removeEventListener('message', handleAck);
|
|
11899
11848
|
clearTimeout(timeoutHandle);
|
|
11900
|
-
if (event.data.success) {
|
|
11901
|
-
console.log('[AuthContext] Parent acknowledged login successfully');
|
|
11902
|
-
}
|
|
11903
|
-
else {
|
|
11904
|
-
console.warn('[AuthContext] Parent rejected login:', event.data.error);
|
|
11905
|
-
}
|
|
11906
11849
|
resolve();
|
|
11907
11850
|
}
|
|
11908
11851
|
};
|
|
11909
11852
|
const timeoutHandle = setTimeout(() => {
|
|
11910
11853
|
window.removeEventListener('message', handleAck);
|
|
11911
|
-
|
|
11912
|
-
resolve(); // Don't block forever - fallback to current behavior
|
|
11854
|
+
resolve();
|
|
11913
11855
|
}, timeoutMs);
|
|
11914
11856
|
window.addEventListener('message', handleAck);
|
|
11915
|
-
console.log('[AuthContext] Sending login to parent, awaiting acknowledgment:', messageId);
|
|
11916
11857
|
smartlinks.iframe.sendParentCustom('smartlinks:authkit:login', {
|
|
11917
11858
|
messageId,
|
|
11918
11859
|
token: authToken,
|
|
@@ -11932,9 +11873,7 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
11932
11873
|
if (authAccountData) {
|
|
11933
11874
|
await tokenStorage.saveAccountData(authAccountData);
|
|
11934
11875
|
}
|
|
11935
|
-
smartlinks__namespace.auth.verifyToken(authToken).catch(
|
|
11936
|
-
console.warn('Failed to set bearer token on login:', err);
|
|
11937
|
-
});
|
|
11876
|
+
smartlinks__namespace.auth.verifyToken(authToken).catch(() => { });
|
|
11938
11877
|
}
|
|
11939
11878
|
// Always update memory state (but NOT isVerified yet - wait for parent ack in iframe mode)
|
|
11940
11879
|
setToken(authToken);
|
|
@@ -11946,7 +11885,6 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
11946
11885
|
// This ensures the parent has validated the token and the server recognizes
|
|
11947
11886
|
// the session before we attempt any authenticated API calls (like syncContact)
|
|
11948
11887
|
if (smartlinks.iframe.isIframe()) {
|
|
11949
|
-
console.log('[AuthContext] Notifying parent of login and waiting for acknowledgment');
|
|
11950
11888
|
await notifyParentLoginAndWait(authToken, authUser, authAccountData);
|
|
11951
11889
|
}
|
|
11952
11890
|
// NOW set isVerified - after parent has acknowledged and session is ready
|
|
@@ -11960,9 +11898,7 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
11960
11898
|
});
|
|
11961
11899
|
// Optionally preload account info on login (standalone mode only)
|
|
11962
11900
|
if (!proxyMode && preloadAccountInfo) {
|
|
11963
|
-
getAccount(true).catch(
|
|
11964
|
-
console.warn('[AuthContext] Failed to preload account info:', error);
|
|
11965
|
-
});
|
|
11901
|
+
getAccount(true).catch(() => { });
|
|
11966
11902
|
}
|
|
11967
11903
|
}
|
|
11968
11904
|
catch (error) {
|
|
@@ -11974,18 +11910,12 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
11974
11910
|
const currentUser = user;
|
|
11975
11911
|
const currentContactId = contactId;
|
|
11976
11912
|
try {
|
|
11977
|
-
// Clear Google Sign-In session on native side (fire-and-forget with timeout)
|
|
11978
|
-
// This ensures the next login shows the account picker instead of auto-signing in
|
|
11979
|
-
console.log('[AuthContext] Checking for native Google sign-out...');
|
|
11980
11913
|
try {
|
|
11981
|
-
// Dynamic import to avoid circular dependency
|
|
11982
11914
|
const { signOutGoogleNative } = await Promise.resolve().then(function () { return SmartlinksAuthUI$1; });
|
|
11983
11915
|
await signOutGoogleNative();
|
|
11984
|
-
console.log('[AuthContext] Native Google sign-out completed');
|
|
11985
11916
|
}
|
|
11986
11917
|
catch (err) {
|
|
11987
|
-
// signOutGoogleNative is fire-and-forget
|
|
11988
|
-
console.log('[AuthContext] Native Google sign-out skipped or failed (non-blocking):', err);
|
|
11918
|
+
// signOutGoogleNative is fire-and-forget
|
|
11989
11919
|
}
|
|
11990
11920
|
// Only clear persistent storage in standalone mode
|
|
11991
11921
|
if (!proxyMode) {
|
|
@@ -12003,7 +11933,6 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
12003
11933
|
pendingVerificationRef.current = false;
|
|
12004
11934
|
// Cross-iframe auth state synchronization
|
|
12005
11935
|
if (smartlinks.iframe.isIframe()) {
|
|
12006
|
-
console.log('[AuthContext] Notifying parent of logout via postMessage');
|
|
12007
11936
|
smartlinks.iframe.sendParentCustom('smartlinks:authkit:logout', {});
|
|
12008
11937
|
}
|
|
12009
11938
|
notifyAuthStateChange('LOGOUT', null, null, null, null, false);
|
|
@@ -12045,7 +11974,6 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
12045
11974
|
// Refresh token - validates current token and extends session if backend supports it
|
|
12046
11975
|
const refreshToken = React.useCallback(async () => {
|
|
12047
11976
|
if (proxyMode) {
|
|
12048
|
-
console.log('[AuthContext] Proxy mode: token refresh handled by parent');
|
|
12049
11977
|
throw new Error('Token refresh in proxy mode is handled by the parent application');
|
|
12050
11978
|
}
|
|
12051
11979
|
const storedToken = await tokenStorage.getToken();
|
|
@@ -12053,20 +11981,13 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
12053
11981
|
throw new Error('No token to refresh. Please login first.');
|
|
12054
11982
|
}
|
|
12055
11983
|
try {
|
|
12056
|
-
console.log('[AuthContext] Refreshing token...');
|
|
12057
|
-
// Verify current token is still valid
|
|
12058
11984
|
const verifyResult = await smartlinks__namespace.auth.verifyToken(storedToken.token);
|
|
12059
11985
|
if (!verifyResult.valid) {
|
|
12060
|
-
console.warn('[AuthContext] Token is no longer valid, clearing session');
|
|
12061
11986
|
await logout();
|
|
12062
11987
|
throw new Error('Token expired or invalid. Please login again.');
|
|
12063
11988
|
}
|
|
12064
|
-
|
|
12065
|
-
// Backend JWT remains valid, we just update our local tracking
|
|
12066
|
-
const newExpiresAt = Date.now() + (7 * 24 * 60 * 60 * 1000); // 7 days from now
|
|
11989
|
+
const newExpiresAt = Date.now() + (7 * 24 * 60 * 60 * 1000);
|
|
12067
11990
|
await tokenStorage.saveToken(storedToken.token, newExpiresAt);
|
|
12068
|
-
console.log('[AuthContext] Token verified and expiration extended to:', new Date(newExpiresAt).toISOString());
|
|
12069
|
-
// Update verified state
|
|
12070
11991
|
setIsVerified(true);
|
|
12071
11992
|
pendingVerificationRef.current = false;
|
|
12072
11993
|
notifyAuthStateChange('TOKEN_REFRESH', user, storedToken.token, accountData, accountInfo, true, contact, contactId);
|
|
@@ -12074,12 +11995,9 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
12074
11995
|
}
|
|
12075
11996
|
catch (error) {
|
|
12076
11997
|
console.error('[AuthContext] Token refresh failed:', error);
|
|
12077
|
-
// If it's a network error, don't logout
|
|
12078
11998
|
if (isNetworkError(error)) {
|
|
12079
|
-
console.warn('[AuthContext] Network error during refresh, keeping session');
|
|
12080
11999
|
throw error;
|
|
12081
12000
|
}
|
|
12082
|
-
// Auth error - clear session
|
|
12083
12001
|
await logout();
|
|
12084
12002
|
throw new Error('Token refresh failed. Please login again.');
|
|
12085
12003
|
}
|
|
@@ -12087,7 +12005,6 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
12087
12005
|
const getAccount = React.useCallback(async (forceRefresh = false) => {
|
|
12088
12006
|
try {
|
|
12089
12007
|
if (proxyMode) {
|
|
12090
|
-
console.log('[AuthContext] Proxy mode: fetching account from parent');
|
|
12091
12008
|
const freshAccountInfo = await smartlinks__namespace.auth.getAccount();
|
|
12092
12009
|
setAccountInfo(freshAccountInfo);
|
|
12093
12010
|
setAccountData(freshAccountInfo);
|
|
@@ -12100,11 +12017,9 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
12100
12017
|
if (!forceRefresh) {
|
|
12101
12018
|
const cached = await tokenStorage.getAccountInfo();
|
|
12102
12019
|
if (cached && !cached.isStale) {
|
|
12103
|
-
console.log('[AuthContext] Returning cached account info');
|
|
12104
12020
|
return cached.data;
|
|
12105
12021
|
}
|
|
12106
12022
|
}
|
|
12107
|
-
console.log('[AuthContext] Fetching fresh account info from API');
|
|
12108
12023
|
const freshAccountInfo = await smartlinks__namespace.auth.getAccount();
|
|
12109
12024
|
await tokenStorage.saveAccountInfo(freshAccountInfo, accountCacheTTL);
|
|
12110
12025
|
setAccountInfo(freshAccountInfo);
|
|
@@ -12116,7 +12031,6 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
12116
12031
|
if (!proxyMode) {
|
|
12117
12032
|
const cached = await tokenStorage.getAccountInfo();
|
|
12118
12033
|
if (cached) {
|
|
12119
|
-
console.warn('[AuthContext] Returning stale cached data due to API error');
|
|
12120
12034
|
return cached.data;
|
|
12121
12035
|
}
|
|
12122
12036
|
}
|
|
@@ -12139,30 +12053,22 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
12139
12053
|
};
|
|
12140
12054
|
}, []);
|
|
12141
12055
|
const retryVerification = React.useCallback(async () => {
|
|
12142
|
-
if (!token || !user)
|
|
12143
|
-
console.log('[AuthContext] No session to verify');
|
|
12056
|
+
if (!token || !user)
|
|
12144
12057
|
return false;
|
|
12145
|
-
|
|
12146
|
-
if (isVerified) {
|
|
12147
|
-
console.log('[AuthContext] Session already verified');
|
|
12058
|
+
if (isVerified)
|
|
12148
12059
|
return true;
|
|
12149
|
-
}
|
|
12150
12060
|
try {
|
|
12151
|
-
console.log('[AuthContext] Retrying session verification...');
|
|
12152
12061
|
await smartlinks__namespace.auth.verifyToken(token);
|
|
12153
12062
|
setIsVerified(true);
|
|
12154
12063
|
pendingVerificationRef.current = false;
|
|
12155
|
-
console.log('[AuthContext] Session verified on retry');
|
|
12156
12064
|
notifyAuthStateChange('SESSION_VERIFIED', user, token, accountData, accountInfo, true, contact, contactId);
|
|
12157
12065
|
return true;
|
|
12158
12066
|
}
|
|
12159
12067
|
catch (err) {
|
|
12160
12068
|
if (isNetworkError(err)) {
|
|
12161
|
-
console.warn('[AuthContext] Network still unavailable, will retry later');
|
|
12162
12069
|
return false;
|
|
12163
12070
|
}
|
|
12164
12071
|
else {
|
|
12165
|
-
console.warn('[AuthContext] Session invalid on retry, logging out');
|
|
12166
12072
|
await logout();
|
|
12167
12073
|
return false;
|
|
12168
12074
|
}
|
|
@@ -12173,15 +12079,12 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
12173
12079
|
if (proxyMode)
|
|
12174
12080
|
return;
|
|
12175
12081
|
const handleOnline = () => {
|
|
12176
|
-
console.log('[AuthContext] Network reconnected');
|
|
12177
12082
|
setIsOnline(true);
|
|
12178
12083
|
if (pendingVerificationRef.current && token && user) {
|
|
12179
|
-
console.log('[AuthContext] Retrying pending verification after reconnect...');
|
|
12180
12084
|
retryVerification();
|
|
12181
12085
|
}
|
|
12182
12086
|
};
|
|
12183
12087
|
const handleOffline = () => {
|
|
12184
|
-
console.log('[AuthContext] Network disconnected');
|
|
12185
12088
|
setIsOnline(false);
|
|
12186
12089
|
};
|
|
12187
12090
|
window.addEventListener('online', handleOnline);
|
|
@@ -12196,46 +12099,31 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
12196
12099
|
if (proxyMode || !enableAutoRefresh || !token || !user) {
|
|
12197
12100
|
return;
|
|
12198
12101
|
}
|
|
12199
|
-
console.log('[AuthContext] Setting up automatic token refresh (interval:', refreshCheckInterval, 'ms, threshold:', refreshThresholdPercent, '%)');
|
|
12200
12102
|
const checkAndRefresh = async () => {
|
|
12201
12103
|
try {
|
|
12202
12104
|
const storedToken = await tokenStorage.getToken();
|
|
12203
|
-
if (!storedToken?.expiresAt)
|
|
12204
|
-
console.log('[AuthContext] No token expiration info, skipping refresh check');
|
|
12105
|
+
if (!storedToken?.expiresAt)
|
|
12205
12106
|
return;
|
|
12206
|
-
}
|
|
12207
12107
|
const now = Date.now();
|
|
12208
|
-
const tokenLifetime = storedToken.expiresAt - (storedToken.expiresAt - (7 * 24 * 60 * 60 * 1000));
|
|
12108
|
+
const tokenLifetime = storedToken.expiresAt - (storedToken.expiresAt - (7 * 24 * 60 * 60 * 1000));
|
|
12209
12109
|
const tokenAge = now - (storedToken.expiresAt - (7 * 24 * 60 * 60 * 1000));
|
|
12210
12110
|
const percentUsed = (tokenAge / tokenLifetime) * 100;
|
|
12211
|
-
// Calculate time remaining
|
|
12212
|
-
const timeRemaining = storedToken.expiresAt - now;
|
|
12213
|
-
const hoursRemaining = Math.round(timeRemaining / (60 * 60 * 1000));
|
|
12214
12111
|
if (percentUsed >= refreshThresholdPercent) {
|
|
12215
|
-
console.log(`[AuthContext] Token at ${Math.round(percentUsed)}% lifetime (${hoursRemaining}h remaining), refreshing...`);
|
|
12216
12112
|
try {
|
|
12217
12113
|
await refreshToken();
|
|
12218
|
-
console.log('[AuthContext] Automatic token refresh successful');
|
|
12219
12114
|
}
|
|
12220
12115
|
catch (refreshError) {
|
|
12221
|
-
|
|
12222
|
-
// Don't logout on refresh failure - user can still use the app until token actually expires
|
|
12116
|
+
// Don't logout on refresh failure
|
|
12223
12117
|
}
|
|
12224
12118
|
}
|
|
12225
|
-
else {
|
|
12226
|
-
console.log(`[AuthContext] Token at ${Math.round(percentUsed)}% lifetime (${hoursRemaining}h remaining), no refresh needed`);
|
|
12227
|
-
}
|
|
12228
12119
|
}
|
|
12229
12120
|
catch (error) {
|
|
12230
12121
|
console.error('[AuthContext] Error checking token for refresh:', error);
|
|
12231
12122
|
}
|
|
12232
12123
|
};
|
|
12233
|
-
// Check immediately on mount
|
|
12234
12124
|
checkAndRefresh();
|
|
12235
|
-
// Set up periodic check
|
|
12236
12125
|
const intervalId = setInterval(checkAndRefresh, refreshCheckInterval);
|
|
12237
12126
|
return () => {
|
|
12238
|
-
console.log('[AuthContext] Cleaning up automatic token refresh timer');
|
|
12239
12127
|
clearInterval(intervalId);
|
|
12240
12128
|
};
|
|
12241
12129
|
}, [proxyMode, enableAutoRefresh, refreshCheckInterval, refreshThresholdPercent, token, user, refreshToken]);
|
|
@@ -12459,75 +12347,42 @@ const loadGoogleIdentityServices = () => {
|
|
|
12459
12347
|
// Helper to detect WebView environments (Android/iOS)
|
|
12460
12348
|
const detectWebView = () => {
|
|
12461
12349
|
const ua = navigator.userAgent;
|
|
12462
|
-
console.log(`${LOG_PREFIX} 🔍 detectWebView checking UA:`, ua);
|
|
12463
12350
|
// Android WebView detection
|
|
12464
12351
|
if (/Android/i.test(ua)) {
|
|
12465
|
-
|
|
12466
|
-
// Modern Android WebViews include "wv" in UA string
|
|
12467
|
-
if (/\bwv\b/i.test(ua)) {
|
|
12468
|
-
console.log(`${LOG_PREFIX} ✅ Android WebView detected (wv in UA)`);
|
|
12352
|
+
if (/\bwv\b/i.test(ua))
|
|
12469
12353
|
return true;
|
|
12470
|
-
|
|
12471
|
-
// Check for legacy Android bridge
|
|
12472
|
-
if (typeof window.Android !== 'undefined') {
|
|
12473
|
-
console.log(`${LOG_PREFIX} ✅ Android WebView detected (Android bridge exists)`);
|
|
12354
|
+
if (typeof window.AuthKit !== 'undefined')
|
|
12474
12355
|
return true;
|
|
12475
|
-
}
|
|
12476
|
-
console.log(`${LOG_PREFIX} ❌ Android but not WebView`);
|
|
12477
12356
|
}
|
|
12478
12357
|
// iOS WKWebView detection
|
|
12479
12358
|
if (/iPhone|iPad|iPod/i.test(ua)) {
|
|
12480
|
-
console.log(`${LOG_PREFIX} 🔍 iOS device detected`);
|
|
12481
12359
|
const hasWebKitHandlers = !!window.webkit?.messageHandlers;
|
|
12482
12360
|
const isSafari = !!window.safari;
|
|
12483
|
-
|
|
12484
|
-
// WKWebView has webkit handlers but no safari object
|
|
12485
|
-
if (hasWebKitHandlers && !isSafari) {
|
|
12486
|
-
console.log(`${LOG_PREFIX} ✅ iOS WKWebView detected`);
|
|
12361
|
+
if (hasWebKitHandlers && !isSafari)
|
|
12487
12362
|
return true;
|
|
12488
|
-
}
|
|
12489
|
-
console.log(`${LOG_PREFIX} ❌ iOS but not WKWebView (likely Safari)`);
|
|
12490
12363
|
}
|
|
12491
|
-
console.log(`${LOG_PREFIX} ❌ Not a WebView environment`);
|
|
12492
12364
|
return false;
|
|
12493
12365
|
};
|
|
12494
12366
|
const getNativeBridge = () => {
|
|
12495
|
-
|
|
12496
|
-
|
|
12497
|
-
const native = window.Android;
|
|
12498
|
-
if (native?.signInWithGoogle) {
|
|
12499
|
-
console.log(`${LOG_PREFIX} ✅ Native bridge found!`, {
|
|
12500
|
-
signInWithGoogle: !!native.signInWithGoogle,
|
|
12501
|
-
signOutGoogle: !!native.signOutGoogle,
|
|
12502
|
-
checkGoogleSignIn: !!native.checkGoogleSignIn,
|
|
12503
|
-
});
|
|
12367
|
+
const native = window.AuthKit;
|
|
12368
|
+
if (native?.signInWithGoogle)
|
|
12504
12369
|
return native;
|
|
12505
|
-
}
|
|
12506
|
-
console.log(`${LOG_PREFIX} ❌ No native bridge found (Android.signInWithGoogle not available)`);
|
|
12507
12370
|
return null;
|
|
12508
12371
|
};
|
|
12509
12372
|
// Sign out from Google on the native side (clears cached Google account)
|
|
12510
12373
|
// This is fire-and-forget with a timeout - gracefully degrades if not supported
|
|
12511
12374
|
const signOutGoogleNative = async () => {
|
|
12512
12375
|
const nativeBridge = getNativeBridge();
|
|
12513
|
-
if (!nativeBridge?.signOutGoogle)
|
|
12514
|
-
console.log(`${LOG_PREFIX} 🚪 signOutGoogleNative: no native bridge or signOutGoogle not available`);
|
|
12376
|
+
if (!nativeBridge?.signOutGoogle)
|
|
12515
12377
|
return;
|
|
12516
|
-
}
|
|
12517
12378
|
const callbackId = `google_signout_${Date.now()}`;
|
|
12518
|
-
console.log(`${LOG_PREFIX} 🚪 Initiating native Google sign-out, callbackId:`, callbackId);
|
|
12519
12379
|
return new Promise((resolve) => {
|
|
12520
|
-
|
|
12521
|
-
const timeout = setTimeout(() => {
|
|
12522
|
-
console.log(`${LOG_PREFIX} 🚪 Native sign-out timed out (continuing anyway)`);
|
|
12523
|
-
resolve();
|
|
12524
|
-
}, 3000);
|
|
12380
|
+
const timeout = setTimeout(() => resolve(), 3000);
|
|
12525
12381
|
// Store original callback to restore later
|
|
12526
12382
|
const originalCallback = window.smartlinksNativeCallback;
|
|
12527
12383
|
window.smartlinksNativeCallback = (result) => {
|
|
12528
12384
|
if (result.callbackId === callbackId) {
|
|
12529
12385
|
clearTimeout(timeout);
|
|
12530
|
-
console.log(`${LOG_PREFIX} 🚪 Native Google sign-out result:`, result);
|
|
12531
12386
|
// Restore original callback
|
|
12532
12387
|
window.smartlinksNativeCallback = originalCallback;
|
|
12533
12388
|
resolve();
|
|
@@ -12541,37 +12396,21 @@ const signOutGoogleNative = async () => {
|
|
|
12541
12396
|
type: 'GOOGLE_SIGN_OUT',
|
|
12542
12397
|
callbackId,
|
|
12543
12398
|
});
|
|
12544
|
-
console.log(`${LOG_PREFIX} 🚪 Calling nativeBridge.signOutGoogle with payload:`, payload);
|
|
12545
|
-
// Use non-null assertion - method exists (verified by guard check above)
|
|
12546
|
-
// IMPORTANT: Must call directly on nativeBridge object for Android WebView proxy binding
|
|
12547
12399
|
nativeBridge.signOutGoogle(payload);
|
|
12548
12400
|
});
|
|
12549
12401
|
};
|
|
12550
12402
|
const checkSilentGoogleSignIn = async (clientId, googleClientId) => {
|
|
12551
12403
|
const nativeBridge = getNativeBridge();
|
|
12552
|
-
if (!nativeBridge?.checkGoogleSignIn)
|
|
12553
|
-
console.log(`${LOG_PREFIX} 🔇 checkSilentGoogleSignIn: no native bridge or checkGoogleSignIn not available`);
|
|
12404
|
+
if (!nativeBridge?.checkGoogleSignIn)
|
|
12554
12405
|
return null;
|
|
12555
|
-
}
|
|
12556
12406
|
const callbackId = `google_check_${Date.now()}`;
|
|
12557
|
-
console.log(`${LOG_PREFIX} 🔇 Checking for silent Google sign-in, callbackId:`, callbackId);
|
|
12558
12407
|
return new Promise((resolve) => {
|
|
12559
|
-
|
|
12560
|
-
const timeout = setTimeout(() => {
|
|
12561
|
-
console.log(`${LOG_PREFIX} 🔇 Silent sign-in check timed out`);
|
|
12562
|
-
resolve(null);
|
|
12563
|
-
}, 5000);
|
|
12408
|
+
const timeout = setTimeout(() => resolve(null), 5000);
|
|
12564
12409
|
// Store original callback to restore later
|
|
12565
12410
|
const originalCallback = window.smartlinksNativeCallback;
|
|
12566
12411
|
window.smartlinksNativeCallback = (result) => {
|
|
12567
12412
|
if (result.callbackId === callbackId) {
|
|
12568
12413
|
clearTimeout(timeout);
|
|
12569
|
-
console.log(`${LOG_PREFIX} 🔇 Silent sign-in check result:`, {
|
|
12570
|
-
success: result.success,
|
|
12571
|
-
isSignedIn: result.isSignedIn,
|
|
12572
|
-
hasIdToken: !!result.idToken,
|
|
12573
|
-
email: result.email,
|
|
12574
|
-
});
|
|
12575
12414
|
// Restore original callback
|
|
12576
12415
|
window.smartlinksNativeCallback = originalCallback;
|
|
12577
12416
|
if (result.success) {
|
|
@@ -12599,9 +12438,6 @@ const checkSilentGoogleSignIn = async (clientId, googleClientId) => {
|
|
|
12599
12438
|
serverClientId: googleClientId, // Alias for Android SDK
|
|
12600
12439
|
callbackId,
|
|
12601
12440
|
});
|
|
12602
|
-
console.log(`${LOG_PREFIX} 🔇 Calling nativeBridge.checkGoogleSignIn with payload:`, payload);
|
|
12603
|
-
// Use non-null assertion - method exists (verified by guard check above)
|
|
12604
|
-
// IMPORTANT: Must call directly on nativeBridge object for Android WebView proxy binding
|
|
12605
12441
|
nativeBridge.checkGoogleSignIn(payload);
|
|
12606
12442
|
});
|
|
12607
12443
|
};
|
|
@@ -12688,43 +12524,15 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
12688
12524
|
mediaQuery.addEventListener('change', updateTheme);
|
|
12689
12525
|
return () => mediaQuery.removeEventListener('change', updateTheme);
|
|
12690
12526
|
}, [theme]);
|
|
12691
|
-
//
|
|
12692
|
-
|
|
12693
|
-
console.log(`${LOG_PREFIX} 🚀 COMPONENT MOUNTED with props:`, {
|
|
12694
|
-
apiEndpoint,
|
|
12695
|
-
clientId,
|
|
12696
|
-
clientName,
|
|
12697
|
-
redirectUrl,
|
|
12698
|
-
redirectUrlType: typeof redirectUrl,
|
|
12699
|
-
redirectUrlTruthy: !!redirectUrl,
|
|
12700
|
-
proxyMode,
|
|
12701
|
-
theme,
|
|
12702
|
-
initialMode,
|
|
12703
|
-
skipConfigFetch,
|
|
12704
|
-
minimal,
|
|
12705
|
-
enableSilentGoogleSignIn,
|
|
12706
|
-
enabledProviders,
|
|
12707
|
-
timestamp: new Date().toISOString()
|
|
12708
|
-
});
|
|
12709
|
-
}, []); // Only log on mount
|
|
12527
|
+
// Version tracking for debugging if needed
|
|
12528
|
+
// console.log(`${LOG_PREFIX} Component mounted, v${AUTH_UI_VERSION}`);
|
|
12710
12529
|
// Reinitialize Smartlinks SDK when apiEndpoint or proxyMode changes
|
|
12711
12530
|
// IMPORTANT: Preserve bearer token during reinitialization
|
|
12712
12531
|
React.useEffect(() => {
|
|
12713
|
-
console.log(`${LOG_PREFIX} 🔧 SDK INIT useEffect triggered`, {
|
|
12714
|
-
apiEndpoint,
|
|
12715
|
-
proxyMode,
|
|
12716
|
-
hasLogger: !!logger,
|
|
12717
|
-
timestamp: new Date().toISOString()
|
|
12718
|
-
});
|
|
12719
12532
|
log.log('SDK reinitialize useEffect triggered', { apiEndpoint, proxyMode });
|
|
12720
12533
|
setSdkReady(false); // Mark SDK as not ready during reinitialization
|
|
12721
12534
|
const reinitializeWithToken = async () => {
|
|
12722
12535
|
if (apiEndpoint) {
|
|
12723
|
-
console.log(`${LOG_PREFIX} 🔧 Reinitializing SDK with:`, {
|
|
12724
|
-
baseURL: apiEndpoint,
|
|
12725
|
-
proxyMode: proxyMode,
|
|
12726
|
-
ngrokSkipBrowserWarning: true
|
|
12727
|
-
});
|
|
12728
12536
|
log.log('Reinitializing SDK with baseURL:', apiEndpoint, 'proxyMode:', proxyMode);
|
|
12729
12537
|
// Get current token before reinitializing (only in standalone mode)
|
|
12730
12538
|
const currentToken = !proxyMode ? await auth.getToken() : null;
|
|
@@ -12734,7 +12542,6 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
12734
12542
|
ngrokSkipBrowserWarning: true,
|
|
12735
12543
|
logger: logger, // Pass logger to SDK for verbose SDK logging
|
|
12736
12544
|
});
|
|
12737
|
-
console.log(`${LOG_PREFIX} ✅ SDK reinitialized, proxyMode:`, proxyMode);
|
|
12738
12545
|
log.log('SDK reinitialized successfully');
|
|
12739
12546
|
// Restore bearer token after reinitialization using auth.verifyToken (standalone mode only)
|
|
12740
12547
|
if (currentToken && !proxyMode) {
|
|
@@ -12742,20 +12549,15 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
12742
12549
|
log.warn('Failed to restore bearer token after reinit:', err);
|
|
12743
12550
|
});
|
|
12744
12551
|
}
|
|
12745
|
-
// Mark SDK as ready
|
|
12746
|
-
console.log(`${LOG_PREFIX} ✅ Setting sdkReady=true (with apiEndpoint)`);
|
|
12747
12552
|
setSdkReady(true);
|
|
12748
12553
|
}
|
|
12749
12554
|
else if (proxyMode) {
|
|
12750
12555
|
// In proxy mode without custom endpoint, SDK should already be initialized by parent
|
|
12751
|
-
console.log(`${LOG_PREFIX} ⚠️ Proxy mode WITHOUT apiEndpoint - expecting SDK already initialized by parent`);
|
|
12752
12556
|
log.log('Proxy mode without apiEndpoint, SDK already initialized by parent');
|
|
12753
12557
|
setSdkReady(true);
|
|
12754
12558
|
}
|
|
12755
12559
|
else {
|
|
12756
|
-
console.log(`${LOG_PREFIX} ℹ️ No apiEndpoint, no proxyMode - SDK already initialized by App`);
|
|
12757
12560
|
log.log('No apiEndpoint, SDK already initialized by App');
|
|
12758
|
-
// SDK was initialized by App component, mark as ready
|
|
12759
12561
|
setSdkReady(true);
|
|
12760
12562
|
}
|
|
12761
12563
|
};
|
|
@@ -12763,48 +12565,19 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
12763
12565
|
}, [apiEndpoint, proxyMode, auth, logger, log]);
|
|
12764
12566
|
// Get the effective redirect URL (use prop or default to current page)
|
|
12765
12567
|
const getRedirectUrl = () => {
|
|
12766
|
-
|
|
12767
|
-
redirectUrlProp: redirectUrl,
|
|
12768
|
-
redirectUrlPropType: typeof redirectUrl,
|
|
12769
|
-
redirectUrlPropTruthy: !!redirectUrl,
|
|
12770
|
-
currentHref: window.location.href
|
|
12771
|
-
});
|
|
12772
|
-
if (redirectUrl) {
|
|
12773
|
-
console.log(`${LOG_PREFIX} 🔗 Using redirectUrl prop:`, redirectUrl);
|
|
12568
|
+
if (redirectUrl)
|
|
12774
12569
|
return redirectUrl;
|
|
12775
|
-
|
|
12776
|
-
|
|
12777
|
-
// Remove any existing query parameters to avoid duplication
|
|
12778
|
-
const currentUrl = window.location.href.split('?')[0];
|
|
12779
|
-
console.log(`${LOG_PREFIX} 🔗 No redirectUrl prop, using current URL:`, currentUrl);
|
|
12780
|
-
return currentUrl;
|
|
12570
|
+
// Get the full current URL including hash routes, strip query params
|
|
12571
|
+
return window.location.href.split('?')[0];
|
|
12781
12572
|
};
|
|
12782
12573
|
// Fetch UI configuration
|
|
12783
12574
|
React.useEffect(() => {
|
|
12784
|
-
console.log(`${LOG_PREFIX} 📋 CONFIG FETCH useEffect triggered`, {
|
|
12785
|
-
skipConfigFetch,
|
|
12786
|
-
clientId,
|
|
12787
|
-
apiEndpoint,
|
|
12788
|
-
sdkReady,
|
|
12789
|
-
proxyMode,
|
|
12790
|
-
timestamp: new Date().toISOString()
|
|
12791
|
-
});
|
|
12792
|
-
log.log('Config fetch useEffect triggered', {
|
|
12793
|
-
skipConfigFetch,
|
|
12794
|
-
clientId,
|
|
12795
|
-
clientIdType: typeof clientId,
|
|
12796
|
-
clientIdTruthy: !!clientId,
|
|
12797
|
-
apiEndpoint,
|
|
12798
|
-
sdkReady
|
|
12799
|
-
});
|
|
12800
12575
|
// Wait for SDK to be ready before fetching config
|
|
12801
12576
|
if (!sdkReady) {
|
|
12802
|
-
console.log(`${LOG_PREFIX} ⏳ SDK not ready yet, waiting before config fetch...`);
|
|
12803
12577
|
log.log('SDK not ready yet, waiting...');
|
|
12804
12578
|
return;
|
|
12805
12579
|
}
|
|
12806
12580
|
if (skipConfigFetch) {
|
|
12807
|
-
console.log(`${LOG_PREFIX} ⏭️ Skipping config fetch - skipConfigFetch is true`);
|
|
12808
12581
|
log.log('Skipping config fetch - skipConfigFetch is true');
|
|
12809
12582
|
setConfig(customization || {});
|
|
12810
12583
|
return;
|
|
@@ -12812,7 +12585,6 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
12812
12585
|
const fetchConfig = async () => {
|
|
12813
12586
|
// If no clientId provided, use default config immediately without API call
|
|
12814
12587
|
if (!clientId) {
|
|
12815
|
-
console.log(`${LOG_PREFIX} ⚠️ No clientId provided, using default config`);
|
|
12816
12588
|
log.log('No clientId provided, using default config');
|
|
12817
12589
|
const defaultConfig = {
|
|
12818
12590
|
...DEFAULT_AUTH_CONFIG,
|
|
@@ -12832,39 +12604,25 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
12832
12604
|
const age = Date.now() - timestamp;
|
|
12833
12605
|
// Use cache if less than 1 hour old
|
|
12834
12606
|
if (age < 3600000) {
|
|
12835
|
-
console.log(`${LOG_PREFIX} 📦 Using cached config (age:`, Math.round(age / 1000), 'seconds)');
|
|
12836
12607
|
setConfig({ ...cachedConfig, ...customization });
|
|
12837
12608
|
setConfigLoading(false);
|
|
12838
12609
|
// Fetch in background to update cache
|
|
12839
|
-
console.log(`${LOG_PREFIX} 🔄 Background refresh of config via SDK...`);
|
|
12840
12610
|
api.fetchConfig().then(freshConfig => {
|
|
12841
|
-
console.log(`${LOG_PREFIX} ✅ Background config refresh complete`);
|
|
12842
12611
|
localStorage.setItem(cacheKey, JSON.stringify({
|
|
12843
12612
|
config: freshConfig,
|
|
12844
12613
|
timestamp: Date.now()
|
|
12845
12614
|
}));
|
|
12846
|
-
// Update config if it changed
|
|
12847
12615
|
setConfig({ ...freshConfig, ...customization });
|
|
12848
|
-
}).catch(
|
|
12849
|
-
console.log(`${LOG_PREFIX} ❌ Background config refresh failed:`, err);
|
|
12850
|
-
});
|
|
12616
|
+
}).catch(() => { });
|
|
12851
12617
|
return;
|
|
12852
12618
|
}
|
|
12853
12619
|
}
|
|
12854
12620
|
}
|
|
12855
|
-
else {
|
|
12856
|
-
console.log(`${LOG_PREFIX} ⚠️ Config caching disabled, fetching fresh config`);
|
|
12857
|
-
}
|
|
12858
|
-
// Fetch from API
|
|
12859
|
-
console.log(`${LOG_PREFIX} 🌐 Fetching config via SDK for clientId:`, clientId, 'proxyMode:', proxyMode);
|
|
12860
12621
|
log.log('Fetching config from API for clientId:', clientId);
|
|
12861
12622
|
const fetchedConfig = await api.fetchConfig();
|
|
12862
|
-
console.log(`${LOG_PREFIX} ✅ Config fetched successfully:`, fetchedConfig);
|
|
12863
12623
|
log.log('Received config:', fetchedConfig);
|
|
12864
|
-
// Merge with customization props (props take precedence)
|
|
12865
12624
|
const mergedConfig = { ...fetchedConfig, ...customization };
|
|
12866
12625
|
setConfig(mergedConfig);
|
|
12867
|
-
// Cache the fetched config (unless caching is disabled)
|
|
12868
12626
|
if (!disableConfigCache) {
|
|
12869
12627
|
localStorage.setItem(cacheKey, JSON.stringify({
|
|
12870
12628
|
config: fetchedConfig,
|
|
@@ -12873,7 +12631,6 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
12873
12631
|
}
|
|
12874
12632
|
}
|
|
12875
12633
|
catch (err) {
|
|
12876
|
-
console.log(`${LOG_PREFIX} ❌ Config fetch failed:`, err);
|
|
12877
12634
|
log.error('Failed to fetch config:', err);
|
|
12878
12635
|
setConfig(customization || {});
|
|
12879
12636
|
}
|
|
@@ -12889,13 +12646,10 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
12889
12646
|
return;
|
|
12890
12647
|
const fetchSchema = async () => {
|
|
12891
12648
|
try {
|
|
12892
|
-
console.log(`${LOG_PREFIX} 📋 Fetching contact schema for collection:`, collectionId);
|
|
12893
12649
|
const schema = await smartlinks__namespace.contact.publicGetSchema(collectionId);
|
|
12894
|
-
console.log(`${LOG_PREFIX} ✅ Schema loaded:`, schema);
|
|
12895
12650
|
setContactSchema(schema);
|
|
12896
12651
|
}
|
|
12897
12652
|
catch (err) {
|
|
12898
|
-
console.warn(`${LOG_PREFIX} ⚠️ Failed to fetch schema (non-fatal):`, err);
|
|
12899
12653
|
// Non-fatal - registration will work without schema fields
|
|
12900
12654
|
}
|
|
12901
12655
|
};
|
|
@@ -12909,17 +12663,14 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
12909
12663
|
}
|
|
12910
12664
|
const googleClientId = config?.googleClientId || DEFAULT_GOOGLE_CLIENT_ID;
|
|
12911
12665
|
const performSilentSignIn = async () => {
|
|
12912
|
-
console.log(`${LOG_PREFIX} 🔇 Silent Google Sign-In check enabled, checking native session...`);
|
|
12913
12666
|
try {
|
|
12914
12667
|
const result = await checkSilentGoogleSignIn(clientId, googleClientId);
|
|
12915
12668
|
setSilentSignInChecked(true);
|
|
12916
12669
|
if (result?.isSignedIn && result.idToken) {
|
|
12917
|
-
console.log(`${LOG_PREFIX} 🔇 Silent sign-in found existing Google session, authenticating...`);
|
|
12918
12670
|
setLoading(true);
|
|
12919
12671
|
try {
|
|
12920
12672
|
const authResponse = await api.loginWithGoogle(result.idToken);
|
|
12921
12673
|
if (authResponse.token) {
|
|
12922
|
-
console.log(`${LOG_PREFIX} 🔇 Silent sign-in successful!`);
|
|
12923
12674
|
await auth.login(authResponse.token, authResponse.user, authResponse.accountData, false, getExpirationFromResponse(authResponse));
|
|
12924
12675
|
setAuthSuccess(true);
|
|
12925
12676
|
setSuccessMessage('Signed in automatically with Google!');
|
|
@@ -12927,19 +12678,14 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
12927
12678
|
}
|
|
12928
12679
|
}
|
|
12929
12680
|
catch (err) {
|
|
12930
|
-
console.warn(`${LOG_PREFIX} 🔇 Silent sign-in backend auth failed:`, err);
|
|
12931
12681
|
// Don't show error - user can still log in manually
|
|
12932
12682
|
}
|
|
12933
12683
|
finally {
|
|
12934
12684
|
setLoading(false);
|
|
12935
12685
|
}
|
|
12936
12686
|
}
|
|
12937
|
-
else {
|
|
12938
|
-
console.log(`${LOG_PREFIX} 🔇 No existing Google session found`);
|
|
12939
|
-
}
|
|
12940
12687
|
}
|
|
12941
12688
|
catch (err) {
|
|
12942
|
-
console.warn(`${LOG_PREFIX} 🔇 Silent sign-in check failed:`, err);
|
|
12943
12689
|
setSilentSignInChecked(true);
|
|
12944
12690
|
}
|
|
12945
12691
|
};
|
|
@@ -13184,7 +12930,12 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
13184
12930
|
// Defensive check: validate response before accessing properties
|
|
13185
12931
|
// SDK should throw on 401/error responses, but handle edge cases gracefully
|
|
13186
12932
|
if (!response) {
|
|
13187
|
-
|
|
12933
|
+
// For registration, a missing response might indicate a 409 that wasn't thrown
|
|
12934
|
+
if (mode === 'login') {
|
|
12935
|
+
throw new Error('Authentication failed - no response received');
|
|
12936
|
+
}
|
|
12937
|
+
// For register mode, throw a more specific error
|
|
12938
|
+
throw new Error('Registration failed - please try again');
|
|
13188
12939
|
}
|
|
13189
12940
|
// Get email verification mode from response or config (default: verify-auto-login)
|
|
13190
12941
|
const verificationMode = response.emailVerificationMode || config?.emailVerification?.mode || 'verify-auto-login';
|
|
@@ -13260,7 +13011,9 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
13260
13011
|
}
|
|
13261
13012
|
catch (err) {
|
|
13262
13013
|
// Check if error is about email already registered (409 conflict)
|
|
13263
|
-
|
|
13014
|
+
// Handle both SmartlinksApiError (statusCode 409) and plain Error with keyword matching
|
|
13015
|
+
if (mode === 'register' && (isConflictError(err) ||
|
|
13016
|
+
(err instanceof Error && /already (registered|exists)/i.test(err.message)))) {
|
|
13264
13017
|
setShowResendVerification(true);
|
|
13265
13018
|
setResendEmail(data.email);
|
|
13266
13019
|
setError('This email is already registered. If you didn\'t receive the verification email, you can resend it below.');
|
|
@@ -13318,184 +13071,91 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
13318
13071
|
}
|
|
13319
13072
|
};
|
|
13320
13073
|
const handleGoogleLogin = async () => {
|
|
13321
|
-
console.log(`${LOG_PREFIX} 🚀 handleGoogleLogin called`);
|
|
13322
|
-
// Use custom client ID from config, or fall back to default Smartlinks client ID
|
|
13323
13074
|
const googleClientId = config?.googleClientId || DEFAULT_GOOGLE_CLIENT_ID;
|
|
13324
|
-
// Determine OAuth flow: default to 'oneTap' for better UX, but allow 'popup' for iframe compatibility
|
|
13325
13075
|
const configuredFlow = config?.googleOAuthFlow || 'oneTap';
|
|
13326
|
-
// Check for native bridge and WebView environment
|
|
13327
|
-
console.log(`${LOG_PREFIX} 🔍 Checking environment...`);
|
|
13328
13076
|
const isWebView = detectWebView();
|
|
13329
13077
|
const nativeBridge = getNativeBridge();
|
|
13330
|
-
// For oneTap, automatically use redirect flow in WebView environments (if no native bridge)
|
|
13331
13078
|
const oauthFlow = (configuredFlow === 'oneTap' && isWebView && !nativeBridge) ? 'redirect' : configuredFlow;
|
|
13332
|
-
// Log Google Auth configuration for debugging
|
|
13333
|
-
console.log(`${LOG_PREFIX} 📋 Google Auth configuration:`, {
|
|
13334
|
-
googleClientId,
|
|
13335
|
-
configuredFlow,
|
|
13336
|
-
effectiveFlow: oauthFlow,
|
|
13337
|
-
isWebView,
|
|
13338
|
-
hasNativeBridge: !!nativeBridge,
|
|
13339
|
-
currentOrigin: window.location.origin,
|
|
13340
|
-
currentHref: window.location.href,
|
|
13341
|
-
configGoogleClientId: config?.googleClientId,
|
|
13342
|
-
usingDefaultClientId: !config?.googleClientId,
|
|
13343
|
-
});
|
|
13344
13079
|
log.log('Google Auth initiated:', {
|
|
13345
|
-
googleClientId,
|
|
13346
13080
|
configuredFlow,
|
|
13347
13081
|
effectiveFlow: oauthFlow,
|
|
13348
13082
|
isWebView,
|
|
13349
13083
|
hasNativeBridge: !!nativeBridge,
|
|
13350
|
-
currentOrigin: window.location.origin,
|
|
13351
|
-
currentHref: window.location.href,
|
|
13352
|
-
configGoogleClientId: config?.googleClientId,
|
|
13353
|
-
usingDefaultClientId: !config?.googleClientId,
|
|
13354
13084
|
});
|
|
13355
13085
|
setLoading(true);
|
|
13356
13086
|
setError(undefined);
|
|
13357
13087
|
try {
|
|
13358
|
-
// Priority 1: Use native bridge if available (for WebView environments)
|
|
13359
13088
|
if (nativeBridge) {
|
|
13360
|
-
console.log(`${LOG_PREFIX} 🌉 NATIVE BRIDGE PATH - Using native bridge for Google Sign-In`);
|
|
13361
13089
|
log.log('Using native bridge for Google Sign-In');
|
|
13362
13090
|
const callbackId = `google_auth_${Date.now()}`;
|
|
13363
|
-
console.log(`${LOG_PREFIX} 🔑 Generated callbackId:`, callbackId);
|
|
13364
|
-
// Set up callback for native response
|
|
13365
|
-
console.log(`${LOG_PREFIX} 📡 Registering window.smartlinksNativeCallback...`);
|
|
13366
13091
|
window.smartlinksNativeCallback = async (result) => {
|
|
13367
|
-
console.log(`${LOG_PREFIX} 📨 smartlinksNativeCallback INVOKED with:`, {
|
|
13368
|
-
callbackId: result.callbackId,
|
|
13369
|
-
success: result.success,
|
|
13370
|
-
hasIdToken: !!result.idToken,
|
|
13371
|
-
idTokenLength: result.idToken?.length,
|
|
13372
|
-
email: result.email,
|
|
13373
|
-
name: result.name,
|
|
13374
|
-
error: result.error,
|
|
13375
|
-
errorCode: result.errorCode,
|
|
13376
|
-
});
|
|
13377
13092
|
// Ignore stale callbacks
|
|
13378
13093
|
if (result.callbackId !== callbackId) {
|
|
13379
|
-
console.log(`${LOG_PREFIX} ⚠️ Ignoring stale callback. Expected:`, callbackId, 'Got:', result.callbackId);
|
|
13380
13094
|
log.log('Ignoring stale native callback:', result.callbackId);
|
|
13381
13095
|
return;
|
|
13382
13096
|
}
|
|
13383
|
-
console.log(`${LOG_PREFIX} ✅ Callback ID matches, processing result...`);
|
|
13384
13097
|
log.log('Native callback received:', {
|
|
13385
13098
|
success: result.success,
|
|
13386
13099
|
hasIdToken: !!result.idToken,
|
|
13387
13100
|
email: result.email,
|
|
13388
13101
|
error: result.error,
|
|
13389
|
-
errorCode: result.errorCode,
|
|
13390
13102
|
});
|
|
13391
13103
|
try {
|
|
13392
13104
|
if (result.success && result.idToken) {
|
|
13393
|
-
console.log(`${LOG_PREFIX} 🔐 Success! Calling api.loginWithGoogle with idToken...`);
|
|
13394
|
-
// Process through existing Google auth flow
|
|
13395
13105
|
const authResponse = await api.loginWithGoogle(result.idToken);
|
|
13396
|
-
console.log(`${LOG_PREFIX} 📦 api.loginWithGoogle response:`, {
|
|
13397
|
-
hasToken: !!authResponse.token,
|
|
13398
|
-
hasUser: !!authResponse.user,
|
|
13399
|
-
isNewUser: authResponse.isNewUser,
|
|
13400
|
-
});
|
|
13401
13106
|
if (authResponse.token) {
|
|
13402
|
-
console.log(`${LOG_PREFIX} 🎉 Login successful! Calling auth.login and onAuthSuccess...`);
|
|
13403
13107
|
await auth.login(authResponse.token, authResponse.user, authResponse.accountData, authResponse.isNewUser, getExpirationFromResponse(authResponse));
|
|
13404
13108
|
setAuthSuccess(true);
|
|
13405
13109
|
setSuccessMessage('Google login successful!');
|
|
13406
13110
|
onAuthSuccess(authResponse.token, authResponse.user, authResponse.accountData);
|
|
13407
13111
|
}
|
|
13408
13112
|
else {
|
|
13409
|
-
console.log(`${LOG_PREFIX} ❌ No token in authResponse`);
|
|
13410
13113
|
throw new Error('Authentication failed - no token received');
|
|
13411
13114
|
}
|
|
13412
13115
|
}
|
|
13413
13116
|
else {
|
|
13414
|
-
// Handle error from native
|
|
13415
|
-
console.log(`${LOG_PREFIX} ❌ Native returned error:`, result.error, result.errorCode);
|
|
13416
13117
|
setError(getFriendlyErrorMessage(result.error || 'Google Sign-In failed'));
|
|
13417
13118
|
onAuthError?.(new Error(result.error || 'Google Sign-In failed'));
|
|
13418
13119
|
}
|
|
13419
13120
|
}
|
|
13420
13121
|
catch (err) {
|
|
13421
|
-
console.log(`${LOG_PREFIX} 💥 Exception in callback handler:`, err);
|
|
13422
13122
|
setError(getFriendlyErrorMessage(err));
|
|
13423
13123
|
onAuthError?.(err instanceof Error ? err : new Error(getFriendlyErrorMessage(err)));
|
|
13424
13124
|
}
|
|
13425
13125
|
finally {
|
|
13426
|
-
console.log(`${LOG_PREFIX} 🏁 Callback processing complete, setting loading=false`);
|
|
13427
13126
|
setLoading(false);
|
|
13428
13127
|
}
|
|
13429
13128
|
};
|
|
13430
|
-
console.log(`${LOG_PREFIX} ✅ window.smartlinksNativeCallback registered`);
|
|
13431
|
-
// Invoke native sign-in
|
|
13432
|
-
// Pass comprehensive payload for Android to configure Google Sign-In correctly
|
|
13433
13129
|
const payloadObj = {
|
|
13434
13130
|
type: 'GOOGLE_SIGN_IN',
|
|
13435
|
-
clientId,
|
|
13436
|
-
googleClientId,
|
|
13437
|
-
serverClientId: googleClientId,
|
|
13131
|
+
clientId,
|
|
13132
|
+
googleClientId,
|
|
13133
|
+
serverClientId: googleClientId,
|
|
13438
13134
|
callbackId,
|
|
13439
|
-
|
|
13440
|
-
|
|
13441
|
-
|
|
13442
|
-
requestServerAuthCode: false, // We don't need server auth code
|
|
13135
|
+
scopes: ['email', 'profile'],
|
|
13136
|
+
requestIdToken: true,
|
|
13137
|
+
requestServerAuthCode: false,
|
|
13443
13138
|
};
|
|
13444
13139
|
const payload = JSON.stringify(payloadObj);
|
|
13445
|
-
|
|
13446
|
-
console.log(`${LOG_PREFIX} 📤 Payload JSON string:`, payload);
|
|
13447
|
-
log.log('Invoking native signInWithGoogle with payload:', payloadObj);
|
|
13448
|
-
// 🔍 DEBUG: Re-validate bridge before invocation
|
|
13449
|
-
console.log(`${LOG_PREFIX} 🔍 Re-validating bridge before invocation...`);
|
|
13450
|
-
console.log(`${LOG_PREFIX} 🔍 window.Android:`, window.Android);
|
|
13451
|
-
console.log(`${LOG_PREFIX} 🔍 window.Android.signInWithGoogle:`, window.Android?.signInWithGoogle);
|
|
13452
|
-
console.log(`${LOG_PREFIX} 🔍 typeof signInWithGoogle:`, typeof window.Android?.signInWithGoogle);
|
|
13453
|
-
console.log(`${LOG_PREFIX} 🔍 nativeBridge === window.Android:`, nativeBridge === window.Android);
|
|
13454
|
-
console.log(`${LOG_PREFIX} 🔍 nativeBridge.signInWithGoogle:`, nativeBridge.signInWithGoogle);
|
|
13455
|
-
console.log(`${LOG_PREFIX} 🔍 typeof nativeBridge.signInWithGoogle:`, typeof nativeBridge.signInWithGoogle);
|
|
13456
|
-
// 🔍 DEBUG: Bridge method comparison
|
|
13457
|
-
console.log(`${LOG_PREFIX} 🔍 Bridge method comparison:`);
|
|
13458
|
-
const android = window.Android;
|
|
13459
|
-
if (android) {
|
|
13460
|
-
console.log(`${LOG_PREFIX} - signInWithGoogle: ${typeof android.signInWithGoogle} | descriptor:`, Object.getOwnPropertyDescriptor(android, 'signInWithGoogle'));
|
|
13461
|
-
console.log(`${LOG_PREFIX} - signOutGoogle: ${typeof android.signOutGoogle} | descriptor:`, Object.getOwnPropertyDescriptor(android, 'signOutGoogle'));
|
|
13462
|
-
console.log(`${LOG_PREFIX} - checkGoogleSignIn: ${typeof android.checkGoogleSignIn} | descriptor:`, Object.getOwnPropertyDescriptor(android, 'checkGoogleSignIn'));
|
|
13463
|
-
console.log(`${LOG_PREFIX} - All keys:`, Object.keys(android));
|
|
13464
|
-
try {
|
|
13465
|
-
console.log(`${LOG_PREFIX} - Prototype:`, Object.getPrototypeOf(android));
|
|
13466
|
-
}
|
|
13467
|
-
catch (e) {
|
|
13468
|
-
console.log(`${LOG_PREFIX} - Prototype: (error getting prototype)`, e);
|
|
13469
|
-
}
|
|
13470
|
-
}
|
|
13471
|
-
// 🔍 DEBUG: Wrap invocation in try/catch to capture exact exception
|
|
13140
|
+
log.log('Invoking native signInWithGoogle');
|
|
13472
13141
|
try {
|
|
13473
|
-
console.log(`${LOG_PREFIX} 📤 About to invoke nativeBridge.signInWithGoogle...`);
|
|
13474
13142
|
nativeBridge.signInWithGoogle(payload);
|
|
13475
|
-
console.log(`${LOG_PREFIX} ✅ nativeBridge.signInWithGoogle called successfully`);
|
|
13476
|
-
console.log(`${LOG_PREFIX} ⏳ Waiting for native callback...`);
|
|
13477
|
-
console.log(`${LOG_PREFIX} 💡 Android should use serverClientId for GoogleSignInOptions.Builder().requestIdToken(serverClientId)`);
|
|
13478
13143
|
}
|
|
13479
13144
|
catch (invokeError) {
|
|
13480
|
-
console.error(`${LOG_PREFIX}
|
|
13481
|
-
|
|
13482
|
-
console.error(`${LOG_PREFIX} 💥 Error constructor:`, invokeError?.constructor?.name);
|
|
13483
|
-
console.error(`${LOG_PREFIX} 💥 Error message:`, invokeError?.message);
|
|
13484
|
-
console.error(`${LOG_PREFIX} 💥 Error stack:`, invokeError?.stack);
|
|
13485
|
-
throw invokeError; // Re-throw so it propagates normally
|
|
13145
|
+
console.error(`${LOG_PREFIX} Exception invoking signInWithGoogle:`, invokeError);
|
|
13146
|
+
throw invokeError;
|
|
13486
13147
|
}
|
|
13487
13148
|
// Don't set loading to false - waiting for native callback
|
|
13488
13149
|
return;
|
|
13489
13150
|
}
|
|
13490
13151
|
// Priority 2: If WebView but no native bridge, show helpful error
|
|
13491
13152
|
if (isWebView) {
|
|
13492
|
-
console.log(`${LOG_PREFIX} ⚠️ WebView detected but NO native bridge - showing error`);
|
|
13493
13153
|
log.log('WebView detected but no native bridge available');
|
|
13494
13154
|
setError('Google Sign-In is not available in this app. Please use email or phone login instead.');
|
|
13495
13155
|
setLoading(false);
|
|
13496
13156
|
return;
|
|
13497
13157
|
}
|
|
13498
|
-
|
|
13158
|
+
log.log('Using web-based OAuth flow:', oauthFlow);
|
|
13499
13159
|
// Priority 3: Web-based flows (redirect, popup, oneTap)
|
|
13500
13160
|
// Dynamically load Google Identity Services if not already loaded
|
|
13501
13161
|
await loadGoogleIdentityServices();
|
|
@@ -13724,21 +13384,13 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
13724
13384
|
setLoading(true);
|
|
13725
13385
|
setError(undefined);
|
|
13726
13386
|
const effectiveRedirectUrl = getRedirectUrl();
|
|
13727
|
-
console.log(`${LOG_PREFIX} 🔑 handlePasswordReset called`, {
|
|
13728
|
-
email: emailOrPassword,
|
|
13729
|
-
hasResetToken: !!resetToken,
|
|
13730
|
-
hasConfirmPassword: !!confirmPassword,
|
|
13731
|
-
effectiveRedirectUrl,
|
|
13732
|
-
redirectUrlProp: redirectUrl
|
|
13733
|
-
});
|
|
13734
13387
|
try {
|
|
13735
13388
|
if (resetToken && confirmPassword) {
|
|
13736
13389
|
// Complete password reset with token
|
|
13737
|
-
|
|
13390
|
+
await api.completePasswordReset(resetToken, emailOrPassword);
|
|
13738
13391
|
await api.completePasswordReset(resetToken, emailOrPassword);
|
|
13739
13392
|
// Auto-login with the new password if we have the email
|
|
13740
13393
|
if (resetEmail) {
|
|
13741
|
-
console.log(`${LOG_PREFIX} 🔑 Auto-signing in after password reset`);
|
|
13742
13394
|
try {
|
|
13743
13395
|
const loginResponse = await api.login(resetEmail, emailOrPassword);
|
|
13744
13396
|
if (loginResponse.token) {
|
|
@@ -13746,15 +13398,13 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
13746
13398
|
setAuthSuccess(true);
|
|
13747
13399
|
setSuccessMessage('Password reset successful! You are now signed in.');
|
|
13748
13400
|
onAuthSuccess(loginResponse.token, loginResponse.user, loginResponse.accountData);
|
|
13749
|
-
// Clear reset state
|
|
13750
13401
|
setResetToken(undefined);
|
|
13751
13402
|
setResetEmail(undefined);
|
|
13752
|
-
return;
|
|
13403
|
+
return;
|
|
13753
13404
|
}
|
|
13754
13405
|
}
|
|
13755
13406
|
catch (loginErr) {
|
|
13756
13407
|
// Auto-login failed, fall back to showing success message
|
|
13757
|
-
console.log(`${LOG_PREFIX} ⚠️ Auto-login after reset failed, showing manual login prompt`, loginErr);
|
|
13758
13408
|
}
|
|
13759
13409
|
}
|
|
13760
13410
|
// Fallback: show success but require manual login
|
|
@@ -13765,7 +13415,6 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
13765
13415
|
}
|
|
13766
13416
|
else {
|
|
13767
13417
|
// Request password reset email
|
|
13768
|
-
console.log(`${LOG_PREFIX} 🔑 Requesting password reset email with redirectUrl:`, effectiveRedirectUrl);
|
|
13769
13418
|
const result = await api.requestPasswordReset(emailOrPassword, effectiveRedirectUrl);
|
|
13770
13419
|
setResetSuccess(true);
|
|
13771
13420
|
// Use backend message if available
|