@proveanything/smartlinks-auth-ui 0.1.46 → 0.1.47
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/components/SmartlinksAuthUI.d.ts.map +1 -1
- package/dist/index.esm.js +91 -28
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +91 -28
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -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,EAAyF,MAAM,UAAU,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,EAAyF,MAAM,UAAU,CAAC;AAiJ7I,QAAA,MAAM,mBAAmB,QAAa,OAAO,CAAC,IAAI,CA+CjD,CAAC;AAyFF,OAAO,EAAE,mBAAmB,EAAE,CAAC;AA8B/B,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAstD5D,CAAC"}
|
package/dist/index.esm.js
CHANGED
|
@@ -12235,8 +12235,22 @@ const useAuth = () => {
|
|
|
12235
12235
|
};
|
|
12236
12236
|
|
|
12237
12237
|
// VERSION: Update this when making changes to help identify which version is running
|
|
12238
|
-
const AUTH_UI_VERSION = '
|
|
12238
|
+
const AUTH_UI_VERSION = '44';
|
|
12239
12239
|
const LOG_PREFIX = `[SmartlinksAuthUI:v${AUTH_UI_VERSION}]`;
|
|
12240
|
+
// Helper to check for URL auth params synchronously (runs during initialization)
|
|
12241
|
+
// This prevents the form from flashing before detecting deep-link flows
|
|
12242
|
+
const getInitialUrlAuthParams = () => {
|
|
12243
|
+
// Check hash params first (for hash routing like #/test?mode=verifyEmail&token=abc)
|
|
12244
|
+
const hash = window.location.hash;
|
|
12245
|
+
const hashQueryIndex = hash.indexOf('?');
|
|
12246
|
+
const params = hashQueryIndex !== -1
|
|
12247
|
+
? new URLSearchParams(hash.substring(hashQueryIndex + 1))
|
|
12248
|
+
: new URLSearchParams(window.location.search);
|
|
12249
|
+
return {
|
|
12250
|
+
mode: params.get('mode'),
|
|
12251
|
+
token: params.get('token'),
|
|
12252
|
+
};
|
|
12253
|
+
};
|
|
12240
12254
|
// Helper to calculate expiration from AuthResponse
|
|
12241
12255
|
const getExpirationFromResponse = (response) => {
|
|
12242
12256
|
if (response.expiresAt)
|
|
@@ -12470,7 +12484,18 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
12470
12484
|
// Determine initial mode based on signupProminence setting
|
|
12471
12485
|
// 'emphasized' mode defaults to 'register' unless explicitly overridden
|
|
12472
12486
|
const effectiveInitialMode = initialMode ?? (resolvedSignupProminence === 'emphasized' ? 'register' : 'login');
|
|
12473
|
-
|
|
12487
|
+
// Check URL params synchronously to avoid form flash during deep-link flows
|
|
12488
|
+
const initialUrlParams = useMemo(() => getInitialUrlAuthParams(), []);
|
|
12489
|
+
const isUrlAuthFlow = !!(initialUrlParams.mode && initialUrlParams.token);
|
|
12490
|
+
// Track if we're processing a URL-based auth flow (verification, magic link, etc.)
|
|
12491
|
+
const [urlAuthProcessing, setUrlAuthProcessing] = useState(isUrlAuthFlow);
|
|
12492
|
+
const [mode, setMode] = useState(() => {
|
|
12493
|
+
// If URL has reset password token, start in reset-password mode
|
|
12494
|
+
if (initialUrlParams.mode === 'resetPassword' && initialUrlParams.token) {
|
|
12495
|
+
return 'reset-password';
|
|
12496
|
+
}
|
|
12497
|
+
return effectiveInitialMode;
|
|
12498
|
+
});
|
|
12474
12499
|
const [loading, setLoading] = useState(false);
|
|
12475
12500
|
const [error, setError] = useState();
|
|
12476
12501
|
const [resolvedTheme, setResolvedTheme] = useState('light');
|
|
@@ -12492,17 +12517,34 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
12492
12517
|
const log = useMemo(() => createLoggerWrapper(logger), [logger]);
|
|
12493
12518
|
const api = new AuthAPI(apiEndpoint, clientId, clientName, logger);
|
|
12494
12519
|
const auth = useAuth();
|
|
12520
|
+
// Helper function to strip auth params (mode, token) from URLs
|
|
12521
|
+
// These params are used for deep-link flows and must be removed after processing
|
|
12522
|
+
const stripAuthParams = (url) => {
|
|
12523
|
+
try {
|
|
12524
|
+
const urlObj = new URL(url);
|
|
12525
|
+
urlObj.searchParams.delete('mode');
|
|
12526
|
+
urlObj.searchParams.delete('token');
|
|
12527
|
+
return urlObj.toString();
|
|
12528
|
+
}
|
|
12529
|
+
catch {
|
|
12530
|
+
// If URL parsing fails, try simple string manipulation
|
|
12531
|
+
return url.split('?')[0];
|
|
12532
|
+
}
|
|
12533
|
+
};
|
|
12495
12534
|
// Helper function to perform redirects, respecting the onRedirect callback
|
|
12496
12535
|
// When onRedirect is provided, delegates navigation to parent app
|
|
12497
12536
|
// Otherwise falls back to direct window.location.href for backward compatibility
|
|
12537
|
+
// IMPORTANT: Always strips auth params (mode, token) to prevent re-verification on reload
|
|
12498
12538
|
const performRedirect = (url, reason) => {
|
|
12539
|
+
const cleanUrl = stripAuthParams(url);
|
|
12540
|
+
log.log(`Redirect requested: ${url} -> cleaned: ${cleanUrl} (${reason})`);
|
|
12499
12541
|
if (onRedirect) {
|
|
12500
|
-
log.log(`Delegating redirect to onRedirect callback: ${
|
|
12501
|
-
onRedirect(
|
|
12542
|
+
log.log(`Delegating redirect to onRedirect callback: ${cleanUrl} (${reason})`);
|
|
12543
|
+
onRedirect(cleanUrl, reason);
|
|
12502
12544
|
}
|
|
12503
12545
|
else {
|
|
12504
|
-
log.log(`Performing direct redirect to: ${
|
|
12505
|
-
window.location.href =
|
|
12546
|
+
log.log(`Performing direct redirect to: ${cleanUrl} (${reason})`);
|
|
12547
|
+
window.location.href = cleanUrl;
|
|
12506
12548
|
}
|
|
12507
12549
|
};
|
|
12508
12550
|
// Dark mode detection and theme management
|
|
@@ -12803,6 +12845,25 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
12803
12845
|
const authCode = params.get('code');
|
|
12804
12846
|
const state = params.get('state');
|
|
12805
12847
|
log.log('URL params detected:', { urlMode, token, authCode: !!authCode, state: !!state, hash: window.location.hash, search: window.location.search });
|
|
12848
|
+
// Check if user is already authenticated
|
|
12849
|
+
const isAlreadyAuthenticated = !!auth.user?.uid;
|
|
12850
|
+
if (isAlreadyAuthenticated && (urlMode === 'verifyEmail' || urlMode === 'magicLink')) {
|
|
12851
|
+
// User is already logged in but we have verification params in URL
|
|
12852
|
+
// This happens when parent reloads iframe after successful verification
|
|
12853
|
+
// Don't re-verify - just redirect to clean URL
|
|
12854
|
+
log.log('Already authenticated, skipping re-verification. Redirecting to clean URL.');
|
|
12855
|
+
setUrlAuthProcessing(false); // Clear processing state
|
|
12856
|
+
if (redirectUrl) {
|
|
12857
|
+
performRedirect(redirectUrl, urlMode === 'verifyEmail' ? 'email-verified' : 'magic-link');
|
|
12858
|
+
}
|
|
12859
|
+
else {
|
|
12860
|
+
// No redirect configured - just show success and notify parent
|
|
12861
|
+
setAuthSuccess(true);
|
|
12862
|
+
setSuccessMessage('You are already logged in!');
|
|
12863
|
+
onAuthSuccess(auth.token, auth.user, auth.accountData ?? undefined);
|
|
12864
|
+
}
|
|
12865
|
+
return;
|
|
12866
|
+
}
|
|
12806
12867
|
if (authCode && state) {
|
|
12807
12868
|
// Google OAuth redirect callback
|
|
12808
12869
|
handleGoogleAuthCodeCallback(authCode, state);
|
|
@@ -12814,6 +12875,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
12814
12875
|
const handleURLBasedAuth = async (urlMode, token) => {
|
|
12815
12876
|
setLoading(true);
|
|
12816
12877
|
setError(undefined);
|
|
12878
|
+
// Note: urlAuthProcessing is already true from initialization if we got here
|
|
12817
12879
|
try {
|
|
12818
12880
|
if (urlMode === 'verifyEmail') {
|
|
12819
12881
|
log.log('Verifying email with token:', token);
|
|
@@ -12826,9 +12888,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
12826
12888
|
// MUST await to ensure session is ready before any redirect
|
|
12827
12889
|
await auth.login(response.token, response.user, response.accountData, true, getExpirationFromResponse(response), proxyMode // waitForParentAck - ensures parent has validated/persisted before redirect
|
|
12828
12890
|
);
|
|
12829
|
-
//
|
|
12830
|
-
const cleanUrl = window.location.href.split('?')[0];
|
|
12831
|
-
window.history.replaceState({}, document.title, cleanUrl);
|
|
12891
|
+
setUrlAuthProcessing(false); // Clear processing state before redirect/success
|
|
12832
12892
|
if (redirectUrl) {
|
|
12833
12893
|
// Redirect to clean URL and resume flow
|
|
12834
12894
|
// In proxy mode: parent already validated & persisted via acknowledgment in auth.login()
|
|
@@ -12849,6 +12909,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
12849
12909
|
}
|
|
12850
12910
|
else {
|
|
12851
12911
|
// verify-manual-login mode or no token: Show success but require manual login
|
|
12912
|
+
setUrlAuthProcessing(false); // Clear processing state
|
|
12852
12913
|
setAuthSuccess(true);
|
|
12853
12914
|
setSuccessMessage('Email verified successfully! Please log in with your credentials.');
|
|
12854
12915
|
// Clear the URL parameters
|
|
@@ -12869,6 +12930,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
12869
12930
|
if (!verifyResult.valid) {
|
|
12870
12931
|
throw new Error(verifyResult.message || 'Invalid or expired token');
|
|
12871
12932
|
}
|
|
12933
|
+
setUrlAuthProcessing(false); // Clear processing state - showing reset form
|
|
12872
12934
|
setResetToken(token); // Store token for use in password reset
|
|
12873
12935
|
if (verifyResult.email) {
|
|
12874
12936
|
setResetEmail(verifyResult.email); // Store email for auto-login after reset
|
|
@@ -12887,9 +12949,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
12887
12949
|
// MUST await to ensure session is ready before any redirect
|
|
12888
12950
|
await auth.login(response.token, response.user, response.accountData, true, getExpirationFromResponse(response), proxyMode // waitForParentAck
|
|
12889
12951
|
);
|
|
12890
|
-
//
|
|
12891
|
-
const cleanUrl = window.location.href.split('?')[0];
|
|
12892
|
-
window.history.replaceState({}, document.title, cleanUrl);
|
|
12952
|
+
setUrlAuthProcessing(false); // Clear processing state before redirect/success
|
|
12893
12953
|
if (redirectUrl) {
|
|
12894
12954
|
// Redirect to clean URL and resume flow
|
|
12895
12955
|
log.log('Magic link verification complete, redirecting to:', redirectUrl);
|
|
@@ -12912,6 +12972,7 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
12912
12972
|
catch (err) {
|
|
12913
12973
|
log.error('URL-based auth error:', err);
|
|
12914
12974
|
const errorMessage = err instanceof Error ? err.message : 'An error occurred';
|
|
12975
|
+
setUrlAuthProcessing(false); // Clear processing state on error so user can retry
|
|
12915
12976
|
// If it's an email verification error (expired/invalid token), show resend option
|
|
12916
12977
|
if (urlMode === 'verifyEmail') {
|
|
12917
12978
|
setError(`${errorMessage} - Please enter your email below to receive a new verification link.`);
|
|
@@ -13495,23 +13556,14 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
13495
13556
|
}
|
|
13496
13557
|
// Update auth context with account data if token is provided
|
|
13497
13558
|
if (response.token) {
|
|
13498
|
-
// Phone auth
|
|
13499
|
-
//
|
|
13500
|
-
|
|
13559
|
+
// Phone auth is an INTERACTIVE flow (user entered OTP in UI)
|
|
13560
|
+
// Unlike deep-link flows (email verification, magic link), there's no URL token to clean up
|
|
13561
|
+
// Do NOT auto-redirect - let the parent app control the next step (profile completion, etc.)
|
|
13562
|
+
await auth.login(response.token, response.user, response.accountData, response.isNewUser, getExpirationFromResponse(response), false // No waitForParentAck needed - interactive flow doesn't redirect
|
|
13501
13563
|
);
|
|
13502
|
-
|
|
13503
|
-
|
|
13504
|
-
|
|
13505
|
-
performRedirect(redirectUrl, 'phone-verified');
|
|
13506
|
-
}
|
|
13507
|
-
else {
|
|
13508
|
-
// No redirect configured: show success UI
|
|
13509
|
-
setAuthSuccess(true);
|
|
13510
|
-
setSuccessMessage('Phone verified! You are now logged in.');
|
|
13511
|
-
if (!proxyMode) {
|
|
13512
|
-
onAuthSuccess(response.token, response.user, response.accountData);
|
|
13513
|
-
}
|
|
13514
|
-
}
|
|
13564
|
+
setAuthSuccess(true);
|
|
13565
|
+
setSuccessMessage('Phone verified! You are now logged in.');
|
|
13566
|
+
onAuthSuccess(response.token, response.user, response.accountData);
|
|
13515
13567
|
}
|
|
13516
13568
|
else {
|
|
13517
13569
|
// Check if response contains an error message from the backend
|
|
@@ -13609,6 +13661,17 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
13609
13661
|
setLoading(false);
|
|
13610
13662
|
}
|
|
13611
13663
|
};
|
|
13664
|
+
// Show processing state for URL-based auth (verification, magic link, password reset)
|
|
13665
|
+
// This runs BEFORE configLoading check to prevent form flash on deep-link flows
|
|
13666
|
+
if (urlAuthProcessing) {
|
|
13667
|
+
return (jsx(AuthContainer, { theme: resolvedTheme, className: className, minimal: minimal || config?.branding?.minimal || false, children: jsxs("div", { style: { textAlign: 'center', padding: '2rem' }, children: [jsx("div", { className: "auth-spinner", style: { marginBottom: '1rem' } }), jsx("p", { style: {
|
|
13668
|
+
color: resolvedTheme === 'dark' ? '#94a3b8' : '#6B7280',
|
|
13669
|
+
fontSize: '0.875rem'
|
|
13670
|
+
}, children: initialUrlParams.mode === 'verifyEmail' ? 'Verifying your email...' :
|
|
13671
|
+
initialUrlParams.mode === 'magicLink' ? 'Processing magic link...' :
|
|
13672
|
+
initialUrlParams.mode === 'resetPassword' ? 'Validating reset link...' :
|
|
13673
|
+
'Processing...' })] }) }));
|
|
13674
|
+
}
|
|
13612
13675
|
if (configLoading) {
|
|
13613
13676
|
return (jsx(AuthContainer, { theme: resolvedTheme, className: className, minimal: minimal || config?.branding?.minimal || false, children: jsx("div", { style: { textAlign: 'center', padding: '2rem' }, children: jsx("div", { className: "auth-spinner" }) }) }));
|
|
13614
13677
|
}
|