@proveanything/smartlinks-auth-ui 0.5.15 → 0.5.17
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.
|
@@ -1 +1 @@
|
|
|
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;
|
|
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;AA2V7I,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,CAutE5D,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,CAoqCpD,CAAC;AAEF,eAAO,MAAM,OAAO,QAAO,gBAM1B,CAAC"}
|
package/dist/index.esm.js
CHANGED
|
@@ -12134,10 +12134,34 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
|
|
|
12134
12134
|
}
|
|
12135
12135
|
});
|
|
12136
12136
|
}, []);
|
|
12137
|
-
// Sync contact to Smartlinks (non-blocking)
|
|
12137
|
+
// Sync contact to Smartlinks (non-blocking).
|
|
12138
|
+
// Strategy: call publicGetMine() first — the "me" endpoint tells us if a
|
|
12139
|
+
// contact already exists for this (org, userId) pair. If it does, we skip
|
|
12140
|
+
// the upsert entirely (the backend's "upsert" currently calls
|
|
12141
|
+
// prisma.contact.create() unconditionally and 400s on the unique
|
|
12142
|
+
// constraint, so we must not call it for returning users). Only when no
|
|
12143
|
+
// contact exists do we create one via publicUpsert.
|
|
12138
12144
|
const syncContact = useCallback(async (authUser, customFields) => {
|
|
12139
12145
|
if (!collectionId || !shouldSyncContacts)
|
|
12140
12146
|
return null;
|
|
12147
|
+
// 1. Look up existing contact via the "me" endpoint.
|
|
12148
|
+
try {
|
|
12149
|
+
const myContactResponse = await smartlinks.contact.publicGetMine(collectionId);
|
|
12150
|
+
const existing = myContactResponse?.contact;
|
|
12151
|
+
if (existing?.contactId) {
|
|
12152
|
+
if (!proxyMode) {
|
|
12153
|
+
await tokenStorage.saveContactId(existing.contactId);
|
|
12154
|
+
}
|
|
12155
|
+
setContact(existing);
|
|
12156
|
+
setContactId(existing.contactId);
|
|
12157
|
+
notifyAuthStateChange('CONTACT_SYNCED', authUser, token, accountData, accountInfo, isVerified, existing, existing.contactId);
|
|
12158
|
+
return existing.contactId;
|
|
12159
|
+
}
|
|
12160
|
+
}
|
|
12161
|
+
catch (lookupErr) {
|
|
12162
|
+
// Non-fatal — fall through to create.
|
|
12163
|
+
}
|
|
12164
|
+
// 2. No existing contact — create one.
|
|
12141
12165
|
try {
|
|
12142
12166
|
const result = await smartlinks.contact.publicUpsert(collectionId, {
|
|
12143
12167
|
userId: authUser.uid,
|
|
@@ -13088,8 +13112,48 @@ const useAuth = () => {
|
|
|
13088
13112
|
};
|
|
13089
13113
|
|
|
13090
13114
|
// VERSION: Update this when making changes to help identify which version is running
|
|
13091
|
-
const AUTH_UI_VERSION = '
|
|
13115
|
+
const AUTH_UI_VERSION = '47';
|
|
13092
13116
|
const LOG_PREFIX = `[SmartlinksAuthUI:v${AUTH_UI_VERSION}]`;
|
|
13117
|
+
const PERMANENT_WHATSAPP_EXCHANGE_ERROR_CODES = new Set([
|
|
13118
|
+
'TOKEN_ALREADY_USED',
|
|
13119
|
+
'SESSION_ALREADY_USED',
|
|
13120
|
+
'WHATSAPP_SESSION_ALREADY_USED',
|
|
13121
|
+
'INVALID_TOKEN',
|
|
13122
|
+
'TOKEN_EXPIRED',
|
|
13123
|
+
'INVALID_SESSION',
|
|
13124
|
+
'SESSION_EXPIRED',
|
|
13125
|
+
'SESSION_NOT_FOUND',
|
|
13126
|
+
]);
|
|
13127
|
+
const getExchangeErrorCode = (error) => {
|
|
13128
|
+
if (!error || typeof error !== 'object')
|
|
13129
|
+
return undefined;
|
|
13130
|
+
const err = error;
|
|
13131
|
+
return err.errorCode || err.details?.errorCode || err.details?.error || err.response?.data?.errorCode || err.response?.data?.error;
|
|
13132
|
+
};
|
|
13133
|
+
const getExchangeErrorStatus = (error) => {
|
|
13134
|
+
if (!error || typeof error !== 'object')
|
|
13135
|
+
return undefined;
|
|
13136
|
+
const err = error;
|
|
13137
|
+
return err.statusCode || err.status || err.response?.status;
|
|
13138
|
+
};
|
|
13139
|
+
const getExchangeErrorMessage = (error) => {
|
|
13140
|
+
if (error instanceof Error)
|
|
13141
|
+
return error.message || '';
|
|
13142
|
+
if (!error || typeof error !== 'object')
|
|
13143
|
+
return '';
|
|
13144
|
+
const err = error;
|
|
13145
|
+
return err.message || err.details?.message || err.response?.data?.message || '';
|
|
13146
|
+
};
|
|
13147
|
+
const isPermanentWhatsAppExchangeError = (error) => {
|
|
13148
|
+
const errorCode = getExchangeErrorCode(error)?.toUpperCase();
|
|
13149
|
+
if (errorCode && PERMANENT_WHATSAPP_EXCHANGE_ERROR_CODES.has(errorCode)) {
|
|
13150
|
+
return true;
|
|
13151
|
+
}
|
|
13152
|
+
const status = getExchangeErrorStatus(error);
|
|
13153
|
+
const message = getExchangeErrorMessage(error).toLowerCase();
|
|
13154
|
+
const looksPermanentMessage = /(already used|already been used|session.*used|expired|invalid|not found|consumed)/i.test(message);
|
|
13155
|
+
return looksPermanentMessage && [400, 401, 404, 409, 410].includes(status ?? -1);
|
|
13156
|
+
};
|
|
13093
13157
|
// Normalize malformed query strings where a second '?' is used instead of '&'.
|
|
13094
13158
|
// Some host platforms append "?mode=...&token=..." to a URL that already has a "?pageId=...",
|
|
13095
13159
|
// resulting in "?pageId=xxx?mode=resetPassword&token=yyy". Convert any extra '?' to '&'
|
|
@@ -14731,18 +14795,40 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
14731
14795
|
try {
|
|
14732
14796
|
// No ensureAccount and no phone number — the backend creates/links the
|
|
14733
14797
|
// account from the inbound WhatsApp webhook (sender's number is the proof).
|
|
14734
|
-
// Resolve reply config: per-call prop wins,
|
|
14735
|
-
|
|
14798
|
+
// Resolve reply config: per-call prop wins, then AuthKit config, then a
|
|
14799
|
+
// sensible built-in default so the post-verification WhatsApp reply ALWAYS
|
|
14800
|
+
// carries a "Continue" CTA back to the app with the resume token in the URL.
|
|
14801
|
+
// Without this, a user verifying in WhatsApp on one device/browser can't
|
|
14802
|
+
// resume the session by tapping back into our app from the chat thread.
|
|
14736
14803
|
const effectiveRedirectUrl = getRedirectUrl();
|
|
14737
14804
|
const resumableRedirectUrl = appendWhatsAppResumeParams(effectiveRedirectUrl, '{{token}}');
|
|
14805
|
+
const friendlyClientName = (clientName && clientName.trim()) || 'this app';
|
|
14806
|
+
const defaultReplyConfig = {
|
|
14807
|
+
enabled: true,
|
|
14808
|
+
text: `You're verified! Tap below to continue signing in to ${friendlyClientName}.`,
|
|
14809
|
+
cta: {
|
|
14810
|
+
body: `You're verified! Tap below to continue signing in to ${friendlyClientName}.`,
|
|
14811
|
+
buttonLabel: 'Continue',
|
|
14812
|
+
buttonUrl: '{{returnUrl}}',
|
|
14813
|
+
},
|
|
14814
|
+
};
|
|
14815
|
+
const replyConfig = whatsappReply ??
|
|
14816
|
+
config?.whatsappReply ??
|
|
14817
|
+
defaultReplyConfig;
|
|
14738
14818
|
const reply = buildWhatsAppReply(replyConfig, {
|
|
14739
14819
|
name: displayName,
|
|
14740
14820
|
clientName,
|
|
14741
14821
|
returnUrl: resumableRedirectUrl,
|
|
14742
14822
|
clientId,
|
|
14743
14823
|
});
|
|
14744
|
-
// Resolve outbound prefill message: per-call prop wins, then AuthKit
|
|
14745
|
-
|
|
14824
|
+
// Resolve outbound prefill message: per-call prop wins, then AuthKit config,
|
|
14825
|
+
// then a sensible built-in default so the wa.me body is never blank and always
|
|
14826
|
+
// carries the verification token (the SDK appends {{token}} if missing, but we
|
|
14827
|
+
// also want friendly human context for the recipient).
|
|
14828
|
+
const defaultPrefill = `Hi! I'd like to sign in to ${friendlyClientName}. Code: {{token}}`;
|
|
14829
|
+
const prefillMessage = whatsappPrefillMessage ??
|
|
14830
|
+
config?.whatsappPrefillMessage ??
|
|
14831
|
+
defaultPrefill;
|
|
14746
14832
|
// Pass collected signup details as contactData so the backend creates the
|
|
14747
14833
|
// contact with name/customFields already attached and returns a fully
|
|
14748
14834
|
// formed session.user — no follow-up updateProfile call needed.
|
|
@@ -14791,6 +14877,10 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
14791
14877
|
try {
|
|
14792
14878
|
await updatePendingWhatsAppSession({ exchangeStartedAt: Date.now() });
|
|
14793
14879
|
const session = await api.exchangeWhatsAppSession(send.token, send.sessionKey);
|
|
14880
|
+
const resultErrorMessage = getActionResultErrorMessage(session);
|
|
14881
|
+
if (resultErrorMessage) {
|
|
14882
|
+
throw Object.assign(new Error(resultErrorMessage), session);
|
|
14883
|
+
}
|
|
14794
14884
|
if (session?.token && session.user) {
|
|
14795
14885
|
await auth.login(session.token, session.user, session.accountData, true, getExpirationFromResponse(session));
|
|
14796
14886
|
if (!proxyMode) {
|
|
@@ -14803,6 +14893,15 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
14803
14893
|
}
|
|
14804
14894
|
}
|
|
14805
14895
|
catch (err) {
|
|
14896
|
+
if (isPermanentWhatsAppExchangeError(err)) {
|
|
14897
|
+
log.warn('WhatsApp session exchange failed permanently; clearing stale pending session:', err);
|
|
14898
|
+
setAuthSuccess(false);
|
|
14899
|
+
setSuccessMessage(undefined);
|
|
14900
|
+
setError('This WhatsApp sign-in session has already been used or expired. Please start again.');
|
|
14901
|
+
setRestoredWhatsAppSend(null);
|
|
14902
|
+
await clearPendingWhatsAppSession();
|
|
14903
|
+
return true;
|
|
14904
|
+
}
|
|
14806
14905
|
const latestPending = await loadPendingWhatsAppSession();
|
|
14807
14906
|
if (!latestPending) {
|
|
14808
14907
|
return true;
|
|
@@ -14851,9 +14950,12 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
14851
14950
|
color: config?.branding?.inheritHostStyles
|
|
14852
14951
|
? 'hsl(var(--foreground, 215 25% 15%))'
|
|
14853
14952
|
: (resolvedTheme === 'dark' ? '#f1f5f9' : 'inherit')
|
|
14854
|
-
}, children: successMessage?.includes('
|
|
14855
|
-
successMessage?.includes('
|
|
14856
|
-
|
|
14953
|
+
}, children: successMessage?.includes('WhatsApp') ? 'WhatsApp Verified!' :
|
|
14954
|
+
successMessage?.includes('Phone') ? 'Phone Verified!' :
|
|
14955
|
+
successMessage?.includes('Email verified') || successMessage?.includes('email verified') ? 'Email Verified!' :
|
|
14956
|
+
successMessage?.includes('Magic link') ? 'Check Your Email!' :
|
|
14957
|
+
successMessage?.includes('verified') ? 'Verified!' :
|
|
14958
|
+
mode === 'register' ? 'Account Created!' : 'Login Successful!' }), jsx("p", { style: {
|
|
14857
14959
|
color: config?.branding?.inheritHostStyles
|
|
14858
14960
|
? 'hsl(var(--muted-foreground, 215 15% 45%))'
|
|
14859
14961
|
: (resolvedTheme === 'dark' ? '#94a3b8' : '#6B7280'),
|