@proveanything/smartlinks-auth-ui 0.5.9 → 0.5.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/SmartlinksAuthUI.d.ts.map +1 -1
- package/dist/context/AuthContext.d.ts.map +1 -1
- package/dist/index.esm.js +177 -19
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +179 -21
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SmartlinksAuthUI.d.ts","sourceRoot":"","sources":["../../src/components/SmartlinksAuthUI.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA+C,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"SmartlinksAuthUI.d.ts","sourceRoot":"","sources":["../../src/components/SmartlinksAuthUI.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA+C,MAAM,OAAO,CAAC;AAcpE,OAAO,KAAK,EAAE,qBAAqB,EAAyF,MAAM,UAAU,CAAC;AAiR7I,QAAA,MAAM,mBAAmB,QAAa,OAAO,CAAC,IAAI,CAqBjD,CAAC;AAqDF,OAAO,EAAE,mBAAmB,EAAE,CAAC;AAI/B,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CA2kE5D,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuthContext.d.ts","sourceRoot":"","sources":["../../src/context/AuthContext.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8E,MAAM,OAAO,CAAC;AAOnG,OAAO,KAAK,EAAqC,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAGvG,eAAO,MAAM,WAAW,6CAAyD,CAAC;AAGlF,YAAY,EAAE,gBAAgB,EAAE,CAAC;AAEjC,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,
|
|
1
|
+
{"version":3,"file":"AuthContext.d.ts","sourceRoot":"","sources":["../../src/context/AuthContext.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8E,MAAM,OAAO,CAAC;AAOnG,OAAO,KAAK,EAAqC,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAGvG,eAAO,MAAM,WAAW,6CAAyD,CAAC;AAGlF,YAAY,EAAE,gBAAgB,EAAE,CAAC;AAEjC,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAm+BpD,CAAC;AAEF,eAAO,MAAM,OAAO,QAAO,gBAM1B,CAAC"}
|
package/dist/index.esm.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import React, { useEffect, useState, useMemo, useRef, useCallback, createContext, useContext } from 'react';
|
|
3
3
|
import * as smartlinks from '@proveanything/smartlinks';
|
|
4
|
-
import { SmartlinksApiError, iframe } from '@proveanything/smartlinks';
|
|
5
|
-
import { post, getApiHeaders, isProxyEnabled } from '@proveanything/smartlinks/dist/http';
|
|
4
|
+
import { SmartlinksApiError, post, iframe, getApiHeaders, isProxyEnabled } from '@proveanything/smartlinks';
|
|
6
5
|
|
|
7
6
|
const AuthContainer = ({ children, theme = 'light', className = '', config, minimal = false, }) => {
|
|
8
7
|
// Apply CSS variables for customization
|
|
@@ -3097,24 +3096,25 @@ function isPossiblePhoneNumber(input, options, metadata) {
|
|
|
3097
3096
|
}
|
|
3098
3097
|
}
|
|
3099
3098
|
|
|
3100
|
-
// Old metadata (< 1.0.18) had no "possible length" data.
|
|
3099
|
+
// Old (legacy) metadata (< 1.0.18) had no "possible length" data.
|
|
3100
|
+
// So `isPossibleNumber()` function is supported only for non-legacy metadata.
|
|
3101
3101
|
if (metadata.possibleLengths()) {
|
|
3102
3102
|
return isPossibleNumber(input.phone || input.nationalNumber, input.country, metadata);
|
|
3103
|
-
} else {
|
|
3104
|
-
// There was a bug between `1.7.35` and `1.7.37` where "possible_lengths"
|
|
3105
|
-
// were missing for "non-geographical" numbering plans.
|
|
3106
|
-
// Just assume the number is possible in such cases:
|
|
3107
|
-
// it's unlikely that anyone generated their custom metadata
|
|
3108
|
-
// in that short period of time (one day).
|
|
3109
|
-
// This code can be removed in some future major version update.
|
|
3110
|
-
if (input.countryCallingCode && metadata.isNonGeographicCallingCode(input.countryCallingCode)) {
|
|
3111
|
-
// "Non-geographic entities" did't have `possibleLengths`
|
|
3112
|
-
// due to a bug in metadata generation process.
|
|
3113
|
-
return true;
|
|
3114
|
-
} else {
|
|
3115
|
-
throw new Error('Missing "possibleLengths" in metadata. Perhaps the metadata has been generated before v1.0.18.');
|
|
3116
|
-
}
|
|
3117
3103
|
}
|
|
3104
|
+
|
|
3105
|
+
// There was a bug in versions from `1.7.35` to `1.7.37` of `libphonenumber-js`
|
|
3106
|
+
// where "possible_lengths" property was missing from "non-geographical" numbering plans' metadata.
|
|
3107
|
+
// After that, the bug was noticed and fixed.
|
|
3108
|
+
// So for versions from `1.7.35` to `1.7.37`, just assume that all "non=geotraphical" numbers are possible.
|
|
3109
|
+
// The reason is that the bug was noticed relatively quickly (within a day or so)
|
|
3110
|
+
// and it's unlikely that anyone generated their custom metadata in that short time span.
|
|
3111
|
+
// And if they didn't generate any custom metadata then a follow-up package update would've silently fixed the bug.
|
|
3112
|
+
if (input.countryCallingCode && metadata.isNonGeographicCallingCode(input.countryCallingCode)) {
|
|
3113
|
+
return true;
|
|
3114
|
+
}
|
|
3115
|
+
|
|
3116
|
+
// `isPossibleNumber()` function is not supported.
|
|
3117
|
+
throw new Error('Missing "possibleLengths" in metadata. Perhaps the metadata has been generated before v1.0.18.');
|
|
3118
3118
|
}
|
|
3119
3119
|
function isPossibleNumber(nationalNumber, country, metadata) {
|
|
3120
3120
|
//, isInternational) {
|
|
@@ -12036,6 +12036,7 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
12036
12036
|
const callbacksRef = useRef(new Set());
|
|
12037
12037
|
const initializingRef = useRef(false);
|
|
12038
12038
|
const pendingVerificationRef = useRef(false);
|
|
12039
|
+
const proxyRefreshInFlightRef = useRef(false);
|
|
12039
12040
|
// Stable refs for callbacks to avoid useEffect dependency cycles
|
|
12040
12041
|
const syncContactRef = useRef();
|
|
12041
12042
|
const trackInteractionRef = useRef();
|
|
@@ -12397,6 +12398,85 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
12397
12398
|
window.removeEventListener('message', handleParentMessage);
|
|
12398
12399
|
};
|
|
12399
12400
|
}, [proxyMode, notifyAuthStateChange]);
|
|
12401
|
+
// In proxy mode, mobile auth often returns from WhatsApp/Google via app switching,
|
|
12402
|
+
// so the page stays mounted while the parent session changes underneath it.
|
|
12403
|
+
// Re-check account state on focus/visibility/page restore so login/logout appears
|
|
12404
|
+
// immediately instead of waiting for a full refresh.
|
|
12405
|
+
useEffect(() => {
|
|
12406
|
+
if (!proxyMode)
|
|
12407
|
+
return;
|
|
12408
|
+
const clearProxyState = () => {
|
|
12409
|
+
setUser(null);
|
|
12410
|
+
setToken(null);
|
|
12411
|
+
setAccountData(null);
|
|
12412
|
+
setAccountInfo(null);
|
|
12413
|
+
setContact(null);
|
|
12414
|
+
setContactId(null);
|
|
12415
|
+
setIsVerified(false);
|
|
12416
|
+
pendingVerificationRef.current = false;
|
|
12417
|
+
notifyAuthStateChange('LOGOUT', null, null, null, null, false);
|
|
12418
|
+
};
|
|
12419
|
+
const refreshProxySession = async () => {
|
|
12420
|
+
if (proxyRefreshInFlightRef.current)
|
|
12421
|
+
return;
|
|
12422
|
+
const headers = getApiHeaders();
|
|
12423
|
+
const hasBearer = !!headers['Authorization'];
|
|
12424
|
+
const hasSdkProxy = isProxyEnabled();
|
|
12425
|
+
if (!hasBearer && !hasSdkProxy) {
|
|
12426
|
+
clearProxyState();
|
|
12427
|
+
return;
|
|
12428
|
+
}
|
|
12429
|
+
proxyRefreshInFlightRef.current = true;
|
|
12430
|
+
try {
|
|
12431
|
+
const accountResponse = await smartlinks.auth.getAccount();
|
|
12432
|
+
const accountAny = accountResponse;
|
|
12433
|
+
const hasValidSession = accountAny?.uid && accountAny.uid.length > 0;
|
|
12434
|
+
if (!hasValidSession) {
|
|
12435
|
+
clearProxyState();
|
|
12436
|
+
return;
|
|
12437
|
+
}
|
|
12438
|
+
const userObj = {
|
|
12439
|
+
uid: accountAny.uid,
|
|
12440
|
+
email: accountAny?.email,
|
|
12441
|
+
displayName: accountAny?.displayName || accountAny?.name,
|
|
12442
|
+
phoneNumber: accountAny?.phoneNumber,
|
|
12443
|
+
};
|
|
12444
|
+
setUser(userObj);
|
|
12445
|
+
setAccountData(accountResponse);
|
|
12446
|
+
setAccountInfo(accountResponse);
|
|
12447
|
+
setIsVerified(true);
|
|
12448
|
+
pendingVerificationRef.current = false;
|
|
12449
|
+
notifyAuthStateChange('CROSS_TAB_SYNC', userObj, null, accountResponse, accountResponse, true);
|
|
12450
|
+
}
|
|
12451
|
+
catch (error) {
|
|
12452
|
+
if (!isNetworkError(error)) {
|
|
12453
|
+
clearProxyState();
|
|
12454
|
+
}
|
|
12455
|
+
}
|
|
12456
|
+
finally {
|
|
12457
|
+
proxyRefreshInFlightRef.current = false;
|
|
12458
|
+
}
|
|
12459
|
+
};
|
|
12460
|
+
const handleFocus = () => {
|
|
12461
|
+
refreshProxySession().catch(() => { });
|
|
12462
|
+
};
|
|
12463
|
+
const handlePageShow = () => {
|
|
12464
|
+
refreshProxySession().catch(() => { });
|
|
12465
|
+
};
|
|
12466
|
+
const handleVisibilityChange = () => {
|
|
12467
|
+
if (document.visibilityState === 'visible') {
|
|
12468
|
+
refreshProxySession().catch(() => { });
|
|
12469
|
+
}
|
|
12470
|
+
};
|
|
12471
|
+
window.addEventListener('focus', handleFocus);
|
|
12472
|
+
window.addEventListener('pageshow', handlePageShow);
|
|
12473
|
+
document.addEventListener('visibilitychange', handleVisibilityChange);
|
|
12474
|
+
return () => {
|
|
12475
|
+
window.removeEventListener('focus', handleFocus);
|
|
12476
|
+
window.removeEventListener('pageshow', handlePageShow);
|
|
12477
|
+
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
|
12478
|
+
};
|
|
12479
|
+
}, [proxyMode, notifyAuthStateChange, isNetworkError]);
|
|
12400
12480
|
// Cross-tab synchronization - standalone mode only
|
|
12401
12481
|
useEffect(() => {
|
|
12402
12482
|
if (proxyMode)
|
|
@@ -12878,13 +12958,18 @@ const getActionResultErrorMessage = (result) => {
|
|
|
12878
12958
|
details: candidate.details,
|
|
12879
12959
|
});
|
|
12880
12960
|
};
|
|
12961
|
+
// ----- WhatsApp reply helpers -----
|
|
12962
|
+
const WHATSAPP_PENDING_SESSION_KEY = 'whatsapp_pending_session';
|
|
12881
12963
|
const interpolateReply = (input, vars) => {
|
|
12882
12964
|
if (!input)
|
|
12883
12965
|
return input;
|
|
12884
12966
|
return input
|
|
12885
12967
|
.replace(/\{\{\s*name\s*\}\}/gi, vars.name?.trim() || 'there')
|
|
12886
12968
|
.replace(/\{\{\s*clientName\s*\}\}/gi, vars.clientName?.trim() || '')
|
|
12887
|
-
.replace(/\{\{\s*phoneNumber\s*\}\}/gi, vars.phoneNumber?.trim() || '')
|
|
12969
|
+
.replace(/\{\{\s*phoneNumber\s*\}\}/gi, vars.phoneNumber?.trim() || '')
|
|
12970
|
+
.replace(/\{\{\s*returnUrl\s*\}\}/gi, vars.returnUrl?.trim() || '')
|
|
12971
|
+
.replace(/\{\{\s*clientId\s*\}\}/gi, vars.clientId?.trim() || '')
|
|
12972
|
+
.replace(/\{\{\s*token\s*\}\}/gi, vars.token?.trim() || '');
|
|
12888
12973
|
};
|
|
12889
12974
|
const buildWhatsAppReply = (cfg, vars) => {
|
|
12890
12975
|
if (!cfg)
|
|
@@ -13210,6 +13295,24 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
13210
13295
|
// Get the full current URL including hash routes, strip query params
|
|
13211
13296
|
return window.location.href.split('?')[0];
|
|
13212
13297
|
};
|
|
13298
|
+
const savePendingWhatsAppSession = async (session) => {
|
|
13299
|
+
if (proxyMode)
|
|
13300
|
+
return;
|
|
13301
|
+
const storage = await getStorage();
|
|
13302
|
+
await storage.setItem(WHATSAPP_PENDING_SESSION_KEY, session);
|
|
13303
|
+
};
|
|
13304
|
+
const loadPendingWhatsAppSession = async () => {
|
|
13305
|
+
if (proxyMode)
|
|
13306
|
+
return null;
|
|
13307
|
+
const storage = await getStorage();
|
|
13308
|
+
return storage.getItem(WHATSAPP_PENDING_SESSION_KEY);
|
|
13309
|
+
};
|
|
13310
|
+
const clearPendingWhatsAppSession = async () => {
|
|
13311
|
+
if (proxyMode)
|
|
13312
|
+
return;
|
|
13313
|
+
const storage = await getStorage();
|
|
13314
|
+
await storage.removeItem(WHATSAPP_PENDING_SESSION_KEY);
|
|
13315
|
+
};
|
|
13213
13316
|
// Fetch UI configuration
|
|
13214
13317
|
useEffect(() => {
|
|
13215
13318
|
// Wait for SDK to be ready before fetching config
|
|
@@ -14299,6 +14402,48 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
14299
14402
|
};
|
|
14300
14403
|
// Track the most recent WhatsApp send so we can exchange its sessionKey for a real login session.
|
|
14301
14404
|
const whatsappSendRef = useRef(null);
|
|
14405
|
+
useEffect(() => {
|
|
14406
|
+
if (proxyMode)
|
|
14407
|
+
return;
|
|
14408
|
+
let cancelled = false;
|
|
14409
|
+
const resumePendingWhatsAppSession = async () => {
|
|
14410
|
+
try {
|
|
14411
|
+
const pending = await loadPendingWhatsAppSession();
|
|
14412
|
+
if (!pending || cancelled)
|
|
14413
|
+
return;
|
|
14414
|
+
// Expire stale pending sessions after 30 minutes to avoid resurrecting old attempts.
|
|
14415
|
+
if (Date.now() - pending.createdAt > 30 * 60 * 1000) {
|
|
14416
|
+
await clearPendingWhatsAppSession();
|
|
14417
|
+
return;
|
|
14418
|
+
}
|
|
14419
|
+
whatsappSendRef.current = {
|
|
14420
|
+
token: pending.token,
|
|
14421
|
+
sessionKey: pending.sessionKey,
|
|
14422
|
+
displayName: pending.displayName,
|
|
14423
|
+
};
|
|
14424
|
+
const status = await api.getWhatsAppStatus(pending.token);
|
|
14425
|
+
if (cancelled)
|
|
14426
|
+
return;
|
|
14427
|
+
if (status.verified) {
|
|
14428
|
+
await handleWhatsAppVerified(status);
|
|
14429
|
+
return;
|
|
14430
|
+
}
|
|
14431
|
+
if (status.status === 'pending') {
|
|
14432
|
+
setMode('whatsapp');
|
|
14433
|
+
}
|
|
14434
|
+
else if (status.status === 'failed' || status.status === 'expired' || status.status === 'unknown') {
|
|
14435
|
+
await clearPendingWhatsAppSession();
|
|
14436
|
+
}
|
|
14437
|
+
}
|
|
14438
|
+
catch (err) {
|
|
14439
|
+
log.warn('Failed to resume pending WhatsApp session:', err);
|
|
14440
|
+
}
|
|
14441
|
+
};
|
|
14442
|
+
resumePendingWhatsAppSession();
|
|
14443
|
+
return () => {
|
|
14444
|
+
cancelled = true;
|
|
14445
|
+
};
|
|
14446
|
+
}, [proxyMode, clientId]);
|
|
14302
14447
|
const handleWhatsAppSend = async (displayName) => {
|
|
14303
14448
|
setLoading(true);
|
|
14304
14449
|
setError(undefined);
|
|
@@ -14307,9 +14452,12 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
14307
14452
|
// account from the inbound WhatsApp webhook (sender's number is the proof).
|
|
14308
14453
|
// Resolve reply config: per-call prop wins, otherwise fall back to AuthKit default.
|
|
14309
14454
|
const replyConfig = whatsappReply ?? config?.whatsappReply;
|
|
14455
|
+
const effectiveRedirectUrl = getRedirectUrl();
|
|
14310
14456
|
const reply = buildWhatsAppReply(replyConfig, {
|
|
14311
14457
|
name: displayName,
|
|
14312
14458
|
clientName,
|
|
14459
|
+
returnUrl: effectiveRedirectUrl,
|
|
14460
|
+
clientId,
|
|
14313
14461
|
});
|
|
14314
14462
|
// Resolve outbound prefill message: per-call prop wins, then AuthKit default.
|
|
14315
14463
|
const prefillMessage = whatsappPrefillMessage ?? config?.whatsappPrefillMessage;
|
|
@@ -14318,12 +14466,19 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
14318
14466
|
// formed session.user — no follow-up updateProfile call needed.
|
|
14319
14467
|
const trimmedName = displayName?.trim() || undefined;
|
|
14320
14468
|
const contactData = trimmedName ? { name: trimmedName } : undefined;
|
|
14321
|
-
const result = await api.sendWhatsApp(
|
|
14469
|
+
const result = await api.sendWhatsApp(effectiveRedirectUrl, undefined, reply, prefillMessage, contactData);
|
|
14322
14470
|
whatsappSendRef.current = {
|
|
14323
14471
|
token: result.token,
|
|
14324
14472
|
sessionKey: result.sessionKey,
|
|
14325
14473
|
displayName: trimmedName,
|
|
14326
14474
|
};
|
|
14475
|
+
await savePendingWhatsAppSession({
|
|
14476
|
+
token: result.token,
|
|
14477
|
+
sessionKey: result.sessionKey,
|
|
14478
|
+
displayName: trimmedName,
|
|
14479
|
+
redirectUrl: effectiveRedirectUrl,
|
|
14480
|
+
createdAt: Date.now(),
|
|
14481
|
+
});
|
|
14327
14482
|
return result;
|
|
14328
14483
|
}
|
|
14329
14484
|
catch (err) {
|
|
@@ -14354,12 +14509,15 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
14354
14509
|
if (!proxyMode) {
|
|
14355
14510
|
onAuthSuccess(session.token, session.user, session.accountData);
|
|
14356
14511
|
}
|
|
14512
|
+
await clearPendingWhatsAppSession();
|
|
14513
|
+
return;
|
|
14357
14514
|
}
|
|
14358
14515
|
}
|
|
14359
14516
|
catch (err) {
|
|
14360
14517
|
log.warn('WhatsApp session exchange failed, falling back to redirect-only flow:', err);
|
|
14361
14518
|
}
|
|
14362
14519
|
}
|
|
14520
|
+
await clearPendingWhatsAppSession();
|
|
14363
14521
|
performRedirect(target, 'magic-link');
|
|
14364
14522
|
};
|
|
14365
14523
|
// Show processing state for URL-based auth (verification, magic link, password reset)
|