@proveanything/smartlinks-auth-ui 0.5.22 → 0.6.0

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.esm.js CHANGED
@@ -316,7 +316,7 @@ const EmailAuthForm = ({ mode, onSubmit, onModeSwitch, onForgotPassword, loading
316
316
  }, disabled: loading })), jsxs("div", { className: "auth-form-header", children: [jsx("h2", { className: "auth-form-title", children: title }), jsx("p", { className: "auth-form-subtitle", children: subtitle })] }), error && (jsxs("div", { className: "auth-error", role: "alert", children: [jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "currentColor", children: jsx("path", { d: "M8 0C3.58 0 0 3.58 0 8s3.58 8 8 8 8-3.58 8-8-3.58-8-8-8zm1 13H7v-2h2v2zm0-3H7V4h2v6z" }) }), error] })), mode === 'register' && (jsxs("div", { className: "auth-form-group", children: [jsx("label", { htmlFor: "displayName", className: "auth-label", children: "Full Name" }), jsx("input", { type: "text", id: "displayName", className: "auth-input", value: formData.displayName || '', onChange: (e) => handleChange('displayName', e.target.value), required: mode === 'register', disabled: loading, placeholder: "John Smith" })] })), jsxs("div", { className: "auth-form-group", children: [jsx("label", { htmlFor: "email", className: "auth-label", children: "Email address" }), jsx("input", { type: "email", id: "email", className: "auth-input", value: formData.email || '', onChange: (e) => handleChange('email', e.target.value), required: true, disabled: loading, placeholder: "you@example.com", autoComplete: "email" })] }), jsxs("div", { className: "auth-form-group", children: [jsx("label", { htmlFor: "password", className: "auth-label", children: "Password" }), jsx("input", { type: "password", id: "password", className: "auth-input", value: formData.password || '', onChange: (e) => handleChange('password', e.target.value), required: true, disabled: loading, placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022", autoComplete: mode === 'login' ? 'current-password' : 'new-password', minLength: 6 })] }), mode === 'register' && hasSchemaFields && schemaFields.inline.map(renderSchemaField), mode === 'register' && hasLegacyFields && additionalFields.map(renderLegacyField), mode === 'register' && hasSchemaFields && schemaFields.postCredentials.length > 0 && (jsxs(Fragment, { children: [jsx("div", { className: "auth-divider", style: { margin: '16px 0' }, children: jsx("span", { children: "Additional Information" }) }), schemaFields.postCredentials.map(renderSchemaField)] })), mode === 'login' && (jsx("div", { className: "auth-form-footer", children: jsx("button", { type: "button", className: "auth-link", onClick: onForgotPassword, disabled: loading, children: "Forgot password?" }) })), jsx("button", { type: "submit", className: "auth-button auth-button-primary", disabled: loading, children: loading ? (jsx("span", { className: "auth-spinner" })) : mode === 'login' ? ('Sign in') : ('Create account') }), signupProminence !== 'balanced' && signupProminence !== 'none' && (jsxs("div", { className: "auth-divider", children: [jsx("span", { children: mode === 'login' ? "Don't have an account?" : 'Already have an account?' }), signupRedirectUrl && mode === 'login' ? (jsx("a", { href: signupRedirectUrl, target: "_top", className: "auth-link auth-link-bold", children: "Sign up" })) : (jsx("button", { type: "button", className: "auth-link auth-link-bold", onClick: onModeSwitch, disabled: loading, children: mode === 'login' ? 'Sign up' : 'Sign in' }))] }))] }));
317
317
  };
318
318
 
319
- const ProviderButtons = ({ enabledProviders, providerOrder, onEmailLogin, onGoogleLogin, onPhoneLogin, onMagicLinkLogin, onWhatsAppLogin, loading, }) => {
319
+ const ProviderButtons = ({ enabledProviders, providerOrder, onEmailLogin, onGoogleLogin, onAppleLogin, onPhoneLogin, onMagicLinkLogin, onWhatsAppLogin, loading, }) => {
320
320
  if (enabledProviders.length === 0)
321
321
  return null;
322
322
  // Determine the order of providers to display
@@ -339,6 +339,11 @@ const ProviderButtons = ({ enabledProviders, providerOrder, onEmailLogin, onGoog
339
339
  icon: (jsxs("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", children: [jsx("path", { d: "M19.6 10.227c0-.709-.064-1.39-.182-2.045H10v3.868h5.382a4.6 4.6 0 01-1.996 3.018v2.51h3.232c1.891-1.742 2.982-4.305 2.982-7.35z", fill: "#4285F4" }), jsx("path", { d: "M10 20c2.7 0 4.964-.895 6.618-2.423l-3.232-2.509c-.895.6-2.04.955-3.386.955-2.605 0-4.81-1.76-5.595-4.123H1.064v2.59A9.996 9.996 0 0010 20z", fill: "#34A853" }), jsx("path", { d: "M4.405 11.9c-.2-.6-.314-1.24-.314-1.9 0-.66.114-1.3.314-1.9V5.51H1.064A9.996 9.996 0 000 10c0 1.614.386 3.14 1.064 4.49l3.34-2.59z", fill: "#FBBC05" }), jsx("path", { d: "M10 3.977c1.468 0 2.786.505 3.823 1.496l2.868-2.868C14.959.99 12.695 0 10 0 6.09 0 2.71 2.24 1.064 5.51l3.34 2.59C5.19 5.736 7.395 3.977 10 3.977z", fill: "#EA4335" })] })),
340
340
  onClick: onGoogleLogin
341
341
  },
342
+ apple: {
343
+ label: 'Continue with Apple',
344
+ icon: (jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true", children: jsx("path", { d: "M17.05 20.28c-.98.95-2.05.8-3.08.35-1.09-.46-2.09-.48-3.24 0-1.44.62-2.2.44-3.06-.35C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.54 4.09l.01-.01zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z" }) })),
345
+ onClick: () => onAppleLogin?.()
346
+ },
342
347
  phone: {
343
348
  label: 'Continue with Phone',
344
349
  icon: (jsx("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", children: jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V15a2 2 0 01-2 2h-1C7.82 17 2 11.18 2 5V4z" }) })),
@@ -11410,6 +11415,25 @@ class AuthAPI {
11410
11415
  redirectUri,
11411
11416
  });
11412
11417
  }
11418
+ /**
11419
+ * Sign in with Apple via an identity token (from native "Sign in with Apple"
11420
+ * or the web JS flow). The backend verifies the JWT against Apple's keys.
11421
+ *
11422
+ * Delegates to `smartlinks.authKit.appleLogin`, which on success stores the
11423
+ * bearer token automatically and invalidates the SDK cache.
11424
+ */
11425
+ async loginWithApple(identityToken, options) {
11426
+ this.log.log('loginWithApple called:', {
11427
+ tokenLength: identityToken?.length,
11428
+ hasAuthCode: !!options?.authorizationCode,
11429
+ hasUserInfo: !!options?.appleUserInfo,
11430
+ });
11431
+ return smartlinks.authKit.appleLogin(this.clientId, identityToken, {
11432
+ authorizationCode: options?.authorizationCode,
11433
+ nonce: options?.nonce,
11434
+ userInfo: options?.appleUserInfo,
11435
+ });
11436
+ }
11413
11437
  async sendPhoneCode(phoneNumber) {
11414
11438
  return smartlinks.authKit.sendPhoneCode(this.clientId, phoneNumber);
11415
11439
  }
@@ -11940,6 +11964,24 @@ async function getStorage() {
11940
11964
  storageInstance = await storageInitPromise;
11941
11965
  return storageInstance;
11942
11966
  }
11967
+ /**
11968
+ * Install a custom storage backend (e.g. Capacitor Keychain / Keystore).
11969
+ *
11970
+ * Must be called BEFORE `getStorage()` is invoked for the first time
11971
+ * (typically right after your app boots, before `<SmartlinksAuthUI />` mounts).
11972
+ * The supplied adapter then handles ALL token, user, and account persistence
11973
+ * — replacing the default IndexedDB → localStorage → in-memory chain.
11974
+ *
11975
+ * Intended for native shells (Capacitor / Capgo) that want tokens stored in
11976
+ * the OS secure enclave (Keychain on iOS, Keystore on Android) instead of the
11977
+ * WebView's IndexedDB. Implement the `PersistentStorage` interface against
11978
+ * your secure-storage plugin and pass the instance here.
11979
+ */
11980
+ function setStorageAdapter(adapter) {
11981
+ StorageFactory.reset();
11982
+ storageInstance = adapter;
11983
+ storageInitPromise = Promise.resolve(adapter);
11984
+ }
11943
11985
  /**
11944
11986
  * Listen for storage changes from other tabs
11945
11987
  */
@@ -11958,6 +12000,7 @@ function onStorageChange(callback) {
11958
12000
  }
11959
12001
 
11960
12002
  const TOKEN_KEY = 'token';
12003
+ const REFRESH_TOKEN_KEY = 'refresh_token';
11961
12004
  const USER_KEY = 'user';
11962
12005
  const ACCOUNT_DATA_KEY = 'account_data';
11963
12006
  const ACCOUNT_INFO_KEY = 'account_info';
@@ -11996,6 +12039,28 @@ const tokenStorage = {
11996
12039
  const storage = await getStorage();
11997
12040
  await storage.removeItem(TOKEN_KEY);
11998
12041
  },
12042
+ // ----- Refresh token (native / long-lived sessions) ------------------
12043
+ async saveRefreshToken(token, expiresAt) {
12044
+ const storage = await getStorage();
12045
+ const payload = { token, expiresAt };
12046
+ await storage.setItem(REFRESH_TOKEN_KEY, payload);
12047
+ },
12048
+ async getRefreshToken() {
12049
+ const storage = await getStorage();
12050
+ const rt = await storage.getItem(REFRESH_TOKEN_KEY);
12051
+ if (!rt)
12052
+ return null;
12053
+ if (rt.expiresAt && rt.expiresAt < Date.now()) {
12054
+ console.log('[TokenStorage] Refresh token expired - clearing');
12055
+ await this.clearRefreshToken();
12056
+ return null;
12057
+ }
12058
+ return rt;
12059
+ },
12060
+ async clearRefreshToken() {
12061
+ const storage = await getStorage();
12062
+ await storage.removeItem(REFRESH_TOKEN_KEY);
12063
+ },
11999
12064
  async saveUser(user) {
12000
12065
  const storage = await getStorage();
12001
12066
  await storage.setItem(USER_KEY, user);
@@ -12010,6 +12075,7 @@ const tokenStorage = {
12010
12075
  },
12011
12076
  async clearAll() {
12012
12077
  await this.clearToken();
12078
+ await this.clearRefreshToken();
12013
12079
  await this.clearUser();
12014
12080
  await this.clearAccountData();
12015
12081
  await this.clearAccountInfo();
@@ -12067,12 +12133,18 @@ const tokenStorage = {
12067
12133
 
12068
12134
  // Export context for optional usage (e.g., SmartlinksFrame can work without AuthProvider)
12069
12135
  const AuthContext = createContext(undefined);
12070
- const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 * 1000, preloadAccountInfo = false,
12136
+ const AuthProvider = ({ children, proxyMode = false, accountCacheTTL = 5 * 60 * 1000, preloadAccountInfo = false, clientId: clientIdProp,
12071
12137
  // Token refresh settings
12072
12138
  enableAutoRefresh = true, refreshThresholdPercent = 75, // Refresh when 75% of token lifetime has passed
12073
12139
  refreshCheckInterval = 60 * 1000, // Check every minute
12140
+ refreshOnResume,
12074
12141
  // Contact & Interaction features
12075
12142
  collectionId, enableContactSync, enableInteractionTracking, interactionAppId, interactionConfig, }) => {
12143
+ // Resolved client ID — explicit prop wins; otherwise fall back to the
12144
+ // collection's defaultAuthKitId (the standard pattern — see
12145
+ // mem://architecture/default-authkit-collection-property).
12146
+ const [resolvedClientId, setResolvedClientId] = useState(clientIdProp);
12147
+ const clientId = resolvedClientId;
12076
12148
  const [user, setUser] = useState(null);
12077
12149
  const [token, setToken] = useState(null);
12078
12150
  const [accountData, setAccountData] = useState(null);
@@ -12088,8 +12160,8 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
12088
12160
  const pendingVerificationRef = useRef(false);
12089
12161
  const proxyRefreshInFlightRef = useRef(false);
12090
12162
  // Stable refs for callbacks to avoid useEffect dependency cycles
12091
- const syncContactRef = useRef();
12092
- const trackInteractionRef = useRef();
12163
+ const syncContactRef = useRef(undefined);
12164
+ const trackInteractionRef = useRef(undefined);
12093
12165
  // Default to enabled if collectionId is provided
12094
12166
  const shouldSyncContacts = enableContactSync ?? !!collectionId;
12095
12167
  const shouldTrackInteractions = enableInteractionTracking ?? !!collectionId;
@@ -12772,9 +12844,7 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
12772
12844
  });
12773
12845
  });
12774
12846
  };
12775
- const login = useCallback(async (authToken, authUser, authAccountData, isNewUser, expiresAt
12776
- // Note: waitForParentAck removed - we ALWAYS wait for parent ack in iframe mode now
12777
- ) => {
12847
+ const login = useCallback(async (authToken, authUser, authAccountData, isNewUser, expiresAt, refreshTokenValue, refreshTokenExpiresAt) => {
12778
12848
  try {
12779
12849
  // Only persist to storage in standalone mode
12780
12850
  if (!proxyMode) {
@@ -12788,6 +12858,15 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
12788
12858
  else {
12789
12859
  await tokenStorage.clearAccountData();
12790
12860
  }
12861
+ // Persist refresh token when the backend issued one (native clients).
12862
+ if (refreshTokenValue) {
12863
+ const rtExp = refreshTokenExpiresAt
12864
+ ?? Date.now() + 90 * 24 * 60 * 60 * 1000; // 90d fallback per spec
12865
+ await tokenStorage.saveRefreshToken(refreshTokenValue, rtExp);
12866
+ }
12867
+ else {
12868
+ await tokenStorage.clearRefreshToken();
12869
+ }
12791
12870
  smartlinks.auth.verifyToken(authToken).catch(() => { });
12792
12871
  }
12793
12872
  // Always update memory state (but NOT isVerified yet - wait for parent ack in iframe mode)
@@ -12851,6 +12930,17 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
12851
12930
  }
12852
12931
  // Only clear persistent storage in standalone mode
12853
12932
  if (!proxyMode) {
12933
+ // Best-effort: revoke the refresh-token family server-side before
12934
+ // wiping local state, so a stolen copy is dead immediately.
12935
+ try {
12936
+ const storedRefresh = await tokenStorage.getRefreshToken();
12937
+ if (storedRefresh?.token && clientId) {
12938
+ await smartlinks.authKit.logout(clientId, storedRefresh.token);
12939
+ }
12940
+ }
12941
+ catch (err) {
12942
+ console.warn('[AuthContext] Refresh-token revoke failed (continuing):', err);
12943
+ }
12854
12944
  await tokenStorage.clearAll();
12855
12945
  smartlinks.auth.logout();
12856
12946
  }
@@ -12876,7 +12966,7 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
12876
12966
  catch (error) {
12877
12967
  console.error('Failed to clear auth data from storage:', error);
12878
12968
  }
12879
- }, [proxyMode, notifyAuthStateChange, user, contactId, collectionId, shouldTrackInteractions, trackInteraction]);
12969
+ }, [proxyMode, clientId, notifyAuthStateChange, user, contactId, collectionId, shouldTrackInteractions, trackInteraction]);
12880
12970
  const getToken = useCallback(async () => {
12881
12971
  if (proxyMode) {
12882
12972
  return token;
@@ -12903,12 +12993,44 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
12903
12993
  expiresIn: Math.max(0, storedToken.expiresAt - Date.now()),
12904
12994
  };
12905
12995
  }, [proxyMode, token]);
12906
- // Refresh token - validates current token and extends session if backend supports it
12996
+ // Refresh token - prefers the AuthKit refresh-token endpoint when a refresh
12997
+ // token is stored (native clients); otherwise falls back to verify-and-extend
12998
+ // for web clients whose backend still issues only short-lived JWTs.
12907
12999
  const refreshToken = useCallback(async () => {
12908
13000
  if (proxyMode) {
12909
13001
  throw new Error('Token refresh in proxy mode is handled by the parent application');
12910
13002
  }
12911
13003
  const storedToken = await tokenStorage.getToken();
13004
+ const storedRefresh = await tokenStorage.getRefreshToken();
13005
+ // -------- Path A: true refresh-token rotation (native) ---------------
13006
+ if (storedRefresh?.token && clientId) {
13007
+ try {
13008
+ const resp = await smartlinks.authKit.refreshToken(clientId, storedRefresh.token);
13009
+ await tokenStorage.saveToken(resp.token, resp.expiresAt);
13010
+ await tokenStorage.saveRefreshToken(resp.refreshToken, resp.refreshTokenExpiresAt);
13011
+ setToken(resp.token);
13012
+ setIsVerified(true);
13013
+ pendingVerificationRef.current = false;
13014
+ notifyAuthStateChange('TOKEN_REFRESH', user, resp.token, accountData, accountInfo, true, contact, contactId);
13015
+ return resp.token;
13016
+ }
13017
+ catch (error) {
13018
+ console.error('[AuthContext] Refresh-token rotation failed:', error);
13019
+ if (isNetworkError(error))
13020
+ throw error;
13021
+ // Reuse / invalid / expired → force re-login. The SDK surfaces these
13022
+ // as RefreshErrorCode (INVALID_REFRESH_TOKEN / REUSE_DETECTED / MISSING).
13023
+ const code = error?.errorCode ?? error?.code;
13024
+ if (code === 'INVALID_REFRESH_TOKEN' ||
13025
+ code === 'REFRESH_TOKEN_REUSE_DETECTED' ||
13026
+ code === 'MISSING_REFRESH_TOKEN') {
13027
+ await logout();
13028
+ throw new Error('Session expired. Please login again.');
13029
+ }
13030
+ // Fall through to verify-and-extend on unexpected errors.
13031
+ }
13032
+ }
13033
+ // -------- Path B: legacy verify-and-extend (web) ---------------------
12912
13034
  if (!storedToken?.token) {
12913
13035
  throw new Error('No token to refresh. Please login first.');
12914
13036
  }
@@ -12933,7 +13055,7 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
12933
13055
  await logout();
12934
13056
  throw new Error('Token refresh failed. Please login again.');
12935
13057
  }
12936
- }, [proxyMode, user, accountData, accountInfo, contact, contactId, notifyAuthStateChange, isNetworkError, logout]);
13058
+ }, [proxyMode, clientId, user, accountData, accountInfo, contact, contactId, notifyAuthStateChange, isNetworkError, logout]);
12937
13059
  const getAccount = useCallback(async (forceRefresh = false) => {
12938
13060
  try {
12939
13061
  if (proxyMode) {
@@ -13097,6 +13219,105 @@ collectionId, enableContactSync, enableInteractionTracking, interactionAppId, in
13097
13219
  clearInterval(intervalId);
13098
13220
  };
13099
13221
  }, [proxyMode, enableAutoRefresh, refreshCheckInterval, refreshThresholdPercent, token, user, refreshToken]);
13222
+ // Resolve clientId from the collection's defaultAuthKitId when not provided
13223
+ // explicitly. Most apps only pass `collectionId` — this means refresh-token
13224
+ // rotation and server-side logout revocation still work end-to-end.
13225
+ useEffect(() => {
13226
+ if (clientIdProp) {
13227
+ setResolvedClientId(clientIdProp);
13228
+ return;
13229
+ }
13230
+ if (!collectionId)
13231
+ return;
13232
+ let cancelled = false;
13233
+ (async () => {
13234
+ try {
13235
+ const { getDefaultAuthKitId } = await Promise.resolve().then(function () { return defaultAuthKit; });
13236
+ const id = await getDefaultAuthKitId(collectionId);
13237
+ if (!cancelled && id)
13238
+ setResolvedClientId(id);
13239
+ }
13240
+ catch (err) {
13241
+ console.warn('[AuthContext] Could not resolve default authKitId from collection:', err);
13242
+ }
13243
+ })();
13244
+ return () => { cancelled = true; };
13245
+ }, [clientIdProp, collectionId]);
13246
+ // Refresh-on-resume: fires when the app returns to the foreground.
13247
+ // - Capacitor: `App.addListener('appStateChange')` via dynamic import (no
13248
+ // runtime dep on web).
13249
+ // - Universal fallback: `document.visibilitychange` — also catches PWAs /
13250
+ // web tabs that have been backgrounded for hours.
13251
+ useEffect(() => {
13252
+ if (proxyMode || !enableAutoRefresh || !token || !user)
13253
+ return;
13254
+ // Default ON when we detect a native shell; OFF otherwise.
13255
+ const isNative = (() => {
13256
+ try {
13257
+ const cap = window.Capacitor;
13258
+ if (cap?.isNativePlatform?.() === true)
13259
+ return true;
13260
+ if (window.AuthKit?.isNative === true)
13261
+ return true;
13262
+ }
13263
+ catch { /* noop */ }
13264
+ return false;
13265
+ })();
13266
+ const enabled = refreshOnResume ?? isNative;
13267
+ if (!enabled)
13268
+ return;
13269
+ const maybeRefresh = async () => {
13270
+ try {
13271
+ const storedToken = await tokenStorage.getToken();
13272
+ if (!storedToken?.expiresAt)
13273
+ return;
13274
+ const remainingMs = storedToken.expiresAt - Date.now();
13275
+ const totalLifetimeMs = 7 * 24 * 60 * 60 * 1000;
13276
+ const percentUsed = ((totalLifetimeMs - remainingMs) / totalLifetimeMs) * 100;
13277
+ if (percentUsed < refreshThresholdPercent)
13278
+ return;
13279
+ await refreshToken().catch(() => { });
13280
+ }
13281
+ catch (err) {
13282
+ console.error('[AuthContext] Resume refresh check failed:', err);
13283
+ }
13284
+ };
13285
+ // Universal visibility listener
13286
+ const onVisibility = () => {
13287
+ if (document.visibilityState === 'visible')
13288
+ maybeRefresh();
13289
+ };
13290
+ document.addEventListener('visibilitychange', onVisibility);
13291
+ // Capacitor listener (optional)
13292
+ let capRemove;
13293
+ (async () => {
13294
+ try {
13295
+ // Only attempt to load @capacitor/app in an actual native shell.
13296
+ // The specifier is obfuscated so bundlers (Vite/webpack) don't try
13297
+ // to statically resolve an optional peer dependency at build time.
13298
+ const isNative = typeof window !== 'undefined' &&
13299
+ (window.Capacitor?.isNativePlatform?.() === true ||
13300
+ window.AuthKit?.isNative === true);
13301
+ if (!isNative)
13302
+ return;
13303
+ const specifier = ['@capacitor', 'app'].join('/');
13304
+ const dynamicImport = new Function('s', 'return import(s)');
13305
+ const mod = await dynamicImport(specifier);
13306
+ const handle = await mod.App.addListener('appStateChange', (state) => {
13307
+ if (state.isActive)
13308
+ maybeRefresh();
13309
+ });
13310
+ capRemove = () => handle?.remove?.();
13311
+ }
13312
+ catch {
13313
+ // @capacitor/app not installed or unavailable — visibilitychange handles it.
13314
+ }
13315
+ })();
13316
+ return () => {
13317
+ document.removeEventListener('visibilitychange', onVisibility);
13318
+ capRemove?.();
13319
+ };
13320
+ }, [proxyMode, enableAutoRefresh, refreshOnResume, refreshThresholdPercent, token, user, refreshToken]);
13100
13321
  const value = {
13101
13322
  user,
13102
13323
  token,
@@ -13358,8 +13579,22 @@ const loadGoogleIdentityServices = () => {
13358
13579
  document.head.appendChild(script);
13359
13580
  });
13360
13581
  };
13582
+ // Helper to detect a Capacitor native runtime (iOS/Android shell, not the web)
13583
+ const isCapacitorNative = () => {
13584
+ const cap = window.Capacitor;
13585
+ if (!cap)
13586
+ return false;
13587
+ // isNativePlatform() is the modern API; fall back to platform string for older cores
13588
+ if (typeof cap.isNativePlatform === 'function')
13589
+ return cap.isNativePlatform();
13590
+ return cap.platform === 'ios' || cap.platform === 'android';
13591
+ };
13361
13592
  // Helper to detect WebView environments (Android/iOS)
13362
13593
  const detectWebView = () => {
13594
+ // Capacitor apps run inside a WebView (WKWebView/Android WebView) but don't
13595
+ // always set the `wv` UA token — treat them as WebView explicitly.
13596
+ if (isCapacitorNative())
13597
+ return true;
13363
13598
  const ua = navigator.userAgent;
13364
13599
  // Android WebView detection
13365
13600
  if (/Android/i.test(ua)) {
@@ -13467,7 +13702,7 @@ const checkSilentGoogleSignIn = async (clientId, googleClientId) => {
13467
13702
  });
13468
13703
  };
13469
13704
  // getFriendlyErrorMessage is now imported from ../utils/errorHandling
13470
- const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAuthSuccess, onAuthError, onRedirect, enabledProviders = ['email', 'google', 'phone'], initialMode, signupProminence, redirectUrl, theme = 'light', className, customization, skipConfigFetch = false, minimal = false, logger, proxyMode = false, collectionId, disableConfigCache = false, enableSilentGoogleSignIn = false, whatsappReply, whatsappPrefillMessage, }) => {
13705
+ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAuthSuccess, onAuthError, onRedirect, enabledProviders = ['email', 'google', 'phone'], initialMode, signupProminence, redirectUrl, theme = 'light', className, customization, skipConfigFetch = false, minimal = false, logger, proxyMode = false, collectionId, disableConfigCache = false, enableSilentGoogleSignIn = false, nativeAuth, enableSilentNativeSignIn = false, whatsappReply, whatsappPrefillMessage, }) => {
13471
13706
  // Resolve signup prominence from props, customization, config, or default
13472
13707
  const resolvedSignupProminence = signupProminence || customization?.signupProminence || 'minimal';
13473
13708
  // Determine initial mode based on signupProminence setting
@@ -13767,23 +14002,28 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
13767
14002
  };
13768
14003
  fetchSchema();
13769
14004
  }, [collectionId, sdkReady]);
13770
- // Silent Google Sign-In check on mount (for native Android apps)
13771
- // When enabled, checks if user is already signed in via Google on the native side
14005
+ // Silent Sign-In check on mount (for native apps).
14006
+ // Prefers the injected `nativeAuth.checkSignIn` adapter (Capacitor) when
14007
+ // `enableSilentNativeSignIn` is set; otherwise falls back to the legacy
14008
+ // `window.AuthKit` bridge when `enableSilentGoogleSignIn` is set.
14009
+ const wantsSilentNative = enableSilentNativeSignIn && !!nativeAuth?.checkSignIn;
13772
14010
  useEffect(() => {
13773
- if (!enableSilentGoogleSignIn || silentSignInChecked || !sdkReady || auth.isAuthenticated) {
14011
+ if ((!enableSilentGoogleSignIn && !wantsSilentNative) || silentSignInChecked || !sdkReady || auth.isAuthenticated) {
13774
14012
  return;
13775
14013
  }
13776
14014
  const googleClientId = config?.googleClientId || DEFAULT_GOOGLE_CLIENT_ID;
13777
14015
  const performSilentSignIn = async () => {
13778
14016
  try {
13779
- const result = await checkSilentGoogleSignIn(clientId, googleClientId);
14017
+ const result = wantsSilentNative
14018
+ ? await nativeAuth.checkSignIn({ serverClientId: googleClientId })
14019
+ : await checkSilentGoogleSignIn(clientId, googleClientId);
13780
14020
  setSilentSignInChecked(true);
13781
14021
  if (result?.isSignedIn && result.idToken) {
13782
14022
  setLoading(true);
13783
14023
  try {
13784
14024
  const authResponse = await api.loginWithGoogle(result.idToken);
13785
14025
  if (authResponse.token) {
13786
- await auth.login(authResponse.token, authResponse.user, authResponse.accountData, false, getExpirationFromResponse(authResponse));
14026
+ await auth.login(authResponse.token, authResponse.user, authResponse.accountData, false, getExpirationFromResponse(authResponse), authResponse.refreshToken, authResponse.refreshTokenExpiresAt);
13787
14027
  setAuthSuccess(true);
13788
14028
  setSuccessMessage('Signed in automatically with Google!');
13789
14029
  onAuthSuccess(authResponse.token, authResponse.user, authResponse.accountData);
@@ -13802,7 +14042,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
13802
14042
  }
13803
14043
  };
13804
14044
  performSilentSignIn();
13805
- }, [enableSilentGoogleSignIn, silentSignInChecked, sdkReady, auth.isAuthenticated, clientId, config?.googleClientId, api, auth, onAuthSuccess]);
14045
+ }, [enableSilentGoogleSignIn, wantsSilentNative, nativeAuth, silentSignInChecked, sdkReady, auth.isAuthenticated, clientId, config?.googleClientId, api, auth, onAuthSuccess]);
13806
14046
  // Reset showEmailForm when mode changes away from login/register
13807
14047
  useEffect(() => {
13808
14048
  if (mode !== 'login' && mode !== 'register') {
@@ -13878,7 +14118,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
13878
14118
  if ((verificationMode === 'verify-auto-login' || verificationMode === 'immediate') && response.token) {
13879
14119
  // Auto-login modes: Log the user in immediately if token is provided
13880
14120
  // Always await - auth.login now waits for parent ack automatically in iframe mode
13881
- await auth.login(response.token, response.user, response.accountData, true, getExpirationFromResponse(response));
14121
+ await auth.login(response.token, response.user, response.accountData, true, getExpirationFromResponse(response), response.refreshToken, response.refreshTokenExpiresAt);
13882
14122
  setUrlAuthProcessing(false); // Clear processing state before redirect/success
13883
14123
  if (redirectUrl) {
13884
14124
  // Redirect to clean URL and resume flow
@@ -13938,7 +14178,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
13938
14178
  // Auto-login with magic link if token is provided
13939
14179
  if (response.token) {
13940
14180
  // Always await - auth.login now waits for parent ack automatically in iframe mode
13941
- await auth.login(response.token, response.user, response.accountData, true, getExpirationFromResponse(response));
14181
+ await auth.login(response.token, response.user, response.accountData, true, getExpirationFromResponse(response), response.refreshToken, response.refreshTokenExpiresAt);
13942
14182
  setUrlAuthProcessing(false); // Clear processing state before redirect/success
13943
14183
  if (redirectUrl) {
13944
14184
  // Redirect to clean URL and resume flow
@@ -14013,7 +14253,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
14013
14253
  log.log('Google OAuth code exchange response:', { hasToken: !!response.token, hasUser: !!response.user, isNewUser: response.isNewUser });
14014
14254
  if (response.token) {
14015
14255
  // Await login to ensure token is persisted before any navigation
14016
- await auth.login(response.token, response.user, response.accountData, response.isNewUser, getExpirationFromResponse(response));
14256
+ await auth.login(response.token, response.user, response.accountData, response.isNewUser, getExpirationFromResponse(response), response.refreshToken, response.refreshTokenExpiresAt);
14017
14257
  setAuthSuccess(true);
14018
14258
  setSuccessMessage('Google login successful!');
14019
14259
  onAuthSuccess(response.token, response.user, response.accountData);
@@ -14067,7 +14307,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
14067
14307
  // Handle different verification modes
14068
14308
  if (verificationMode === 'immediate' && response.token) {
14069
14309
  // Immediate mode: Log in right away if token is provided (isNewUser=true for registration)
14070
- await auth.login(response.token, response.user, response.accountData, true, getExpirationFromResponse(response));
14310
+ await auth.login(response.token, response.user, response.accountData, true, getExpirationFromResponse(response), response.refreshToken, response.refreshTokenExpiresAt);
14071
14311
  setAuthSuccess(true);
14072
14312
  const deadline = response.emailVerificationDeadline
14073
14313
  ? new Date(response.emailVerificationDeadline).toLocaleString()
@@ -14129,7 +14369,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
14129
14369
  setLoading(false);
14130
14370
  return;
14131
14371
  }
14132
- await auth.login(response.token, response.user, response.accountData, false, getExpirationFromResponse(response));
14372
+ await auth.login(response.token, response.user, response.accountData, false, getExpirationFromResponse(response), response.refreshToken, response.refreshTokenExpiresAt);
14133
14373
  setAuthSuccess(true);
14134
14374
  setSuccessMessage('Login successful!');
14135
14375
  onAuthSuccess(response.token, response.user, response.accountData);
@@ -14259,6 +14499,55 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
14259
14499
  }
14260
14500
  });
14261
14501
  };
14502
+ // Finish a login once a backend AuthResponse has been obtained (shared by the
14503
+ // native adapter paths for Google and Apple).
14504
+ const completeNativeLogin = async (authResponse, successLabel) => {
14505
+ if (!authResponse.token) {
14506
+ throw new Error('Authentication failed - no token received');
14507
+ }
14508
+ await auth.login(authResponse.token, authResponse.user, authResponse.accountData, authResponse.isNewUser, getExpirationFromResponse(authResponse));
14509
+ setAuthSuccess(true);
14510
+ setSuccessMessage(successLabel);
14511
+ onAuthSuccess(authResponse.token, authResponse.user, authResponse.accountData);
14512
+ };
14513
+ // Sign in with Apple. Apple is native-only in this kit today: it requires an
14514
+ // injected `nativeAuth` adapter (e.g. a Capacitor plugin) that resolves an
14515
+ // Apple identity token. There is no in-browser fallback yet.
14516
+ const handleAppleLogin = async () => {
14517
+ if (!nativeAuth?.signInWithApple) {
14518
+ log.warn('Apple sign-in requested but no nativeAuth.signInWithApple adapter is configured');
14519
+ setError('Apple Sign-In is not available in this environment.');
14520
+ onAuthError?.(new Error('No nativeAuth.signInWithApple adapter configured'));
14521
+ return;
14522
+ }
14523
+ setLoading(true);
14524
+ setError(undefined);
14525
+ try {
14526
+ log.log('Using native adapter for Apple Sign-In');
14527
+ const result = await nativeAuth.signInWithApple({
14528
+ clientId: config?.appleClientId,
14529
+ scopes: ['name', 'email'],
14530
+ });
14531
+ if (!result?.idToken) {
14532
+ throw new Error('Apple Sign-In did not return an identity token');
14533
+ }
14534
+ const authResponse = await api.loginWithApple(result.idToken, {
14535
+ authorizationCode: result.authorizationCode,
14536
+ nonce: result.nonce,
14537
+ appleUserInfo: result.email || result.name ? { email: result.email, name: result.name } : undefined,
14538
+ });
14539
+ log.log('Native Apple login response:', { hasToken: !!authResponse.token, isNewUser: authResponse.isNewUser });
14540
+ await completeNativeLogin(authResponse, 'Apple login successful!');
14541
+ }
14542
+ catch (err) {
14543
+ log.error('Apple Sign-In failed:', err);
14544
+ setError(getFriendlyErrorMessage(err));
14545
+ onAuthError?.(err instanceof Error ? err : new Error(getFriendlyErrorMessage(err)));
14546
+ }
14547
+ finally {
14548
+ setLoading(false);
14549
+ }
14550
+ };
14262
14551
  const handleGoogleLogin = async () => {
14263
14552
  const hasCustomGoogleClientId = !!config?.googleClientId;
14264
14553
  const googleClientId = config?.googleClientId || DEFAULT_GOOGLE_CLIENT_ID;
@@ -14288,6 +14577,32 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
14288
14577
  setLoading(true);
14289
14578
  setError(undefined);
14290
14579
  try {
14580
+ // Priority 0: injected Promise-based native adapter (Capacitor, etc.).
14581
+ // This is the clean path — await the plugin directly, no callback bridge.
14582
+ if (nativeAuth?.signInWithGoogle) {
14583
+ log.log('Using injected nativeAuth adapter for Google Sign-In');
14584
+ try {
14585
+ const result = await nativeAuth.signInWithGoogle({
14586
+ serverClientId: googleClientId,
14587
+ scopes: ['email', 'profile'],
14588
+ });
14589
+ if (!result?.idToken) {
14590
+ throw new Error('Google Sign-In did not return an ID token');
14591
+ }
14592
+ const authResponse = await api.loginWithGoogle(result.idToken);
14593
+ log.log('Native (adapter) Google login response:', { hasToken: !!authResponse.token, isNewUser: authResponse.isNewUser });
14594
+ await completeNativeLogin(authResponse, 'Google login successful!');
14595
+ }
14596
+ catch (err) {
14597
+ log.error('Adapter Google Sign-In failed:', err);
14598
+ setError(getFriendlyErrorMessage(err));
14599
+ onAuthError?.(err instanceof Error ? err : new Error(getFriendlyErrorMessage(err)));
14600
+ }
14601
+ finally {
14602
+ setLoading(false);
14603
+ }
14604
+ return;
14605
+ }
14291
14606
  if (nativeBridge) {
14292
14607
  log.log('Using native bridge for Google Sign-In');
14293
14608
  const callbackId = `google_auth_${Date.now()}`;
@@ -14306,7 +14621,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
14306
14621
  const authResponse = await api.loginWithGoogle(result.idToken);
14307
14622
  log.log('Native Google login response:', { hasToken: !!authResponse.token, hasUser: !!authResponse.user, isNewUser: authResponse.isNewUser });
14308
14623
  if (authResponse.token) {
14309
- await auth.login(authResponse.token, authResponse.user, authResponse.accountData, authResponse.isNewUser, getExpirationFromResponse(authResponse));
14624
+ await auth.login(authResponse.token, authResponse.user, authResponse.accountData, authResponse.isNewUser, getExpirationFromResponse(authResponse), authResponse.refreshToken, authResponse.refreshTokenExpiresAt);
14310
14625
  setAuthSuccess(true);
14311
14626
  setSuccessMessage('Google login successful!');
14312
14627
  onAuthSuccess(authResponse.token, authResponse.user, authResponse.accountData);
@@ -14526,7 +14841,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
14526
14841
  log.log('Popup Google login response:', { hasToken: !!authResponse.token, hasUser: !!authResponse.user, isNewUser: authResponse.isNewUser });
14527
14842
  if (authResponse.token) {
14528
14843
  // Google OAuth can be login or signup - use isNewUser flag from backend if available
14529
- await auth.login(authResponse.token, authResponse.user, authResponse.accountData, authResponse.isNewUser, getExpirationFromResponse(authResponse));
14844
+ await auth.login(authResponse.token, authResponse.user, authResponse.accountData, authResponse.isNewUser, getExpirationFromResponse(authResponse), authResponse.refreshToken, authResponse.refreshTokenExpiresAt);
14530
14845
  setAuthSuccess(true);
14531
14846
  setSuccessMessage('Google login successful!');
14532
14847
  onAuthSuccess(authResponse.token, authResponse.user, authResponse.accountData);
@@ -14576,7 +14891,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
14576
14891
  log.log('OneTap Google login response:', { hasToken: !!authResponse.token, hasUser: !!authResponse.user, isNewUser: authResponse.isNewUser });
14577
14892
  if (authResponse.token) {
14578
14893
  // Google OAuth can be login or signup - use isNewUser flag from backend if available
14579
- await auth.login(authResponse.token, authResponse.user, authResponse.accountData, authResponse.isNewUser, getExpirationFromResponse(authResponse));
14894
+ await auth.login(authResponse.token, authResponse.user, authResponse.accountData, authResponse.isNewUser, getExpirationFromResponse(authResponse), authResponse.refreshToken, authResponse.refreshTokenExpiresAt);
14580
14895
  setAuthSuccess(true);
14581
14896
  setSuccessMessage('Google login successful!');
14582
14897
  onAuthSuccess(authResponse.token, authResponse.user, authResponse.accountData);
@@ -14658,7 +14973,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
14658
14973
  // Phone auth is an INTERACTIVE flow (user entered OTP in UI)
14659
14974
  // Unlike deep-link flows (email verification, magic link), there's no URL token to clean up
14660
14975
  // Do NOT auto-redirect - let the parent app control the next step (profile completion, etc.)
14661
- await auth.login(response.token, response.user, response.accountData, response.isNewUser, getExpirationFromResponse(response));
14976
+ await auth.login(response.token, response.user, response.accountData, response.isNewUser, getExpirationFromResponse(response), response.refreshToken, response.refreshTokenExpiresAt);
14662
14977
  setAuthSuccess(true);
14663
14978
  setSuccessMessage('Phone verified! You are now logged in.');
14664
14979
  onAuthSuccess(response.token, response.user, response.accountData);
@@ -14948,7 +15263,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
14948
15263
  throw Object.assign(new Error(resultErrorMessage), session);
14949
15264
  }
14950
15265
  if (session?.token && session.user) {
14951
- await auth.login(session.token, session.user, session.accountData, true, getExpirationFromResponse(session));
15266
+ await auth.login(session.token, session.user, session.accountData, true, getExpirationFromResponse(session), session.refreshToken, session.refreshTokenExpiresAt);
14952
15267
  if (!proxyMode) {
14953
15268
  onAuthSuccess(session.token, session.user, session.accountData);
14954
15269
  }
@@ -15278,11 +15593,11 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
15278
15593
  const hasEmailProvider = actualProviders.includes('email');
15279
15594
  // If email provider is not enabled, only show provider buttons (no email/password form)
15280
15595
  if (!hasEmailProvider) {
15281
- return (jsx(ProviderButtons, { enabledProviders: actualProviders, providerOrder: providerOrder, onGoogleLogin: handleGoogleLogin, onPhoneLogin: () => setMode('phone'), onMagicLinkLogin: () => setMode('magic-link'), onWhatsAppLogin: () => setMode('whatsapp'), loading: loading }));
15596
+ return (jsx(ProviderButtons, { enabledProviders: actualProviders, providerOrder: providerOrder, onGoogleLogin: handleGoogleLogin, onAppleLogin: handleAppleLogin, onPhoneLogin: () => setMode('phone'), onMagicLinkLogin: () => setMode('magic-link'), onWhatsAppLogin: () => setMode('whatsapp'), loading: loading }));
15282
15597
  }
15283
15598
  // Button mode: show provider selection first, then email form if email is selected
15284
15599
  if (emailDisplayMode === 'button' && !showEmailForm) {
15285
- return (jsx(ProviderButtons, { enabledProviders: actualProviders, providerOrder: providerOrder, onEmailLogin: () => setShowEmailForm(true), onGoogleLogin: handleGoogleLogin, onPhoneLogin: () => setMode('phone'), onMagicLinkLogin: () => setMode('magic-link'), onWhatsAppLogin: () => setMode('whatsapp'), loading: loading }));
15600
+ return (jsx(ProviderButtons, { enabledProviders: actualProviders, providerOrder: providerOrder, onEmailLogin: () => setShowEmailForm(true), onGoogleLogin: handleGoogleLogin, onAppleLogin: handleAppleLogin, onPhoneLogin: () => setMode('phone'), onMagicLinkLogin: () => setMode('magic-link'), onWhatsAppLogin: () => setMode('whatsapp'), loading: loading }));
15286
15601
  }
15287
15602
  // Form mode or email button was clicked: show email form with other providers
15288
15603
  return (jsxs(Fragment, { children: [emailDisplayMode === 'button' && showEmailForm && (jsx("button", { onClick: () => setShowEmailForm(false), style: {
@@ -15301,7 +15616,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
15301
15616
  setShowResendVerification(false);
15302
15617
  setShowRequestNewReset(false);
15303
15618
  setError(undefined);
15304
- }, onForgotPassword: () => setMode('reset-password'), loading: loading, error: error, signupProminence: resolvedSignupProminence, signupRedirectUrl: config?.signupRedirectUrl || customization?.signupRedirectUrl, schema: contactSchema, registrationFieldsConfig: config?.registrationFields, additionalFields: config?.signupAdditionalFields }), emailDisplayMode === 'form' && actualProviders.length > 1 && (jsx(ProviderButtons, { enabledProviders: actualProviders.filter((p) => p !== 'email'), providerOrder: providerOrder, onGoogleLogin: handleGoogleLogin, onPhoneLogin: () => setMode('phone'), onMagicLinkLogin: () => setMode('magic-link'), onWhatsAppLogin: () => setMode('whatsapp'), loading: loading }))] }));
15619
+ }, onForgotPassword: () => setMode('reset-password'), loading: loading, error: error, signupProminence: resolvedSignupProminence, signupRedirectUrl: config?.signupRedirectUrl || customization?.signupRedirectUrl, schema: contactSchema, registrationFieldsConfig: config?.registrationFields, additionalFields: config?.signupAdditionalFields }), emailDisplayMode === 'form' && actualProviders.length > 1 && (jsx(ProviderButtons, { enabledProviders: actualProviders.filter((p) => p !== 'email'), providerOrder: providerOrder, onGoogleLogin: handleGoogleLogin, onAppleLogin: handleAppleLogin, onPhoneLogin: () => setMode('phone'), onMagicLinkLogin: () => setMode('magic-link'), onWhatsAppLogin: () => setMode('whatsapp'), loading: loading }))] }));
15305
15620
  })()] })) })) : null }));
15306
15621
  };
15307
15622
 
@@ -16442,5 +16757,11 @@ async function setDefaultAuthKitId(collectionId, authKitId) {
16442
16757
  });
16443
16758
  }
16444
16759
 
16445
- export { AccountManagement, AuthProvider, AuthUIPreview, SmartlinksAuthUI as FirebaseAuthUI, ProtectedRoute, SchemaFieldRenderer, SmartlinksAuthUI, SmartlinksFrame, buildIframeSrc, evaluateConditions, getDefaultAuthKitId, getEditableFields, getErrorCode, getErrorStatusCode, getFriendlyErrorMessage, getRegistrationFields, isAdminFromRoles, isAuthError, isConflictError, isRateLimitError, isServerError, resolveFields, setDefaultAuthKitId, sortFieldsByPlacement, tokenStorage, useAdminDetection, useAuth, useIframeMessages, useIframeResize };
16760
+ var defaultAuthKit = /*#__PURE__*/Object.freeze({
16761
+ __proto__: null,
16762
+ getDefaultAuthKitId: getDefaultAuthKitId,
16763
+ setDefaultAuthKitId: setDefaultAuthKitId
16764
+ });
16765
+
16766
+ export { AccountManagement, AuthProvider, AuthUIPreview, SmartlinksAuthUI as FirebaseAuthUI, ProtectedRoute, SchemaFieldRenderer, SmartlinksAuthUI, SmartlinksFrame, buildIframeSrc, evaluateConditions, getDefaultAuthKitId, getEditableFields, getErrorCode, getErrorStatusCode, getFriendlyErrorMessage, getRegistrationFields, isAdminFromRoles, isAuthError, isConflictError, isRateLimitError, isServerError, resolveFields, setDefaultAuthKitId, setStorageAdapter, sortFieldsByPlacement, tokenStorage, useAdminDetection, useAuth, useIframeMessages, useIframeResize };
16446
16767
  //# sourceMappingURL=index.esm.js.map