@proveanything/smartlinks-auth-ui 0.1.28 → 0.1.29

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/api.d.ts CHANGED
@@ -19,6 +19,7 @@ export declare class AuthAPI {
19
19
  };
20
20
  tokenType?: 'id_token' | 'access_token';
21
21
  }): Promise<AuthResponse>;
22
+ loginWithGoogleCode(code: string, redirectUri: string): Promise<AuthResponse>;
22
23
  sendPhoneCode(phoneNumber: string): Promise<{
23
24
  verificationId: string;
24
25
  }>;
package/dist/api.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAGpF;;;GAGG;AACH,qBAAa,OAAO;IAClB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,UAAU,CAAC,CAAS;IAC5B,OAAO,CAAC,GAAG,CAAyC;gBAExC,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,UAAU;IAQtF,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAI7D,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;IAWnD,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAC7C,cAAc,CAAC,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAC;YAAC,OAAO,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QACjF,SAAS,CAAC,EAAE,UAAU,GAAG,cAAc,CAAC;KACzC,GAAG,OAAO,CAAC,YAAY,CAAC;IAmBnB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,cAAc,EAAE,MAAM,CAAA;KAAE,CAAC;IAIvE,eAAe,CACnB,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,YAAY,CAAC;IAIlB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAQxG,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAIlH,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAIzG,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IASzH,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAIhF,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAStH,WAAW,IAAI,OAAO,CAAC,YAAY,CAAC;IA2BpC,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAOjG,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAGlF"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAGpF;;;GAGG;AACH,qBAAa,OAAO;IAClB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,UAAU,CAAC,CAAS;IAC5B,OAAO,CAAC,GAAG,CAAyC;gBAExC,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,UAAU;IAQtF,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAI7D,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;IAWnD,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAC7C,cAAc,CAAC,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAC;YAAC,OAAO,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QACjF,SAAS,CAAC,EAAE,UAAU,GAAG,cAAc,CAAC;KACzC,GAAG,OAAO,CAAC,YAAY,CAAC;IAmBnB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAc7E,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,cAAc,EAAE,MAAM,CAAA;KAAE,CAAC;IAIvE,eAAe,CACnB,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,YAAY,CAAC;IAIlB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAQxG,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAIlH,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAIzG,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IASzH,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAIhF,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAStH,WAAW,IAAI,OAAO,CAAC,YAAY,CAAC;IA2BpC,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAOjG,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAGlF"}
@@ -1 +1 @@
1
- {"version":3,"file":"SmartlinksAuthUI.d.ts","sourceRoot":"","sources":["../../src/components/SmartlinksAuthUI.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAuC,MAAM,OAAO,CAAC;AAW5D,OAAO,KAAK,EAAE,qBAAqB,EAAuD,MAAM,UAAU,CAAC;AAqF3G,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAmlC5D,CAAC"}
1
+ {"version":3,"file":"SmartlinksAuthUI.d.ts","sourceRoot":"","sources":["../../src/components/SmartlinksAuthUI.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAuC,MAAM,OAAO,CAAC;AAW5D,OAAO,KAAK,EAAE,qBAAqB,EAAuD,MAAM,UAAU,CAAC;AA4G3G,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CA4qC5D,CAAC"}
package/dist/index.esm.js CHANGED
@@ -2,6 +2,7 @@ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import React, { useEffect, useState, useMemo, useRef, useCallback, createContext, useContext } from 'react';
3
3
  import * as smartlinks from '@proveanything/smartlinks';
4
4
  import { iframe } from '@proveanything/smartlinks';
5
+ import { post } from '@proveanything/smartlinks/dist/http';
5
6
 
6
7
  const AuthContainer = ({ children, theme = 'light', className = '', config, minimal = false, }) => {
7
8
  // Apply CSS variables for customization
@@ -10789,6 +10790,18 @@ class AuthAPI {
10789
10790
  // Pass token to SDK - backend verifies with Google
10790
10791
  return smartlinks.authKit.googleLogin(this.clientId, token);
10791
10792
  }
10793
+ async loginWithGoogleCode(code, redirectUri) {
10794
+ this.log.log('loginWithGoogleCode called:', {
10795
+ codeLength: code?.length,
10796
+ redirectUri,
10797
+ });
10798
+ // Exchange authorization code for tokens via backend
10799
+ // Use direct HTTP call since SDK may not have this method in authKit namespace yet
10800
+ return post(`/api/v1/authkit/${this.clientId}/google-code`, {
10801
+ code,
10802
+ redirectUri,
10803
+ });
10804
+ }
10792
10805
  async sendPhoneCode(phoneNumber) {
10793
10806
  return smartlinks.authKit.sendPhoneCode(this.clientId, phoneNumber);
10794
10807
  }
@@ -12176,6 +12189,28 @@ const loadGoogleIdentityServices = () => {
12176
12189
  document.head.appendChild(script);
12177
12190
  });
12178
12191
  };
12192
+ // Helper to detect WebView environments (Android/iOS)
12193
+ const detectWebView = () => {
12194
+ const ua = navigator.userAgent;
12195
+ // Android WebView detection
12196
+ if (/Android/i.test(ua)) {
12197
+ // Modern Android WebViews include "wv" in UA string
12198
+ if (/\bwv\b/i.test(ua))
12199
+ return true;
12200
+ // Check for legacy Android bridge
12201
+ if (typeof window.Android !== 'undefined')
12202
+ return true;
12203
+ }
12204
+ // iOS WKWebView detection
12205
+ if (/iPhone|iPad|iPod/i.test(ua)) {
12206
+ const hasWebKitHandlers = !!window.webkit?.messageHandlers;
12207
+ const isSafari = !!window.safari;
12208
+ // WKWebView has webkit handlers but no safari object
12209
+ if (hasWebKitHandlers && !isSafari)
12210
+ return true;
12211
+ }
12212
+ return false;
12213
+ };
12179
12214
  // Helper to convert generic SDK errors to user-friendly messages
12180
12215
  const getFriendlyErrorMessage = (errorMessage) => {
12181
12216
  // Check for common HTTP status codes in the error message
@@ -12437,8 +12472,15 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
12437
12472
  const params = getUrlParams();
12438
12473
  const urlMode = params.get('mode');
12439
12474
  const token = params.get('token');
12440
- log.log('URL params detected:', { urlMode, token, hash: window.location.hash, search: window.location.search });
12441
- if (urlMode && token) {
12475
+ // Check for Google OAuth redirect callback
12476
+ const authCode = params.get('code');
12477
+ const state = params.get('state');
12478
+ log.log('URL params detected:', { urlMode, token, authCode: !!authCode, state: !!state, hash: window.location.hash, search: window.location.search });
12479
+ if (authCode && state) {
12480
+ // Google OAuth redirect callback
12481
+ handleGoogleAuthCodeCallback(authCode, state);
12482
+ }
12483
+ else if (urlMode && token) {
12442
12484
  handleURLBasedAuth(urlMode, token);
12443
12485
  }
12444
12486
  }, []);
@@ -12551,6 +12593,43 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
12551
12593
  setLoading(false);
12552
12594
  }
12553
12595
  };
12596
+ // Handle Google OAuth authorization code callback (from redirect flow)
12597
+ const handleGoogleAuthCodeCallback = async (code, stateParam) => {
12598
+ setLoading(true);
12599
+ setError(undefined);
12600
+ try {
12601
+ // Parse state to get context
12602
+ const state = JSON.parse(decodeURIComponent(stateParam));
12603
+ log.log('Google OAuth redirect callback:', { clientId: state.clientId, returnPath: state.returnPath });
12604
+ // Determine the redirect URI that was used (must match exactly)
12605
+ const redirectUri = state.redirectUri || window.location.origin + window.location.pathname;
12606
+ // Exchange authorization code for tokens
12607
+ const response = await api.loginWithGoogleCode(code, redirectUri);
12608
+ if (response.token) {
12609
+ auth.login(response.token, response.user, response.accountData, response.isNewUser, getExpirationFromResponse(response));
12610
+ setAuthSuccess(true);
12611
+ setSuccessMessage('Google login successful!');
12612
+ onAuthSuccess(response.token, response.user, response.accountData);
12613
+ }
12614
+ else {
12615
+ throw new Error('Authentication failed - no token received');
12616
+ }
12617
+ // Clean URL parameters
12618
+ const cleanUrl = window.location.origin + window.location.pathname + (state.returnPath?.includes('#') ? state.returnPath.split('?')[0] : window.location.hash.split('?')[0]);
12619
+ window.history.replaceState({}, document.title, cleanUrl);
12620
+ }
12621
+ catch (err) {
12622
+ const errorMessage = err instanceof Error ? err.message : 'Google login failed';
12623
+ setError(getFriendlyErrorMessage(errorMessage));
12624
+ onAuthError?.(err instanceof Error ? err : new Error(errorMessage));
12625
+ // Clean URL parameters even on error
12626
+ const cleanUrl = window.location.origin + window.location.pathname + window.location.hash.split('?')[0];
12627
+ window.history.replaceState({}, document.title, cleanUrl);
12628
+ }
12629
+ finally {
12630
+ setLoading(false);
12631
+ }
12632
+ };
12554
12633
  const handleEmailAuth = async (data) => {
12555
12634
  setLoading(true);
12556
12635
  setError(undefined);
@@ -12703,11 +12782,16 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
12703
12782
  // Use custom client ID from config, or fall back to default Smartlinks client ID
12704
12783
  const googleClientId = config?.googleClientId || DEFAULT_GOOGLE_CLIENT_ID;
12705
12784
  // Determine OAuth flow: default to 'oneTap' for better UX, but allow 'popup' for iframe compatibility
12706
- const oauthFlow = config?.googleOAuthFlow || 'oneTap';
12785
+ const configuredFlow = config?.googleOAuthFlow || 'oneTap';
12786
+ // For oneTap, automatically use redirect flow in WebView environments
12787
+ const isWebView = detectWebView();
12788
+ const oauthFlow = (configuredFlow === 'oneTap' && isWebView) ? 'redirect' : configuredFlow;
12707
12789
  // Log Google Auth configuration for debugging
12708
12790
  log.log('Google Auth initiated:', {
12709
12791
  googleClientId,
12710
- oauthFlow,
12792
+ configuredFlow,
12793
+ effectiveFlow: oauthFlow,
12794
+ isWebView,
12711
12795
  currentOrigin: window.location.origin,
12712
12796
  currentHref: window.location.href,
12713
12797
  configGoogleClientId: config?.googleClientId,
@@ -12723,7 +12807,37 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
12723
12807
  throw new Error('Google Identity Services failed to initialize');
12724
12808
  }
12725
12809
  log.log('Google Identity Services loaded, using flow:', oauthFlow);
12726
- if (oauthFlow === 'popup') {
12810
+ if (oauthFlow === 'redirect') {
12811
+ // Use OAuth2 redirect flow (works in WebViews and everywhere)
12812
+ if (!google.accounts.oauth2) {
12813
+ throw new Error('Google OAuth2 not available');
12814
+ }
12815
+ // Build the redirect URI - use current URL without query params
12816
+ const redirectUri = getRedirectUrl();
12817
+ // Build state parameter to preserve context across redirect
12818
+ const state = encodeURIComponent(JSON.stringify({
12819
+ clientId,
12820
+ returnPath: window.location.hash || window.location.pathname,
12821
+ redirectUri,
12822
+ }));
12823
+ log.log('Initializing Google OAuth2 redirect flow:', {
12824
+ client_id: googleClientId,
12825
+ scope: 'openid email profile',
12826
+ redirect_uri: redirectUri,
12827
+ state,
12828
+ });
12829
+ const client = google.accounts.oauth2.initCodeClient({
12830
+ client_id: googleClientId,
12831
+ scope: 'openid email profile',
12832
+ ux_mode: 'redirect',
12833
+ redirect_uri: redirectUri,
12834
+ state,
12835
+ });
12836
+ // This will navigate away from the page
12837
+ client.requestCode();
12838
+ return; // Don't set loading to false - we're navigating away
12839
+ }
12840
+ else if (oauthFlow === 'popup') {
12727
12841
  // Use OAuth2 popup flow (works in iframes but requires popup permission)
12728
12842
  if (!google.accounts.oauth2) {
12729
12843
  throw new Error('Google OAuth2 not available');
@@ -12814,7 +12928,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
12814
12928
  client.requestAccessToken();
12815
12929
  }
12816
12930
  else {
12817
- // Use One Tap / Sign-In button flow (smoother UX but doesn't work in iframes)
12931
+ // Use One Tap / Sign-In button flow (smoother UX but doesn't work in iframes or WebViews)
12818
12932
  log.log('Initializing Google OneTap flow:', {
12819
12933
  client_id: googleClientId,
12820
12934
  origin: window.location.origin,
@@ -12847,8 +12961,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
12847
12961
  },
12848
12962
  auto_select: false,
12849
12963
  cancel_on_tap_outside: true,
12850
- // Note: use_fedcm_for_prompt omitted - requires Permissions-Policy header on hosting server
12851
- // Will be needed when FedCM becomes mandatory in the future
12964
+ use_fedcm_for_prompt: true, // Enable FedCM for future browser compatibility
12852
12965
  });
12853
12966
  // Use timeout fallback instead of deprecated notification methods
12854
12967
  // (isNotDisplayed/isSkippedMoment will stop working when FedCM becomes mandatory)