@proveanything/smartlinks-auth-ui 0.4.4 → 0.4.6

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,KAAuC,MAAM,OAAO,CAAC;AAY5D,OAAO,KAAK,EAAE,qBAAqB,EAAyF,MAAM,UAAU,CAAC;AA8I7I,QAAA,MAAM,mBAAmB,QAAa,OAAO,CAAC,IAAI,CAmCjD,CAAC;AAwEF,OAAO,EAAE,mBAAmB,EAAE,CAAC;AAK/B,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CA8sD5D,CAAC"}
1
+ {"version":3,"file":"SmartlinksAuthUI.d.ts","sourceRoot":"","sources":["../../src/components/SmartlinksAuthUI.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAuC,MAAM,OAAO,CAAC;AAY5D,OAAO,KAAK,EAAE,qBAAqB,EAAyF,MAAM,UAAU,CAAC;AA+L7I,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,CAgyD5D,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"useIframeMessages.d.ts","sourceRoot":"","sources":["../../../src/components/SmartlinksFrame/useIframeMessages.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAQV,UAAU,EACX,MAAM,4BAA4B,CAAC;AAepC,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,UAAU,CAAC;IAEvB,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,SAAS,CAAC,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrI,MAAM,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,IAAI,CAAC;IACtE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AASD;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,iBAAiB,CAAC,EAC7C,OAAO,EAAE,oBAAoB,GAC5B,IAAI,CA8aN"}
1
+ {"version":3,"file":"useIframeMessages.d.ts","sourceRoot":"","sources":["../../../src/components/SmartlinksFrame/useIframeMessages.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAQV,UAAU,EACX,MAAM,4BAA4B,CAAC;AAepC,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,UAAU,CAAC;IAEvB,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,SAAS,CAAC,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrI,MAAM,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,IAAI,CAAC;IACtE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AASD;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,iBAAiB,CAAC,EAC7C,OAAO,EAAE,oBAAoB,GAC5B,IAAI,CAsbN"}
package/dist/index.esm.js CHANGED
@@ -12498,9 +12498,30 @@ function getErrorCode(error) {
12498
12498
  }
12499
12499
  return undefined;
12500
12500
  }
12501
+ /**
12502
+ * Error codes that indicate the user needs to verify their email.
12503
+ */
12504
+ const EMAIL_VERIFICATION_ERROR_CODES = new Set([
12505
+ 'EMAIL_NOT_VERIFIED',
12506
+ 'ACCOUNT_LOCKED',
12507
+ 'EMAIL_VERIFICATION_EXPIRED',
12508
+ ]);
12509
+ /**
12510
+ * Checks if an error requires email verification action from the user.
12511
+ */
12512
+ function requiresEmailVerification(error) {
12513
+ const code = getErrorCode(error);
12514
+ if (code && EMAIL_VERIFICATION_ERROR_CODES.has(code))
12515
+ return true;
12516
+ // Also check the flag from the response body
12517
+ if (error && typeof error === 'object' && 'requiresEmailVerification' in error) {
12518
+ return error.requiresEmailVerification === true;
12519
+ }
12520
+ return false;
12521
+ }
12501
12522
 
12502
12523
  // VERSION: Update this when making changes to help identify which version is running
12503
- const AUTH_UI_VERSION = '44';
12524
+ const AUTH_UI_VERSION = '45';
12504
12525
  const LOG_PREFIX = `[SmartlinksAuthUI:v${AUTH_UI_VERSION}]`;
12505
12526
  // Helper to check for URL auth params synchronously (runs during initialization)
12506
12527
  // This prevents the form from flashing before detecting deep-link flows
@@ -12615,6 +12636,43 @@ const getNativeBridge = () => {
12615
12636
  return native;
12616
12637
  return null;
12617
12638
  };
12639
+ // Helper to register an AuthKit.onAuthResult handler for a specific callbackId
12640
+ // The Android native app calls: window.AuthKit.onAuthResult(resultJson)
12641
+ const registerAuthKitCallback = (callbackId, onResult, timeoutMs = 5000, onTimeout) => {
12642
+ const bridge = window.AuthKit;
12643
+ if (!bridge) {
12644
+ console.warn('[AuthKit] No AuthKit bridge found, cannot register callback');
12645
+ onTimeout?.();
12646
+ return () => { };
12647
+ }
12648
+ // Store the previous handler so we can restore on cleanup
12649
+ const previousHandler = bridge.onAuthResult;
12650
+ const timeout = setTimeout(() => {
12651
+ // Restore previous handler on timeout
12652
+ bridge.onAuthResult = previousHandler;
12653
+ onTimeout?.();
12654
+ }, timeoutMs);
12655
+ bridge.onAuthResult = (resultOrJson) => {
12656
+ const result = typeof resultOrJson === 'string' ? JSON.parse(resultOrJson) : resultOrJson;
12657
+ // Match by callbackId if present, otherwise accept any result
12658
+ if (result.callbackId && result.callbackId !== callbackId) {
12659
+ // Not for us — pass through to previous handler if any
12660
+ if (typeof previousHandler === 'function') {
12661
+ previousHandler(resultOrJson);
12662
+ }
12663
+ return;
12664
+ }
12665
+ clearTimeout(timeout);
12666
+ // Restore previous handler
12667
+ bridge.onAuthResult = previousHandler;
12668
+ onResult(result);
12669
+ };
12670
+ // Return cleanup function
12671
+ return () => {
12672
+ clearTimeout(timeout);
12673
+ bridge.onAuthResult = previousHandler;
12674
+ };
12675
+ };
12618
12676
  // Sign out from Google on the native side (clears cached Google account)
12619
12677
  // This is fire-and-forget with a timeout - gracefully degrades if not supported
12620
12678
  const signOutGoogleNative = async () => {
@@ -12623,21 +12681,7 @@ const signOutGoogleNative = async () => {
12623
12681
  return;
12624
12682
  const callbackId = `google_signout_${Date.now()}`;
12625
12683
  return new Promise((resolve) => {
12626
- const timeout = setTimeout(() => resolve(), 3000);
12627
- // Store original callback to restore later
12628
- const originalCallback = window.smartlinksNativeCallback;
12629
- window.smartlinksNativeCallback = (result) => {
12630
- if (result.callbackId === callbackId) {
12631
- clearTimeout(timeout);
12632
- // Restore original callback
12633
- window.smartlinksNativeCallback = originalCallback;
12634
- resolve();
12635
- }
12636
- else if (originalCallback) {
12637
- // Pass through to original callback for other messages
12638
- originalCallback(result);
12639
- }
12640
- };
12684
+ registerAuthKitCallback(callbackId, () => resolve(), 3000, () => resolve());
12641
12685
  const payload = JSON.stringify({
12642
12686
  type: 'GOOGLE_SIGN_OUT',
12643
12687
  callbackId,
@@ -12651,37 +12695,25 @@ const checkSilentGoogleSignIn = async (clientId, googleClientId) => {
12651
12695
  return null;
12652
12696
  const callbackId = `google_check_${Date.now()}`;
12653
12697
  return new Promise((resolve) => {
12654
- const timeout = setTimeout(() => resolve(null), 5000);
12655
- // Store original callback to restore later
12656
- const originalCallback = window.smartlinksNativeCallback;
12657
- window.smartlinksNativeCallback = (result) => {
12658
- if (result.callbackId === callbackId) {
12659
- clearTimeout(timeout);
12660
- // Restore original callback
12661
- window.smartlinksNativeCallback = originalCallback;
12662
- if (result.success) {
12663
- resolve({
12664
- isSignedIn: result.isSignedIn || false,
12665
- idToken: result.idToken,
12666
- email: result.email,
12667
- name: result.name,
12668
- picture: result.picture,
12669
- });
12670
- }
12671
- else {
12672
- resolve(null);
12673
- }
12698
+ registerAuthKitCallback(callbackId, (result) => {
12699
+ if (result.success) {
12700
+ resolve({
12701
+ isSignedIn: result.isSignedIn || false,
12702
+ idToken: result.idToken,
12703
+ email: result.email,
12704
+ name: result.name,
12705
+ picture: result.picture,
12706
+ });
12674
12707
  }
12675
- else if (originalCallback) {
12676
- // Pass through to original callback for other messages
12677
- originalCallback(result);
12708
+ else {
12709
+ resolve(null);
12678
12710
  }
12679
- };
12711
+ }, 5000, () => resolve(null));
12680
12712
  const payload = JSON.stringify({
12681
12713
  type: 'GOOGLE_CHECK_SIGN_IN',
12682
12714
  clientId,
12683
12715
  googleClientId,
12684
- serverClientId: googleClientId, // Alias for Android SDK
12716
+ serverClientId: googleClientId,
12685
12717
  callbackId,
12686
12718
  });
12687
12719
  nativeBridge.checkGoogleSignIn(payload);
@@ -12725,6 +12757,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
12725
12757
  const [contactSchema, setContactSchema] = useState(null); // Schema for registration fields
12726
12758
  const [silentSignInChecked, setSilentSignInChecked] = useState(false); // Track if silent sign-in has been checked
12727
12759
  const [googleFallbackToPopup, setGoogleFallbackToPopup] = useState(false); // Show popup fallback when FedCM is blocked/dismissed
12760
+ const [googleNativeTimedOut, setGoogleNativeTimedOut] = useState(false); // Native bridge callback timed out
12728
12761
  const log = useMemo(() => createLoggerWrapper(logger), [logger]);
12729
12762
  const api = new AuthAPI(apiEndpoint, clientId, clientName, logger);
12730
12763
  const auth = useAuth();
@@ -13001,6 +13034,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
13001
13034
  if (urlMode === 'verifyEmail') {
13002
13035
  log.log('Verifying email with token:', token);
13003
13036
  const response = await api.verifyEmailWithToken(token);
13037
+ log.log('Email verification response:', { hasToken: !!response.token, hasUser: !!response.user, emailVerificationMode: response.emailVerificationMode, isNewUser: response.isNewUser });
13004
13038
  // Get email verification mode from response or config
13005
13039
  const verificationMode = response.emailVerificationMode || config?.emailVerification?.mode || 'verify-auto-login';
13006
13040
  if ((verificationMode === 'verify-auto-login' || verificationMode === 'immediate') && response.token) {
@@ -13045,6 +13079,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
13045
13079
  log.log('Verifying reset token:', token);
13046
13080
  // Verify token is valid, then show password reset form
13047
13081
  const verifyResult = await api.verifyResetToken(token);
13082
+ log.log('Reset token verification result:', { valid: verifyResult.valid, hasEmail: !!verifyResult.email, message: verifyResult.message });
13048
13083
  // Check if token is valid before proceeding
13049
13084
  if (!verifyResult.valid) {
13050
13085
  throw new Error(verifyResult.message || 'Invalid or expired token');
@@ -13062,6 +13097,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
13062
13097
  else if (urlMode === 'magicLink') {
13063
13098
  log.log('Verifying magic link token:', token);
13064
13099
  const response = await api.verifyMagicLink(token);
13100
+ log.log('Magic link verification response:', { hasToken: !!response.token, hasUser: !!response.user, isNewUser: response.isNewUser });
13065
13101
  // Auto-login with magic link if token is provided
13066
13102
  if (response.token) {
13067
13103
  // Always await - auth.login now waits for parent ack automatically in iframe mode
@@ -13137,6 +13173,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
13137
13173
  const redirectUri = state.redirectUri || window.location.origin + window.location.pathname;
13138
13174
  // Exchange authorization code for tokens
13139
13175
  const response = await api.loginWithGoogleCode(code, redirectUri);
13176
+ log.log('Google OAuth code exchange response:', { hasToken: !!response.token, hasUser: !!response.user, isNewUser: response.isNewUser });
13140
13177
  if (response.token) {
13141
13178
  // Await login to ensure token is persisted before any navigation
13142
13179
  await auth.login(response.token, response.user, response.accountData, response.isNewUser, getExpirationFromResponse(response));
@@ -13167,13 +13204,15 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
13167
13204
  setError(undefined);
13168
13205
  setAuthSuccess(false);
13169
13206
  try {
13207
+ log.log('Submitting email auth:', { mode, email: data.email });
13170
13208
  const response = mode === 'login'
13171
13209
  ? await api.login(data.email, data.password)
13172
13210
  : await api.register({
13173
13211
  ...data,
13174
13212
  accountData: mode === 'register' ? accountData : undefined,
13175
- redirectUrl: getRedirectUrl(), // Include redirect URL for email verification
13213
+ redirectUrl: getRedirectUrl(),
13176
13214
  });
13215
+ log.log('Email auth response:', { mode, hasToken: !!response?.token, hasUser: !!response?.user, isNewUser: response?.isNewUser, requiresEmailVerification: response?.requiresEmailVerification, accountLocked: response?.accountLocked, emailVerificationMode: response?.emailVerificationMode });
13177
13216
  // Defensive check: validate response before accessing properties
13178
13217
  // SDK should throw on 401/error responses, but handle edge cases gracefully
13179
13218
  if (!response) {
@@ -13240,10 +13279,18 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
13240
13279
  if (response.token) {
13241
13280
  // Check for account lock or verification requirements
13242
13281
  if (response.accountLocked) {
13243
- throw new Error('Your account has been locked due to unverified email. Please check your email or request a new verification link.');
13282
+ setShowResendVerification(true);
13283
+ setResendEmail(data.email);
13284
+ setError('Your account has been locked due to unverified email. Click below to resend the verification link.');
13285
+ setLoading(false);
13286
+ return;
13244
13287
  }
13245
13288
  if (response.requiresEmailVerification) {
13246
- throw new Error('Please verify your email before logging in. Check your inbox for the verification link.');
13289
+ setShowResendVerification(true);
13290
+ setResendEmail(data.email);
13291
+ setError('Please verify your email before signing in. Click below to resend the verification link.');
13292
+ setLoading(false);
13293
+ return;
13247
13294
  }
13248
13295
  await auth.login(response.token, response.user, response.accountData, false, getExpirationFromResponse(response));
13249
13296
  setAuthSuccess(true);
@@ -13252,16 +13299,26 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
13252
13299
  // Note: No automatic redirect - app controls navigation via onAuthSuccess callback
13253
13300
  }
13254
13301
  else {
13255
- throw new Error('Authentication failed - please verify your email before logging in.');
13302
+ // No token returned - likely requires email verification
13303
+ setShowResendVerification(true);
13304
+ setResendEmail(data.email);
13305
+ setError('Please verify your email before signing in. Click below to resend the verification link.');
13306
+ setLoading(false);
13307
+ return;
13256
13308
  }
13257
13309
  }
13258
13310
  }
13259
13311
  catch (err) {
13260
13312
  // Debug: log the raw error shape to help diagnose SDK error wrapping issues
13261
13313
  log.error('handleEmailAuth error:', typeof err, err instanceof Error ? `Error.message=${err.message}` : '', JSON.stringify(err, Object.getOwnPropertyNames(err || {})));
13314
+ // Check if error requires email verification (403 EMAIL_NOT_VERIFIED, ACCOUNT_LOCKED, etc.)
13315
+ if (requiresEmailVerification(err)) {
13316
+ setShowResendVerification(true);
13317
+ setResendEmail(data.email);
13318
+ setError(getFriendlyErrorMessage(err) + ' Click below to resend the verification link.');
13319
+ }
13262
13320
  // Check if error is about email already registered (409 conflict)
13263
- // Handle both SmartlinksApiError (statusCode 409) and plain Error with keyword matching
13264
- if (mode === 'register' && (isConflictError(err) ||
13321
+ else if (mode === 'register' && (isConflictError(err) ||
13265
13322
  (err instanceof Error && /already (registered|exists)/i.test(err.message)))) {
13266
13323
  setShowResendVerification(true);
13267
13324
  setResendEmail(data.email);
@@ -13368,21 +13425,20 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
13368
13425
  if (nativeBridge) {
13369
13426
  log.log('Using native bridge for Google Sign-In');
13370
13427
  const callbackId = `google_auth_${Date.now()}`;
13371
- window.smartlinksNativeCallback = async (result) => {
13372
- // Ignore stale callbacks
13373
- if (result.callbackId !== callbackId) {
13374
- log.log('Ignoring stale native callback:', result.callbackId);
13375
- return;
13376
- }
13377
- log.log('Native callback received:', {
13428
+ // Register callback via AuthKit.onAuthResult (the native app calls this)
13429
+ const cleanup = registerAuthKitCallback(callbackId, async (result) => {
13430
+ log.log('Native Google auth result received:', {
13378
13431
  success: result.success,
13379
13432
  hasIdToken: !!result.idToken,
13380
13433
  email: result.email,
13381
13434
  error: result.error,
13435
+ callbackId: result.callbackId,
13382
13436
  });
13437
+ setGoogleNativeTimedOut(false);
13383
13438
  try {
13384
13439
  if (result.success && result.idToken) {
13385
13440
  const authResponse = await api.loginWithGoogle(result.idToken);
13441
+ log.log('Native Google login response:', { hasToken: !!authResponse.token, hasUser: !!authResponse.user, isNewUser: authResponse.isNewUser });
13386
13442
  if (authResponse.token) {
13387
13443
  await auth.login(authResponse.token, authResponse.user, authResponse.accountData, authResponse.isNewUser, getExpirationFromResponse(authResponse));
13388
13444
  setAuthSuccess(true);
@@ -13405,7 +13461,12 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
13405
13461
  finally {
13406
13462
  setLoading(false);
13407
13463
  }
13408
- };
13464
+ }, 30000, // 30s timeout for interactive Google Sign-In
13465
+ () => {
13466
+ log.log('Native Google Sign-In timed out after 30s');
13467
+ setLoading(false);
13468
+ setGoogleNativeTimedOut(true);
13469
+ });
13409
13470
  const payloadObj = {
13410
13471
  type: 'GOOGLE_SIGN_IN',
13411
13472
  clientId,
@@ -13417,12 +13478,13 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
13417
13478
  requestServerAuthCode: false,
13418
13479
  };
13419
13480
  const payload = JSON.stringify(payloadObj);
13420
- log.log('Invoking native signInWithGoogle');
13481
+ log.log('Invoking native signInWithGoogle with callbackId:', callbackId);
13421
13482
  try {
13422
13483
  nativeBridge.signInWithGoogle(payload);
13423
13484
  }
13424
13485
  catch (invokeError) {
13425
13486
  console.error(`${LOG_PREFIX} Exception invoking signInWithGoogle:`, invokeError);
13487
+ cleanup(); // Clean up the callback registration
13426
13488
  throw invokeError;
13427
13489
  }
13428
13490
  // Don't set loading to false - waiting for native callback
@@ -13467,6 +13529,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
13467
13529
  clearInterval(checkClosed);
13468
13530
  try {
13469
13531
  if (event.data.success && event.data.token) {
13532
+ log.log('Google proxy result received:', { success: true, hasUser: !!event.data.user, isNewUser: event.data.isNewUser });
13470
13533
  const { token, user, accountData, isNewUser, expiresAt, expiresIn } = event.data;
13471
13534
  const expiration = expiresAt || (expiresIn ? Date.now() + expiresIn : undefined);
13472
13535
  await auth.login(token, user, accountData, isNewUser, expiration);
@@ -13594,6 +13657,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
13594
13657
  googleUserInfo: userInfo,
13595
13658
  tokenType: 'access_token',
13596
13659
  });
13660
+ log.log('Popup Google login response:', { hasToken: !!authResponse.token, hasUser: !!authResponse.user, isNewUser: authResponse.isNewUser });
13597
13661
  if (authResponse.token) {
13598
13662
  // Google OAuth can be login or signup - use isNewUser flag from backend if available
13599
13663
  await auth.login(authResponse.token, authResponse.user, authResponse.accountData, authResponse.isNewUser, getExpirationFromResponse(authResponse));
@@ -13639,6 +13703,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
13639
13703
  try {
13640
13704
  const idToken = response.credential;
13641
13705
  const authResponse = await api.loginWithGoogle(idToken);
13706
+ log.log('OneTap Google login response:', { hasToken: !!authResponse.token, hasUser: !!authResponse.user, isNewUser: authResponse.isNewUser });
13642
13707
  if (authResponse.token) {
13643
13708
  // Google OAuth can be login or signup - use isNewUser flag from backend if available
13644
13709
  await auth.login(authResponse.token, authResponse.user, authResponse.accountData, authResponse.isNewUser, getExpirationFromResponse(authResponse));
@@ -13705,6 +13770,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
13705
13770
  else {
13706
13771
  // Verify code - Twilio identifies the verification by phone number
13707
13772
  const response = await api.verifyPhoneCode(phoneNumber, verificationCode);
13773
+ log.log('Phone verification response:', { hasToken: !!response?.token, hasUser: !!response?.user, isNewUser: response?.isNewUser });
13708
13774
  // Defensive validation: API may return undefined or error object on failure
13709
13775
  if (!response) {
13710
13776
  throw new Error('Verification failed - please check your code and try again');
@@ -13859,14 +13925,14 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
13859
13925
  color: config?.branding?.inheritHostStyles
13860
13926
  ? 'hsl(var(--foreground, 215 25% 15%))'
13861
13927
  : (resolvedTheme === 'dark' ? '#f1f5f9' : '#374151')
13862
- }, children: "Verification Link Expired" }), jsx("p", { style: {
13928
+ }, children: "Email Verification Required" }), jsx("p", { style: {
13863
13929
  marginBottom: '1rem',
13864
13930
  fontSize: '0.875rem',
13865
13931
  color: config?.branding?.inheritHostStyles
13866
13932
  ? 'hsl(var(--muted-foreground, 215 15% 45%))'
13867
13933
  : (resolvedTheme === 'dark' ? '#94a3b8' : '#6B7280'),
13868
13934
  lineHeight: '1.5'
13869
- }, children: "Your verification link has expired or is no longer valid. Please enter your email address below and we'll send you a new verification link." }), jsx("input", { type: "email", value: resendEmail || '', onChange: (e) => setResendEmail(e.target.value), placeholder: "your@email.com", className: config?.branding?.inheritHostStyles ? 'auth-input' : undefined, style: config?.branding?.inheritHostStyles ? {
13935
+ }, children: "Your email address needs to be verified before you can sign in. Enter your email below and we'll send you a verification link." }), jsx("input", { type: "email", value: resendEmail || '', onChange: (e) => setResendEmail(e.target.value), placeholder: "your@email.com", className: config?.branding?.inheritHostStyles ? 'auth-input' : undefined, style: config?.branding?.inheritHostStyles ? {
13870
13936
  width: '100%',
13871
13937
  padding: '0.625rem',
13872
13938
  marginBottom: '1rem',
@@ -14039,7 +14105,40 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
14039
14105
  backgroundColor: 'transparent',
14040
14106
  border: 'none',
14041
14107
  cursor: 'pointer',
14042
- }, children: "Dismiss" })] })), (() => {
14108
+ }, children: "Dismiss" })] })), googleNativeTimedOut && (jsxs("div", { style: {
14109
+ marginBottom: '1rem',
14110
+ padding: '0.75rem 1rem',
14111
+ borderRadius: '0.5rem',
14112
+ backgroundColor: resolvedTheme === 'dark' ? 'rgba(245, 158, 11, 0.1)' : 'rgba(245, 158, 11, 0.05)',
14113
+ border: `1px solid ${resolvedTheme === 'dark' ? 'rgba(245, 158, 11, 0.3)' : 'rgba(245, 158, 11, 0.2)'}`,
14114
+ }, children: [jsx("p", { style: {
14115
+ fontSize: '0.8125rem',
14116
+ color: resolvedTheme === 'dark' ? '#fbbf24' : '#92400e',
14117
+ marginBottom: '0.5rem',
14118
+ lineHeight: 1.4,
14119
+ }, children: 'Google sign-in didn\'t respond. You can try again or use another sign-in method.' }), jsx("button", { onClick: () => {
14120
+ setGoogleNativeTimedOut(false);
14121
+ handleGoogleLogin();
14122
+ }, style: {
14123
+ width: '100%',
14124
+ padding: '0.5rem 1rem',
14125
+ fontSize: '0.875rem',
14126
+ fontWeight: 500,
14127
+ color: '#fff',
14128
+ backgroundColor: '#4285F4',
14129
+ border: 'none',
14130
+ borderRadius: '0.375rem',
14131
+ cursor: 'pointer',
14132
+ }, children: 'Retry Google Sign-In' }), jsx("button", { onClick: () => setGoogleNativeTimedOut(false), style: {
14133
+ marginTop: '0.375rem',
14134
+ width: '100%',
14135
+ padding: '0.25rem',
14136
+ fontSize: '0.75rem',
14137
+ color: resolvedTheme === 'dark' ? '#64748b' : '#9ca3af',
14138
+ backgroundColor: 'transparent',
14139
+ border: 'none',
14140
+ cursor: 'pointer',
14141
+ }, children: 'Dismiss' })] })), (() => {
14043
14142
  const emailDisplayMode = config?.emailDisplayMode || 'form';
14044
14143
  const providerOrder = config?.providerOrder || (config?.enabledProviders || enabledProviders);
14045
14144
  const actualProviders = config?.enabledProviders || enabledProviders;
@@ -14859,7 +14958,15 @@ function useIframeMessages(iframeRef, options) {
14859
14958
  }
14860
14959
  catch (err) {
14861
14960
  console.error('[SmartlinksFrame] Proxy error:', err);
14862
- response.error = err?.message || 'Unknown error';
14961
+ const statusCode = err?.statusCode ?? err?.response?.status ?? err?.response?.statusCode;
14962
+ const errorBody = err?.details ?? err?.response?.data ?? err?.response ?? err?.cause;
14963
+ response.error = typeof err?.message === 'string' ? err.message : 'Unknown error';
14964
+ if (typeof statusCode === 'number') {
14965
+ response.statusCode = statusCode;
14966
+ }
14967
+ if (errorBody && typeof errorBody === 'object') {
14968
+ response.errorBody = JSON.parse(JSON.stringify(errorBody));
14969
+ }
14863
14970
  onErrorRef.current?.(err);
14864
14971
  }
14865
14972
  sendResponse(event.source, event.origin, response);