@proveanything/smartlinks-auth-ui 0.1.28 → 0.1.30
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 +1 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/components/SmartlinksAuthUI.d.ts.map +1 -1
- package/dist/index.esm.js +158 -36
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +158 -36
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +13 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
var jsxRuntime = require('react/jsx-runtime');
|
|
4
4
|
var React = require('react');
|
|
5
5
|
var smartlinks = require('@proveanything/smartlinks');
|
|
6
|
+
var http = require('@proveanything/smartlinks/dist/http');
|
|
6
7
|
|
|
7
8
|
function _interopNamespaceDefault(e) {
|
|
8
9
|
var n = Object.create(null);
|
|
@@ -10809,6 +10810,18 @@ class AuthAPI {
|
|
|
10809
10810
|
// Pass token to SDK - backend verifies with Google
|
|
10810
10811
|
return smartlinks__namespace.authKit.googleLogin(this.clientId, token);
|
|
10811
10812
|
}
|
|
10813
|
+
async loginWithGoogleCode(code, redirectUri) {
|
|
10814
|
+
this.log.log('loginWithGoogleCode called:', {
|
|
10815
|
+
codeLength: code?.length,
|
|
10816
|
+
redirectUri,
|
|
10817
|
+
});
|
|
10818
|
+
// Exchange authorization code for tokens via backend
|
|
10819
|
+
// Use direct HTTP call since SDK may not have this method in authKit namespace yet
|
|
10820
|
+
return http.post(`/api/v1/authkit/${this.clientId}/google-code`, {
|
|
10821
|
+
code,
|
|
10822
|
+
redirectUri,
|
|
10823
|
+
});
|
|
10824
|
+
}
|
|
10812
10825
|
async sendPhoneCode(phoneNumber) {
|
|
10813
10826
|
return smartlinks__namespace.authKit.sendPhoneCode(this.clientId, phoneNumber);
|
|
10814
10827
|
}
|
|
@@ -12196,6 +12209,28 @@ const loadGoogleIdentityServices = () => {
|
|
|
12196
12209
|
document.head.appendChild(script);
|
|
12197
12210
|
});
|
|
12198
12211
|
};
|
|
12212
|
+
// Helper to detect WebView environments (Android/iOS)
|
|
12213
|
+
const detectWebView = () => {
|
|
12214
|
+
const ua = navigator.userAgent;
|
|
12215
|
+
// Android WebView detection
|
|
12216
|
+
if (/Android/i.test(ua)) {
|
|
12217
|
+
// Modern Android WebViews include "wv" in UA string
|
|
12218
|
+
if (/\bwv\b/i.test(ua))
|
|
12219
|
+
return true;
|
|
12220
|
+
// Check for legacy Android bridge
|
|
12221
|
+
if (typeof window.Android !== 'undefined')
|
|
12222
|
+
return true;
|
|
12223
|
+
}
|
|
12224
|
+
// iOS WKWebView detection
|
|
12225
|
+
if (/iPhone|iPad|iPod/i.test(ua)) {
|
|
12226
|
+
const hasWebKitHandlers = !!window.webkit?.messageHandlers;
|
|
12227
|
+
const isSafari = !!window.safari;
|
|
12228
|
+
// WKWebView has webkit handlers but no safari object
|
|
12229
|
+
if (hasWebKitHandlers && !isSafari)
|
|
12230
|
+
return true;
|
|
12231
|
+
}
|
|
12232
|
+
return false;
|
|
12233
|
+
};
|
|
12199
12234
|
// Helper to convert generic SDK errors to user-friendly messages
|
|
12200
12235
|
const getFriendlyErrorMessage = (errorMessage) => {
|
|
12201
12236
|
// Check for common HTTP status codes in the error message
|
|
@@ -12223,7 +12258,7 @@ const getFriendlyErrorMessage = (errorMessage) => {
|
|
|
12223
12258
|
// Return original message if no pattern matches
|
|
12224
12259
|
return errorMessage;
|
|
12225
12260
|
};
|
|
12226
|
-
const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAuthSuccess, onAuthError, enabledProviders = ['email', 'google', 'phone'], initialMode = 'login', redirectUrl, theme = 'auto', className, customization, skipConfigFetch = false, minimal = false, logger, proxyMode = false, collectionId, }) => {
|
|
12261
|
+
const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAuthSuccess, onAuthError, enabledProviders = ['email', 'google', 'phone'], initialMode = 'login', redirectUrl, theme = 'auto', className, customization, skipConfigFetch = false, minimal = false, logger, proxyMode = false, collectionId, disableConfigCache = false, }) => {
|
|
12227
12262
|
const [mode, setMode] = React.useState(initialMode);
|
|
12228
12263
|
const [loading, setLoading] = React.useState(false);
|
|
12229
12264
|
const [error, setError] = React.useState();
|
|
@@ -12364,31 +12399,38 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
12364
12399
|
return;
|
|
12365
12400
|
}
|
|
12366
12401
|
try {
|
|
12367
|
-
// Check localStorage cache first
|
|
12368
12402
|
const cacheKey = `auth_ui_config_${clientId}`;
|
|
12369
|
-
|
|
12370
|
-
if (
|
|
12371
|
-
const
|
|
12372
|
-
|
|
12373
|
-
|
|
12374
|
-
|
|
12375
|
-
|
|
12376
|
-
|
|
12377
|
-
|
|
12378
|
-
|
|
12379
|
-
|
|
12380
|
-
|
|
12381
|
-
console.log('[SmartlinksAuthUI]
|
|
12382
|
-
|
|
12383
|
-
config
|
|
12384
|
-
|
|
12385
|
-
|
|
12386
|
-
|
|
12387
|
-
|
|
12388
|
-
|
|
12389
|
-
|
|
12403
|
+
// Check localStorage cache first (unless caching is disabled)
|
|
12404
|
+
if (!disableConfigCache) {
|
|
12405
|
+
const cached = localStorage.getItem(cacheKey);
|
|
12406
|
+
if (cached) {
|
|
12407
|
+
const { config: cachedConfig, timestamp } = JSON.parse(cached);
|
|
12408
|
+
const age = Date.now() - timestamp;
|
|
12409
|
+
// Use cache if less than 1 hour old
|
|
12410
|
+
if (age < 3600000) {
|
|
12411
|
+
console.log('[SmartlinksAuthUI] 📦 Using cached config (age:', Math.round(age / 1000), 'seconds)');
|
|
12412
|
+
setConfig({ ...cachedConfig, ...customization });
|
|
12413
|
+
setConfigLoading(false);
|
|
12414
|
+
// Fetch in background to update cache
|
|
12415
|
+
console.log('[SmartlinksAuthUI] 🔄 Background refresh of config via SDK...');
|
|
12416
|
+
api.fetchConfig().then(freshConfig => {
|
|
12417
|
+
console.log('[SmartlinksAuthUI] ✅ Background config refresh complete');
|
|
12418
|
+
localStorage.setItem(cacheKey, JSON.stringify({
|
|
12419
|
+
config: freshConfig,
|
|
12420
|
+
timestamp: Date.now()
|
|
12421
|
+
}));
|
|
12422
|
+
// Update config if it changed
|
|
12423
|
+
setConfig({ ...freshConfig, ...customization });
|
|
12424
|
+
}).catch(err => {
|
|
12425
|
+
console.log('[SmartlinksAuthUI] ❌ Background config refresh failed:', err);
|
|
12426
|
+
});
|
|
12427
|
+
return;
|
|
12428
|
+
}
|
|
12390
12429
|
}
|
|
12391
12430
|
}
|
|
12431
|
+
else {
|
|
12432
|
+
console.log('[SmartlinksAuthUI] ⚠️ Config caching disabled, fetching fresh config');
|
|
12433
|
+
}
|
|
12392
12434
|
// Fetch from API
|
|
12393
12435
|
console.log('[SmartlinksAuthUI] 🌐 Fetching config via SDK for clientId:', clientId, 'proxyMode:', proxyMode);
|
|
12394
12436
|
log.log('Fetching config from API for clientId:', clientId);
|
|
@@ -12398,11 +12440,13 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
12398
12440
|
// Merge with customization props (props take precedence)
|
|
12399
12441
|
const mergedConfig = { ...fetchedConfig, ...customization };
|
|
12400
12442
|
setConfig(mergedConfig);
|
|
12401
|
-
// Cache the fetched config
|
|
12402
|
-
|
|
12403
|
-
|
|
12404
|
-
|
|
12405
|
-
|
|
12443
|
+
// Cache the fetched config (unless caching is disabled)
|
|
12444
|
+
if (!disableConfigCache) {
|
|
12445
|
+
localStorage.setItem(cacheKey, JSON.stringify({
|
|
12446
|
+
config: fetchedConfig,
|
|
12447
|
+
timestamp: Date.now()
|
|
12448
|
+
}));
|
|
12449
|
+
}
|
|
12406
12450
|
}
|
|
12407
12451
|
catch (err) {
|
|
12408
12452
|
console.log('[SmartlinksAuthUI] ❌ Config fetch failed:', err);
|
|
@@ -12457,8 +12501,15 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
12457
12501
|
const params = getUrlParams();
|
|
12458
12502
|
const urlMode = params.get('mode');
|
|
12459
12503
|
const token = params.get('token');
|
|
12460
|
-
|
|
12461
|
-
|
|
12504
|
+
// Check for Google OAuth redirect callback
|
|
12505
|
+
const authCode = params.get('code');
|
|
12506
|
+
const state = params.get('state');
|
|
12507
|
+
log.log('URL params detected:', { urlMode, token, authCode: !!authCode, state: !!state, hash: window.location.hash, search: window.location.search });
|
|
12508
|
+
if (authCode && state) {
|
|
12509
|
+
// Google OAuth redirect callback
|
|
12510
|
+
handleGoogleAuthCodeCallback(authCode, state);
|
|
12511
|
+
}
|
|
12512
|
+
else if (urlMode && token) {
|
|
12462
12513
|
handleURLBasedAuth(urlMode, token);
|
|
12463
12514
|
}
|
|
12464
12515
|
}, []);
|
|
@@ -12571,6 +12622,43 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
12571
12622
|
setLoading(false);
|
|
12572
12623
|
}
|
|
12573
12624
|
};
|
|
12625
|
+
// Handle Google OAuth authorization code callback (from redirect flow)
|
|
12626
|
+
const handleGoogleAuthCodeCallback = async (code, stateParam) => {
|
|
12627
|
+
setLoading(true);
|
|
12628
|
+
setError(undefined);
|
|
12629
|
+
try {
|
|
12630
|
+
// Parse state to get context
|
|
12631
|
+
const state = JSON.parse(decodeURIComponent(stateParam));
|
|
12632
|
+
log.log('Google OAuth redirect callback:', { clientId: state.clientId, returnPath: state.returnPath });
|
|
12633
|
+
// Determine the redirect URI that was used (must match exactly)
|
|
12634
|
+
const redirectUri = state.redirectUri || window.location.origin + window.location.pathname;
|
|
12635
|
+
// Exchange authorization code for tokens
|
|
12636
|
+
const response = await api.loginWithGoogleCode(code, redirectUri);
|
|
12637
|
+
if (response.token) {
|
|
12638
|
+
auth.login(response.token, response.user, response.accountData, response.isNewUser, getExpirationFromResponse(response));
|
|
12639
|
+
setAuthSuccess(true);
|
|
12640
|
+
setSuccessMessage('Google login successful!');
|
|
12641
|
+
onAuthSuccess(response.token, response.user, response.accountData);
|
|
12642
|
+
}
|
|
12643
|
+
else {
|
|
12644
|
+
throw new Error('Authentication failed - no token received');
|
|
12645
|
+
}
|
|
12646
|
+
// Clean URL parameters
|
|
12647
|
+
const cleanUrl = window.location.origin + window.location.pathname + (state.returnPath?.includes('#') ? state.returnPath.split('?')[0] : window.location.hash.split('?')[0]);
|
|
12648
|
+
window.history.replaceState({}, document.title, cleanUrl);
|
|
12649
|
+
}
|
|
12650
|
+
catch (err) {
|
|
12651
|
+
const errorMessage = err instanceof Error ? err.message : 'Google login failed';
|
|
12652
|
+
setError(getFriendlyErrorMessage(errorMessage));
|
|
12653
|
+
onAuthError?.(err instanceof Error ? err : new Error(errorMessage));
|
|
12654
|
+
// Clean URL parameters even on error
|
|
12655
|
+
const cleanUrl = window.location.origin + window.location.pathname + window.location.hash.split('?')[0];
|
|
12656
|
+
window.history.replaceState({}, document.title, cleanUrl);
|
|
12657
|
+
}
|
|
12658
|
+
finally {
|
|
12659
|
+
setLoading(false);
|
|
12660
|
+
}
|
|
12661
|
+
};
|
|
12574
12662
|
const handleEmailAuth = async (data) => {
|
|
12575
12663
|
setLoading(true);
|
|
12576
12664
|
setError(undefined);
|
|
@@ -12723,11 +12811,16 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
12723
12811
|
// Use custom client ID from config, or fall back to default Smartlinks client ID
|
|
12724
12812
|
const googleClientId = config?.googleClientId || DEFAULT_GOOGLE_CLIENT_ID;
|
|
12725
12813
|
// Determine OAuth flow: default to 'oneTap' for better UX, but allow 'popup' for iframe compatibility
|
|
12726
|
-
const
|
|
12814
|
+
const configuredFlow = config?.googleOAuthFlow || 'oneTap';
|
|
12815
|
+
// For oneTap, automatically use redirect flow in WebView environments
|
|
12816
|
+
const isWebView = detectWebView();
|
|
12817
|
+
const oauthFlow = (configuredFlow === 'oneTap' && isWebView) ? 'redirect' : configuredFlow;
|
|
12727
12818
|
// Log Google Auth configuration for debugging
|
|
12728
12819
|
log.log('Google Auth initiated:', {
|
|
12729
12820
|
googleClientId,
|
|
12730
|
-
|
|
12821
|
+
configuredFlow,
|
|
12822
|
+
effectiveFlow: oauthFlow,
|
|
12823
|
+
isWebView,
|
|
12731
12824
|
currentOrigin: window.location.origin,
|
|
12732
12825
|
currentHref: window.location.href,
|
|
12733
12826
|
configGoogleClientId: config?.googleClientId,
|
|
@@ -12743,7 +12836,37 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
12743
12836
|
throw new Error('Google Identity Services failed to initialize');
|
|
12744
12837
|
}
|
|
12745
12838
|
log.log('Google Identity Services loaded, using flow:', oauthFlow);
|
|
12746
|
-
if (oauthFlow === '
|
|
12839
|
+
if (oauthFlow === 'redirect') {
|
|
12840
|
+
// Use OAuth2 redirect flow (works in WebViews and everywhere)
|
|
12841
|
+
if (!google.accounts.oauth2) {
|
|
12842
|
+
throw new Error('Google OAuth2 not available');
|
|
12843
|
+
}
|
|
12844
|
+
// Build the redirect URI - use current URL without query params
|
|
12845
|
+
const redirectUri = getRedirectUrl();
|
|
12846
|
+
// Build state parameter to preserve context across redirect
|
|
12847
|
+
const state = encodeURIComponent(JSON.stringify({
|
|
12848
|
+
clientId,
|
|
12849
|
+
returnPath: window.location.hash || window.location.pathname,
|
|
12850
|
+
redirectUri,
|
|
12851
|
+
}));
|
|
12852
|
+
log.log('Initializing Google OAuth2 redirect flow:', {
|
|
12853
|
+
client_id: googleClientId,
|
|
12854
|
+
scope: 'openid email profile',
|
|
12855
|
+
redirect_uri: redirectUri,
|
|
12856
|
+
state,
|
|
12857
|
+
});
|
|
12858
|
+
const client = google.accounts.oauth2.initCodeClient({
|
|
12859
|
+
client_id: googleClientId,
|
|
12860
|
+
scope: 'openid email profile',
|
|
12861
|
+
ux_mode: 'redirect',
|
|
12862
|
+
redirect_uri: redirectUri,
|
|
12863
|
+
state,
|
|
12864
|
+
});
|
|
12865
|
+
// This will navigate away from the page
|
|
12866
|
+
client.requestCode();
|
|
12867
|
+
return; // Don't set loading to false - we're navigating away
|
|
12868
|
+
}
|
|
12869
|
+
else if (oauthFlow === 'popup') {
|
|
12747
12870
|
// Use OAuth2 popup flow (works in iframes but requires popup permission)
|
|
12748
12871
|
if (!google.accounts.oauth2) {
|
|
12749
12872
|
throw new Error('Google OAuth2 not available');
|
|
@@ -12834,7 +12957,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
12834
12957
|
client.requestAccessToken();
|
|
12835
12958
|
}
|
|
12836
12959
|
else {
|
|
12837
|
-
// Use One Tap / Sign-In button flow (smoother UX but doesn't work in iframes)
|
|
12960
|
+
// Use One Tap / Sign-In button flow (smoother UX but doesn't work in iframes or WebViews)
|
|
12838
12961
|
log.log('Initializing Google OneTap flow:', {
|
|
12839
12962
|
client_id: googleClientId,
|
|
12840
12963
|
origin: window.location.origin,
|
|
@@ -12867,8 +12990,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
12867
12990
|
},
|
|
12868
12991
|
auto_select: false,
|
|
12869
12992
|
cancel_on_tap_outside: true,
|
|
12870
|
-
|
|
12871
|
-
// Will be needed when FedCM becomes mandatory in the future
|
|
12993
|
+
use_fedcm_for_prompt: true, // Enable FedCM for future browser compatibility
|
|
12872
12994
|
});
|
|
12873
12995
|
// Use timeout fallback instead of deprecated notification methods
|
|
12874
12996
|
// (isNotDisplayed/isSkippedMoment will stop working when FedCM becomes mandatory)
|