@proveanything/smartlinks-auth-ui 0.5.14 → 0.5.16

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/index.js CHANGED
@@ -13108,8 +13108,48 @@ const useAuth = () => {
13108
13108
  };
13109
13109
 
13110
13110
  // VERSION: Update this when making changes to help identify which version is running
13111
- const AUTH_UI_VERSION = '46';
13111
+ const AUTH_UI_VERSION = '47';
13112
13112
  const LOG_PREFIX = `[SmartlinksAuthUI:v${AUTH_UI_VERSION}]`;
13113
+ const PERMANENT_WHATSAPP_EXCHANGE_ERROR_CODES = new Set([
13114
+ 'TOKEN_ALREADY_USED',
13115
+ 'SESSION_ALREADY_USED',
13116
+ 'WHATSAPP_SESSION_ALREADY_USED',
13117
+ 'INVALID_TOKEN',
13118
+ 'TOKEN_EXPIRED',
13119
+ 'INVALID_SESSION',
13120
+ 'SESSION_EXPIRED',
13121
+ 'SESSION_NOT_FOUND',
13122
+ ]);
13123
+ const getExchangeErrorCode = (error) => {
13124
+ if (!error || typeof error !== 'object')
13125
+ return undefined;
13126
+ const err = error;
13127
+ return err.errorCode || err.details?.errorCode || err.details?.error || err.response?.data?.errorCode || err.response?.data?.error;
13128
+ };
13129
+ const getExchangeErrorStatus = (error) => {
13130
+ if (!error || typeof error !== 'object')
13131
+ return undefined;
13132
+ const err = error;
13133
+ return err.statusCode || err.status || err.response?.status;
13134
+ };
13135
+ const getExchangeErrorMessage = (error) => {
13136
+ if (error instanceof Error)
13137
+ return error.message || '';
13138
+ if (!error || typeof error !== 'object')
13139
+ return '';
13140
+ const err = error;
13141
+ return err.message || err.details?.message || err.response?.data?.message || '';
13142
+ };
13143
+ const isPermanentWhatsAppExchangeError = (error) => {
13144
+ const errorCode = getExchangeErrorCode(error)?.toUpperCase();
13145
+ if (errorCode && PERMANENT_WHATSAPP_EXCHANGE_ERROR_CODES.has(errorCode)) {
13146
+ return true;
13147
+ }
13148
+ const status = getExchangeErrorStatus(error);
13149
+ const message = getExchangeErrorMessage(error).toLowerCase();
13150
+ const looksPermanentMessage = /(already used|already been used|session.*used|expired|invalid|not found|consumed)/i.test(message);
13151
+ return looksPermanentMessage && [400, 401, 404, 409, 410].includes(status ?? -1);
13152
+ };
13113
13153
  // Normalize malformed query strings where a second '?' is used instead of '&'.
13114
13154
  // Some host platforms append "?mode=...&token=..." to a URL that already has a "?pageId=...",
13115
13155
  // resulting in "?pageId=xxx?mode=resetPassword&token=yyy". Convert any extra '?' to '&'
@@ -13132,7 +13172,10 @@ const appendWhatsAppResumeParams = (url, token) => {
13132
13172
  else {
13133
13173
  nextUrl.searchParams.delete('token');
13134
13174
  }
13135
- return nextUrl.toString();
13175
+ const serializedUrl = nextUrl.toString();
13176
+ return token === '{{token}}'
13177
+ ? serializedUrl.replace(encodeURIComponent(token), token)
13178
+ : serializedUrl;
13136
13179
  }
13137
13180
  catch {
13138
13181
  return url;
@@ -13202,7 +13245,7 @@ const interpolateReply = (input, vars) => {
13202
13245
  .replace(/\{\{\s*phoneNumber\s*\}\}/gi, vars.phoneNumber?.trim() || '')
13203
13246
  .replace(/\{\{\s*returnUrl\s*\}\}/gi, vars.returnUrl?.trim() || '')
13204
13247
  .replace(/\{\{\s*clientId\s*\}\}/gi, vars.clientId?.trim() || '')
13205
- .replace(/\{\{\s*token\s*\}\}/gi, vars.token?.trim() || '');
13248
+ .replace(/\{\{\s*token\s*\}\}/gi, vars.token === undefined ? '{{token}}' : vars.token.trim());
13206
13249
  };
13207
13250
  const buildWhatsAppReply = (cfg, vars) => {
13208
13251
  if (!cfg)
@@ -14751,10 +14794,11 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
14751
14794
  // Resolve reply config: per-call prop wins, otherwise fall back to AuthKit default.
14752
14795
  const replyConfig = whatsappReply ?? config?.whatsappReply;
14753
14796
  const effectiveRedirectUrl = getRedirectUrl();
14797
+ const resumableRedirectUrl = appendWhatsAppResumeParams(effectiveRedirectUrl, '{{token}}');
14754
14798
  const reply = buildWhatsAppReply(replyConfig, {
14755
14799
  name: displayName,
14756
14800
  clientName,
14757
- returnUrl: effectiveRedirectUrl,
14801
+ returnUrl: resumableRedirectUrl,
14758
14802
  clientId,
14759
14803
  });
14760
14804
  // Resolve outbound prefill message: per-call prop wins, then AuthKit default.
@@ -14764,7 +14808,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
14764
14808
  // formed session.user — no follow-up updateProfile call needed.
14765
14809
  const trimmedName = displayName?.trim() || undefined;
14766
14810
  const contactData = trimmedName ? { name: trimmedName } : undefined;
14767
- const result = await api.sendWhatsApp(effectiveRedirectUrl, undefined, reply, prefillMessage, contactData);
14811
+ const result = await api.sendWhatsApp(resumableRedirectUrl, undefined, reply, prefillMessage, contactData);
14768
14812
  whatsappSendRef.current = {
14769
14813
  token: result.token,
14770
14814
  sessionKey: result.sessionKey,
@@ -14807,6 +14851,10 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
14807
14851
  try {
14808
14852
  await updatePendingWhatsAppSession({ exchangeStartedAt: Date.now() });
14809
14853
  const session = await api.exchangeWhatsAppSession(send.token, send.sessionKey);
14854
+ const resultErrorMessage = getActionResultErrorMessage(session);
14855
+ if (resultErrorMessage) {
14856
+ throw Object.assign(new Error(resultErrorMessage), session);
14857
+ }
14810
14858
  if (session?.token && session.user) {
14811
14859
  await auth.login(session.token, session.user, session.accountData, true, getExpirationFromResponse(session));
14812
14860
  if (!proxyMode) {
@@ -14819,6 +14867,15 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
14819
14867
  }
14820
14868
  }
14821
14869
  catch (err) {
14870
+ if (isPermanentWhatsAppExchangeError(err)) {
14871
+ log.warn('WhatsApp session exchange failed permanently; clearing stale pending session:', err);
14872
+ setAuthSuccess(false);
14873
+ setSuccessMessage(undefined);
14874
+ setError('This WhatsApp sign-in session has already been used or expired. Please start again.');
14875
+ setRestoredWhatsAppSend(null);
14876
+ await clearPendingWhatsAppSession();
14877
+ return true;
14878
+ }
14822
14879
  const latestPending = await loadPendingWhatsAppSession();
14823
14880
  if (!latestPending) {
14824
14881
  return true;