@proveanything/smartlinks-auth-ui 0.4.5 → 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.
package/dist/index.js CHANGED
@@ -12541,7 +12541,7 @@ function requiresEmailVerification(error) {
12541
12541
  }
12542
12542
 
12543
12543
  // VERSION: Update this when making changes to help identify which version is running
12544
- const AUTH_UI_VERSION = '44';
12544
+ const AUTH_UI_VERSION = '45';
12545
12545
  const LOG_PREFIX = `[SmartlinksAuthUI:v${AUTH_UI_VERSION}]`;
12546
12546
  // Helper to check for URL auth params synchronously (runs during initialization)
12547
12547
  // This prevents the form from flashing before detecting deep-link flows
@@ -12656,6 +12656,43 @@ const getNativeBridge = () => {
12656
12656
  return native;
12657
12657
  return null;
12658
12658
  };
12659
+ // Helper to register an AuthKit.onAuthResult handler for a specific callbackId
12660
+ // The Android native app calls: window.AuthKit.onAuthResult(resultJson)
12661
+ const registerAuthKitCallback = (callbackId, onResult, timeoutMs = 5000, onTimeout) => {
12662
+ const bridge = window.AuthKit;
12663
+ if (!bridge) {
12664
+ console.warn('[AuthKit] No AuthKit bridge found, cannot register callback');
12665
+ onTimeout?.();
12666
+ return () => { };
12667
+ }
12668
+ // Store the previous handler so we can restore on cleanup
12669
+ const previousHandler = bridge.onAuthResult;
12670
+ const timeout = setTimeout(() => {
12671
+ // Restore previous handler on timeout
12672
+ bridge.onAuthResult = previousHandler;
12673
+ onTimeout?.();
12674
+ }, timeoutMs);
12675
+ bridge.onAuthResult = (resultOrJson) => {
12676
+ const result = typeof resultOrJson === 'string' ? JSON.parse(resultOrJson) : resultOrJson;
12677
+ // Match by callbackId if present, otherwise accept any result
12678
+ if (result.callbackId && result.callbackId !== callbackId) {
12679
+ // Not for us — pass through to previous handler if any
12680
+ if (typeof previousHandler === 'function') {
12681
+ previousHandler(resultOrJson);
12682
+ }
12683
+ return;
12684
+ }
12685
+ clearTimeout(timeout);
12686
+ // Restore previous handler
12687
+ bridge.onAuthResult = previousHandler;
12688
+ onResult(result);
12689
+ };
12690
+ // Return cleanup function
12691
+ return () => {
12692
+ clearTimeout(timeout);
12693
+ bridge.onAuthResult = previousHandler;
12694
+ };
12695
+ };
12659
12696
  // Sign out from Google on the native side (clears cached Google account)
12660
12697
  // This is fire-and-forget with a timeout - gracefully degrades if not supported
12661
12698
  const signOutGoogleNative = async () => {
@@ -12664,21 +12701,7 @@ const signOutGoogleNative = async () => {
12664
12701
  return;
12665
12702
  const callbackId = `google_signout_${Date.now()}`;
12666
12703
  return new Promise((resolve) => {
12667
- const timeout = setTimeout(() => resolve(), 3000);
12668
- // Store original callback to restore later
12669
- const originalCallback = window.smartlinksNativeCallback;
12670
- window.smartlinksNativeCallback = (result) => {
12671
- if (result.callbackId === callbackId) {
12672
- clearTimeout(timeout);
12673
- // Restore original callback
12674
- window.smartlinksNativeCallback = originalCallback;
12675
- resolve();
12676
- }
12677
- else if (originalCallback) {
12678
- // Pass through to original callback for other messages
12679
- originalCallback(result);
12680
- }
12681
- };
12704
+ registerAuthKitCallback(callbackId, () => resolve(), 3000, () => resolve());
12682
12705
  const payload = JSON.stringify({
12683
12706
  type: 'GOOGLE_SIGN_OUT',
12684
12707
  callbackId,
@@ -12692,37 +12715,25 @@ const checkSilentGoogleSignIn = async (clientId, googleClientId) => {
12692
12715
  return null;
12693
12716
  const callbackId = `google_check_${Date.now()}`;
12694
12717
  return new Promise((resolve) => {
12695
- const timeout = setTimeout(() => resolve(null), 5000);
12696
- // Store original callback to restore later
12697
- const originalCallback = window.smartlinksNativeCallback;
12698
- window.smartlinksNativeCallback = (result) => {
12699
- if (result.callbackId === callbackId) {
12700
- clearTimeout(timeout);
12701
- // Restore original callback
12702
- window.smartlinksNativeCallback = originalCallback;
12703
- if (result.success) {
12704
- resolve({
12705
- isSignedIn: result.isSignedIn || false,
12706
- idToken: result.idToken,
12707
- email: result.email,
12708
- name: result.name,
12709
- picture: result.picture,
12710
- });
12711
- }
12712
- else {
12713
- resolve(null);
12714
- }
12718
+ registerAuthKitCallback(callbackId, (result) => {
12719
+ if (result.success) {
12720
+ resolve({
12721
+ isSignedIn: result.isSignedIn || false,
12722
+ idToken: result.idToken,
12723
+ email: result.email,
12724
+ name: result.name,
12725
+ picture: result.picture,
12726
+ });
12715
12727
  }
12716
- else if (originalCallback) {
12717
- // Pass through to original callback for other messages
12718
- originalCallback(result);
12728
+ else {
12729
+ resolve(null);
12719
12730
  }
12720
- };
12731
+ }, 5000, () => resolve(null));
12721
12732
  const payload = JSON.stringify({
12722
12733
  type: 'GOOGLE_CHECK_SIGN_IN',
12723
12734
  clientId,
12724
12735
  googleClientId,
12725
- serverClientId: googleClientId, // Alias for Android SDK
12736
+ serverClientId: googleClientId,
12726
12737
  callbackId,
12727
12738
  });
12728
12739
  nativeBridge.checkGoogleSignIn(payload);
@@ -12766,6 +12777,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
12766
12777
  const [contactSchema, setContactSchema] = React.useState(null); // Schema for registration fields
12767
12778
  const [silentSignInChecked, setSilentSignInChecked] = React.useState(false); // Track if silent sign-in has been checked
12768
12779
  const [googleFallbackToPopup, setGoogleFallbackToPopup] = React.useState(false); // Show popup fallback when FedCM is blocked/dismissed
12780
+ const [googleNativeTimedOut, setGoogleNativeTimedOut] = React.useState(false); // Native bridge callback timed out
12769
12781
  const log = React.useMemo(() => createLoggerWrapper(logger), [logger]);
12770
12782
  const api = new AuthAPI(apiEndpoint, clientId, clientName, logger);
12771
12783
  const auth = useAuth();
@@ -13042,6 +13054,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
13042
13054
  if (urlMode === 'verifyEmail') {
13043
13055
  log.log('Verifying email with token:', token);
13044
13056
  const response = await api.verifyEmailWithToken(token);
13057
+ log.log('Email verification response:', { hasToken: !!response.token, hasUser: !!response.user, emailVerificationMode: response.emailVerificationMode, isNewUser: response.isNewUser });
13045
13058
  // Get email verification mode from response or config
13046
13059
  const verificationMode = response.emailVerificationMode || config?.emailVerification?.mode || 'verify-auto-login';
13047
13060
  if ((verificationMode === 'verify-auto-login' || verificationMode === 'immediate') && response.token) {
@@ -13086,6 +13099,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
13086
13099
  log.log('Verifying reset token:', token);
13087
13100
  // Verify token is valid, then show password reset form
13088
13101
  const verifyResult = await api.verifyResetToken(token);
13102
+ log.log('Reset token verification result:', { valid: verifyResult.valid, hasEmail: !!verifyResult.email, message: verifyResult.message });
13089
13103
  // Check if token is valid before proceeding
13090
13104
  if (!verifyResult.valid) {
13091
13105
  throw new Error(verifyResult.message || 'Invalid or expired token');
@@ -13103,6 +13117,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
13103
13117
  else if (urlMode === 'magicLink') {
13104
13118
  log.log('Verifying magic link token:', token);
13105
13119
  const response = await api.verifyMagicLink(token);
13120
+ log.log('Magic link verification response:', { hasToken: !!response.token, hasUser: !!response.user, isNewUser: response.isNewUser });
13106
13121
  // Auto-login with magic link if token is provided
13107
13122
  if (response.token) {
13108
13123
  // Always await - auth.login now waits for parent ack automatically in iframe mode
@@ -13178,6 +13193,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
13178
13193
  const redirectUri = state.redirectUri || window.location.origin + window.location.pathname;
13179
13194
  // Exchange authorization code for tokens
13180
13195
  const response = await api.loginWithGoogleCode(code, redirectUri);
13196
+ log.log('Google OAuth code exchange response:', { hasToken: !!response.token, hasUser: !!response.user, isNewUser: response.isNewUser });
13181
13197
  if (response.token) {
13182
13198
  // Await login to ensure token is persisted before any navigation
13183
13199
  await auth.login(response.token, response.user, response.accountData, response.isNewUser, getExpirationFromResponse(response));
@@ -13208,13 +13224,15 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
13208
13224
  setError(undefined);
13209
13225
  setAuthSuccess(false);
13210
13226
  try {
13227
+ log.log('Submitting email auth:', { mode, email: data.email });
13211
13228
  const response = mode === 'login'
13212
13229
  ? await api.login(data.email, data.password)
13213
13230
  : await api.register({
13214
13231
  ...data,
13215
13232
  accountData: mode === 'register' ? accountData : undefined,
13216
- redirectUrl: getRedirectUrl(), // Include redirect URL for email verification
13233
+ redirectUrl: getRedirectUrl(),
13217
13234
  });
13235
+ log.log('Email auth response:', { mode, hasToken: !!response?.token, hasUser: !!response?.user, isNewUser: response?.isNewUser, requiresEmailVerification: response?.requiresEmailVerification, accountLocked: response?.accountLocked, emailVerificationMode: response?.emailVerificationMode });
13218
13236
  // Defensive check: validate response before accessing properties
13219
13237
  // SDK should throw on 401/error responses, but handle edge cases gracefully
13220
13238
  if (!response) {
@@ -13427,21 +13445,20 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
13427
13445
  if (nativeBridge) {
13428
13446
  log.log('Using native bridge for Google Sign-In');
13429
13447
  const callbackId = `google_auth_${Date.now()}`;
13430
- window.smartlinksNativeCallback = async (result) => {
13431
- // Ignore stale callbacks
13432
- if (result.callbackId !== callbackId) {
13433
- log.log('Ignoring stale native callback:', result.callbackId);
13434
- return;
13435
- }
13436
- log.log('Native callback received:', {
13448
+ // Register callback via AuthKit.onAuthResult (the native app calls this)
13449
+ const cleanup = registerAuthKitCallback(callbackId, async (result) => {
13450
+ log.log('Native Google auth result received:', {
13437
13451
  success: result.success,
13438
13452
  hasIdToken: !!result.idToken,
13439
13453
  email: result.email,
13440
13454
  error: result.error,
13455
+ callbackId: result.callbackId,
13441
13456
  });
13457
+ setGoogleNativeTimedOut(false);
13442
13458
  try {
13443
13459
  if (result.success && result.idToken) {
13444
13460
  const authResponse = await api.loginWithGoogle(result.idToken);
13461
+ log.log('Native Google login response:', { hasToken: !!authResponse.token, hasUser: !!authResponse.user, isNewUser: authResponse.isNewUser });
13445
13462
  if (authResponse.token) {
13446
13463
  await auth.login(authResponse.token, authResponse.user, authResponse.accountData, authResponse.isNewUser, getExpirationFromResponse(authResponse));
13447
13464
  setAuthSuccess(true);
@@ -13464,7 +13481,12 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
13464
13481
  finally {
13465
13482
  setLoading(false);
13466
13483
  }
13467
- };
13484
+ }, 30000, // 30s timeout for interactive Google Sign-In
13485
+ () => {
13486
+ log.log('Native Google Sign-In timed out after 30s');
13487
+ setLoading(false);
13488
+ setGoogleNativeTimedOut(true);
13489
+ });
13468
13490
  const payloadObj = {
13469
13491
  type: 'GOOGLE_SIGN_IN',
13470
13492
  clientId,
@@ -13476,12 +13498,13 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
13476
13498
  requestServerAuthCode: false,
13477
13499
  };
13478
13500
  const payload = JSON.stringify(payloadObj);
13479
- log.log('Invoking native signInWithGoogle');
13501
+ log.log('Invoking native signInWithGoogle with callbackId:', callbackId);
13480
13502
  try {
13481
13503
  nativeBridge.signInWithGoogle(payload);
13482
13504
  }
13483
13505
  catch (invokeError) {
13484
13506
  console.error(`${LOG_PREFIX} Exception invoking signInWithGoogle:`, invokeError);
13507
+ cleanup(); // Clean up the callback registration
13485
13508
  throw invokeError;
13486
13509
  }
13487
13510
  // Don't set loading to false - waiting for native callback
@@ -13526,6 +13549,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
13526
13549
  clearInterval(checkClosed);
13527
13550
  try {
13528
13551
  if (event.data.success && event.data.token) {
13552
+ log.log('Google proxy result received:', { success: true, hasUser: !!event.data.user, isNewUser: event.data.isNewUser });
13529
13553
  const { token, user, accountData, isNewUser, expiresAt, expiresIn } = event.data;
13530
13554
  const expiration = expiresAt || (expiresIn ? Date.now() + expiresIn : undefined);
13531
13555
  await auth.login(token, user, accountData, isNewUser, expiration);
@@ -13653,6 +13677,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
13653
13677
  googleUserInfo: userInfo,
13654
13678
  tokenType: 'access_token',
13655
13679
  });
13680
+ log.log('Popup Google login response:', { hasToken: !!authResponse.token, hasUser: !!authResponse.user, isNewUser: authResponse.isNewUser });
13656
13681
  if (authResponse.token) {
13657
13682
  // Google OAuth can be login or signup - use isNewUser flag from backend if available
13658
13683
  await auth.login(authResponse.token, authResponse.user, authResponse.accountData, authResponse.isNewUser, getExpirationFromResponse(authResponse));
@@ -13698,6 +13723,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
13698
13723
  try {
13699
13724
  const idToken = response.credential;
13700
13725
  const authResponse = await api.loginWithGoogle(idToken);
13726
+ log.log('OneTap Google login response:', { hasToken: !!authResponse.token, hasUser: !!authResponse.user, isNewUser: authResponse.isNewUser });
13701
13727
  if (authResponse.token) {
13702
13728
  // Google OAuth can be login or signup - use isNewUser flag from backend if available
13703
13729
  await auth.login(authResponse.token, authResponse.user, authResponse.accountData, authResponse.isNewUser, getExpirationFromResponse(authResponse));
@@ -13764,6 +13790,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
13764
13790
  else {
13765
13791
  // Verify code - Twilio identifies the verification by phone number
13766
13792
  const response = await api.verifyPhoneCode(phoneNumber, verificationCode);
13793
+ log.log('Phone verification response:', { hasToken: !!response?.token, hasUser: !!response?.user, isNewUser: response?.isNewUser });
13767
13794
  // Defensive validation: API may return undefined or error object on failure
13768
13795
  if (!response) {
13769
13796
  throw new Error('Verification failed - please check your code and try again');
@@ -14098,7 +14125,40 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
14098
14125
  backgroundColor: 'transparent',
14099
14126
  border: 'none',
14100
14127
  cursor: 'pointer',
14101
- }, children: "Dismiss" })] })), (() => {
14128
+ }, children: "Dismiss" })] })), googleNativeTimedOut && (jsxRuntime.jsxs("div", { style: {
14129
+ marginBottom: '1rem',
14130
+ padding: '0.75rem 1rem',
14131
+ borderRadius: '0.5rem',
14132
+ backgroundColor: resolvedTheme === 'dark' ? 'rgba(245, 158, 11, 0.1)' : 'rgba(245, 158, 11, 0.05)',
14133
+ border: `1px solid ${resolvedTheme === 'dark' ? 'rgba(245, 158, 11, 0.3)' : 'rgba(245, 158, 11, 0.2)'}`,
14134
+ }, children: [jsxRuntime.jsx("p", { style: {
14135
+ fontSize: '0.8125rem',
14136
+ color: resolvedTheme === 'dark' ? '#fbbf24' : '#92400e',
14137
+ marginBottom: '0.5rem',
14138
+ lineHeight: 1.4,
14139
+ }, children: 'Google sign-in didn\'t respond. You can try again or use another sign-in method.' }), jsxRuntime.jsx("button", { onClick: () => {
14140
+ setGoogleNativeTimedOut(false);
14141
+ handleGoogleLogin();
14142
+ }, style: {
14143
+ width: '100%',
14144
+ padding: '0.5rem 1rem',
14145
+ fontSize: '0.875rem',
14146
+ fontWeight: 500,
14147
+ color: '#fff',
14148
+ backgroundColor: '#4285F4',
14149
+ border: 'none',
14150
+ borderRadius: '0.375rem',
14151
+ cursor: 'pointer',
14152
+ }, children: 'Retry Google Sign-In' }), jsxRuntime.jsx("button", { onClick: () => setGoogleNativeTimedOut(false), style: {
14153
+ marginTop: '0.375rem',
14154
+ width: '100%',
14155
+ padding: '0.25rem',
14156
+ fontSize: '0.75rem',
14157
+ color: resolvedTheme === 'dark' ? '#64748b' : '#9ca3af',
14158
+ backgroundColor: 'transparent',
14159
+ border: 'none',
14160
+ cursor: 'pointer',
14161
+ }, children: 'Dismiss' })] })), (() => {
14102
14162
  const emailDisplayMode = config?.emailDisplayMode || 'form';
14103
14163
  const providerOrder = config?.providerOrder || (config?.enabledProviders || enabledProviders);
14104
14164
  const actualProviders = config?.enabledProviders || enabledProviders;