@proveanything/smartlinks-auth-ui 0.5.1 → 0.5.4
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 +12 -1
- package/dist/api.d.ts.map +1 -1
- package/dist/components/SmartlinksAuthUI.d.ts.map +1 -1
- package/dist/components/WhatsAppAuthForm.d.ts +1 -0
- package/dist/components/WhatsAppAuthForm.d.ts.map +1 -1
- package/dist/index.esm.js +74 -15
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +74 -15
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +42 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -11002,9 +11002,9 @@ const WhatsAppAuthForm = ({ onSend, onPollStatus, onVerified, onBack, loading =
|
|
|
11002
11002
|
const awaitingNameInput = collectName && !sent;
|
|
11003
11003
|
const awaitingAutoSend = !collectName && !sent;
|
|
11004
11004
|
return (jsxRuntime.jsxs("form", { className: "auth-form", onSubmit: awaitingNameInput ? handleNameSubmit : (e) => e.preventDefault(), children: [jsxRuntime.jsxs("div", { className: "auth-form-header", children: [jsxRuntime.jsx("h2", { className: "auth-form-title", children: "Continue with WhatsApp" }), jsxRuntime.jsx("p", { className: "auth-form-subtitle", children: sent
|
|
11005
|
-
?
|
|
11005
|
+
? "Tap below to open WhatsApp and send us the pre-filled message — that's how we confirm your number. We'll log you in the moment we receive it."
|
|
11006
11006
|
: awaitingNameInput
|
|
11007
|
-
? '
|
|
11007
|
+
? "We'll open WhatsApp with a pre-filled message. Just hit send to confirm your number — no SMS code, no typing."
|
|
11008
11008
|
: 'Preparing your WhatsApp link…' })] }), displayedError && (jsxRuntime.jsxs("div", { className: "auth-error", role: "alert", children: [jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "currentColor", "aria-hidden": "true", children: jsxRuntime.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" }) }), displayedError] })), awaitingNameInput && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: "auth-form-group", children: [jsxRuntime.jsx("label", { htmlFor: "waName", className: "auth-label", children: "Name" }), jsxRuntime.jsx("input", { id: "waName", type: "text", value: displayName, onChange: (e) => setDisplayName(e.target.value), className: "auth-input", placeholder: "John Smith", disabled: loading, autoComplete: "name", autoFocus: true })] }), jsxRuntime.jsx("button", { type: "submit", className: "auth-button auth-button-primary", disabled: loading, children: loading ? jsxRuntime.jsx("span", { className: "auth-spinner" }) : 'Continue' })] })), awaitingAutoSend && (jsxRuntime.jsx("div", { style: { textAlign: 'center', padding: '1.5rem' }, children: jsxRuntime.jsx("span", { className: "auth-spinner" }) })), sent && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [!showOnDesktop && (jsxRuntime.jsxs("a", { href: sent.waLink, target: "_blank", rel: "noopener noreferrer", className: "auth-button auth-button-primary", style: {
|
|
11009
11009
|
display: 'flex',
|
|
11010
11010
|
alignItems: 'center',
|
|
@@ -11013,7 +11013,7 @@ const WhatsAppAuthForm = ({ onSend, onPollStatus, onVerified, onBack, loading =
|
|
|
11013
11013
|
background: '#25D366',
|
|
11014
11014
|
color: '#fff',
|
|
11015
11015
|
textDecoration: 'none',
|
|
11016
|
-
}, children: [jsxRuntime.jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true", children: jsxRuntime.jsx("path", { d: "M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.247-.694.247-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893A11.821 11.821 0 0020.464 3.488" }) }), "Open WhatsApp"] })), showOnDesktop && qrDataUrl && (jsxRuntime.jsxs("div", { style: { textAlign: 'center', margin: '0.75rem 0' }, children: [jsxRuntime.jsx("div", { style: {
|
|
11016
|
+
}, children: [jsxRuntime.jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true", children: jsxRuntime.jsx("path", { d: "M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.247-.694.247-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893A11.821 11.821 0 0020.464 3.488" }) }), "Open WhatsApp & send message"] })), showOnDesktop && qrDataUrl && (jsxRuntime.jsxs("div", { style: { textAlign: 'center', margin: '0.75rem 0' }, children: [jsxRuntime.jsx("div", { style: {
|
|
11017
11017
|
display: 'inline-block',
|
|
11018
11018
|
padding: '0.75rem',
|
|
11019
11019
|
background: '#fff',
|
|
@@ -11023,7 +11023,7 @@ const WhatsAppAuthForm = ({ onSend, onPollStatus, onVerified, onBack, loading =
|
|
|
11023
11023
|
fontSize: '0.8125rem',
|
|
11024
11024
|
color: '#6B7280',
|
|
11025
11025
|
marginTop: '0.5rem',
|
|
11026
|
-
}, children: "Scan with your phone \u2014 your WhatsApp will open with a pre-filled message. Just hit send." })] })), showOnDesktop && (jsxRuntime.jsx("a", { href: sent.waLink, target: "_blank", rel: "noopener noreferrer", className: "auth-button auth-button-secondary", style: { display: 'block', textAlign: 'center', textDecoration: 'none' }, children: "Or
|
|
11026
|
+
}, children: "Scan with your phone \u2014 your WhatsApp will open with a pre-filled message. Just hit send." })] })), showOnDesktop && (jsxRuntime.jsx("a", { href: sent.waLink, target: "_blank", rel: "noopener noreferrer", className: "auth-button auth-button-secondary", style: { display: 'block', textAlign: 'center', textDecoration: 'none' }, children: "Or send the message from this device" })), jsxRuntime.jsxs("div", { style: {
|
|
11027
11027
|
marginTop: '1rem',
|
|
11028
11028
|
padding: '0.75rem',
|
|
11029
11029
|
borderRadius: '0.5rem',
|
|
@@ -11032,7 +11032,7 @@ const WhatsAppAuthForm = ({ onSend, onPollStatus, onVerified, onBack, loading =
|
|
|
11032
11032
|
fontSize: '0.8125rem',
|
|
11033
11033
|
color: '#374151',
|
|
11034
11034
|
textAlign: 'center',
|
|
11035
|
-
}, children: [jsxRuntime.jsx("span", { className: "auth-spinner", style: { marginRight: '0.5rem' } }), "Waiting for your
|
|
11035
|
+
}, children: [jsxRuntime.jsx("span", { className: "auth-spinner", style: { marginRight: '0.5rem' } }), "Waiting for your message \u2014 we'll log you in automatically\u2026", jsxRuntime.jsxs("div", { style: { marginTop: '0.25rem', fontSize: '0.75rem', opacity: 0.7 }, children: ["Code: ", jsxRuntime.jsx("code", { children: sent.code })] })] }), jsxRuntime.jsx("button", { type: "button", className: "auth-link", onClick: handleSendNew, disabled: loading, style: {
|
|
11036
11036
|
marginTop: '0.75rem',
|
|
11037
11037
|
background: 'none',
|
|
11038
11038
|
border: 'none',
|
|
@@ -11509,10 +11509,19 @@ class AuthAPI {
|
|
|
11509
11509
|
return smartlinks__namespace.authKit.verifyMagicLink(this.clientId, token);
|
|
11510
11510
|
}
|
|
11511
11511
|
// ============= WhatsApp =============
|
|
11512
|
-
async sendWhatsApp(redirectUrl, phoneNumber) {
|
|
11512
|
+
async sendWhatsApp(redirectUrl, phoneNumber, reply, prefillMessage) {
|
|
11513
11513
|
// phoneNumber is optional — backend extracts it from the inbound WhatsApp webhook.
|
|
11514
|
-
//
|
|
11515
|
-
|
|
11514
|
+
// `reply`, when provided, is sent back to the user via WhatsApp on successful verification.
|
|
11515
|
+
// `prefillMessage` customizes the message body pre-filled in the wa.me link.
|
|
11516
|
+
return smartlinks__namespace.authKit.sendWhatsApp(this.clientId, {
|
|
11517
|
+
phoneNumber: phoneNumber ?? '',
|
|
11518
|
+
redirectUrl,
|
|
11519
|
+
...(reply ? { reply } : {}),
|
|
11520
|
+
...(prefillMessage ? { prefillMessage } : {}),
|
|
11521
|
+
});
|
|
11522
|
+
}
|
|
11523
|
+
async exchangeWhatsAppSession(token, sessionKey) {
|
|
11524
|
+
return smartlinks__namespace.authKit.exchangeWhatsAppSession(this.clientId, token, sessionKey);
|
|
11516
11525
|
}
|
|
11517
11526
|
async getWhatsAppStatus(token) {
|
|
11518
11527
|
return smartlinks__namespace.authKit.getWhatsAppStatus(this.clientId, token);
|
|
@@ -12843,6 +12852,31 @@ const getActionResultErrorMessage = (result) => {
|
|
|
12843
12852
|
details: candidate.details,
|
|
12844
12853
|
});
|
|
12845
12854
|
};
|
|
12855
|
+
const interpolateReply = (input, vars) => {
|
|
12856
|
+
if (!input)
|
|
12857
|
+
return input;
|
|
12858
|
+
return input
|
|
12859
|
+
.replace(/\{\{\s*name\s*\}\}/gi, vars.name?.trim() || 'there')
|
|
12860
|
+
.replace(/\{\{\s*clientName\s*\}\}/gi, vars.clientName?.trim() || '')
|
|
12861
|
+
.replace(/\{\{\s*phoneNumber\s*\}\}/gi, vars.phoneNumber?.trim() || '');
|
|
12862
|
+
};
|
|
12863
|
+
const buildWhatsAppReply = (cfg, vars) => {
|
|
12864
|
+
if (!cfg)
|
|
12865
|
+
return undefined;
|
|
12866
|
+
if (cfg.enabled === false)
|
|
12867
|
+
return undefined;
|
|
12868
|
+
const text = interpolateReply(cfg.text, vars);
|
|
12869
|
+
const cta = cfg.cta && cfg.cta.buttonUrl && cfg.cta.buttonLabel
|
|
12870
|
+
? {
|
|
12871
|
+
body: interpolateReply(cfg.cta.body, vars) || '',
|
|
12872
|
+
buttonLabel: interpolateReply(cfg.cta.buttonLabel, vars) || cfg.cta.buttonLabel,
|
|
12873
|
+
buttonUrl: interpolateReply(cfg.cta.buttonUrl, vars) || cfg.cta.buttonUrl,
|
|
12874
|
+
}
|
|
12875
|
+
: undefined;
|
|
12876
|
+
if (!text && !cta)
|
|
12877
|
+
return undefined;
|
|
12878
|
+
return { ...(text ? { text } : {}), ...(cta ? { cta } : {}) };
|
|
12879
|
+
};
|
|
12846
12880
|
// Default Smartlinks Google OAuth Client ID (public - safe to expose)
|
|
12847
12881
|
const DEFAULT_GOOGLE_CLIENT_ID = '696509063554-jdlbjl8vsjt7cr0vgkjkjf3ffnvi3a70.apps.googleusercontent.com';
|
|
12848
12882
|
// Default Google OAuth proxy URL (hosted on our whitelisted domain)
|
|
@@ -13020,7 +13054,7 @@ const checkSilentGoogleSignIn = async (clientId, googleClientId) => {
|
|
|
13020
13054
|
});
|
|
13021
13055
|
};
|
|
13022
13056
|
// getFriendlyErrorMessage is now imported from ../utils/errorHandling
|
|
13023
|
-
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, }) => {
|
|
13057
|
+
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, }) => {
|
|
13024
13058
|
// Resolve signup prominence from props, customization, config, or default
|
|
13025
13059
|
const resolvedSignupProminence = signupProminence || customization?.signupProminence || 'minimal';
|
|
13026
13060
|
// Determine initial mode based on signupProminence setting
|
|
@@ -14237,15 +14271,24 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
14237
14271
|
setLoading(false);
|
|
14238
14272
|
}
|
|
14239
14273
|
};
|
|
14274
|
+
// Track the most recent WhatsApp send so we can exchange its sessionKey for a real login session.
|
|
14275
|
+
const whatsappSendRef = React.useRef(null);
|
|
14240
14276
|
const handleWhatsAppSend = async (displayName) => {
|
|
14241
14277
|
setLoading(true);
|
|
14242
14278
|
setError(undefined);
|
|
14243
14279
|
try {
|
|
14244
14280
|
// No ensureAccount and no phone number — the backend creates/links the
|
|
14245
14281
|
// account from the inbound WhatsApp webhook (sender's number is the proof).
|
|
14246
|
-
//
|
|
14247
|
-
|
|
14248
|
-
const
|
|
14282
|
+
// Resolve reply config: per-call prop wins, otherwise fall back to AuthKit default.
|
|
14283
|
+
const replyConfig = whatsappReply ?? config?.whatsappReply;
|
|
14284
|
+
const reply = buildWhatsAppReply(replyConfig, {
|
|
14285
|
+
name: displayName,
|
|
14286
|
+
clientName,
|
|
14287
|
+
});
|
|
14288
|
+
// Resolve outbound prefill message: per-call prop wins, then AuthKit default.
|
|
14289
|
+
const prefillMessage = whatsappPrefillMessage ?? config?.whatsappPrefillMessage;
|
|
14290
|
+
const result = await api.sendWhatsApp(getRedirectUrl(), undefined, reply, prefillMessage);
|
|
14291
|
+
whatsappSendRef.current = { token: result.token, sessionKey: result.sessionKey };
|
|
14249
14292
|
return result;
|
|
14250
14293
|
}
|
|
14251
14294
|
catch (err) {
|
|
@@ -14260,12 +14303,28 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
|
|
|
14260
14303
|
const handleWhatsAppPoll = async (token) => {
|
|
14261
14304
|
return api.getWhatsAppStatus(token);
|
|
14262
14305
|
};
|
|
14263
|
-
const handleWhatsAppVerified = (status) => {
|
|
14264
|
-
// Backend returns a redirectUrl containing a magic-link-style token that
|
|
14265
|
-
// completes the login on landing. Treat the same as magic-link verification.
|
|
14306
|
+
const handleWhatsAppVerified = async (status) => {
|
|
14266
14307
|
const target = status.redirectUrl || getRedirectUrl();
|
|
14267
14308
|
setAuthSuccess(true);
|
|
14268
14309
|
setSuccessMessage('WhatsApp verified! Signing you in…');
|
|
14310
|
+
// Exchange the verified WhatsApp proof for a real Auth Kit session token,
|
|
14311
|
+
// so the user is fully logged in (not just verified) and any same-tab
|
|
14312
|
+
// continuation flows (claim, bid, etc.) pick up an authenticated session.
|
|
14313
|
+
const send = whatsappSendRef.current;
|
|
14314
|
+
if (send?.sessionKey) {
|
|
14315
|
+
try {
|
|
14316
|
+
const session = await api.exchangeWhatsAppSession(send.token, send.sessionKey);
|
|
14317
|
+
if (session?.token && session.user) {
|
|
14318
|
+
await auth.login(session.token, session.user, session.accountData, true, getExpirationFromResponse(session));
|
|
14319
|
+
if (!proxyMode) {
|
|
14320
|
+
onAuthSuccess(session.token, session.user, session.accountData);
|
|
14321
|
+
}
|
|
14322
|
+
}
|
|
14323
|
+
}
|
|
14324
|
+
catch (err) {
|
|
14325
|
+
log.warn('WhatsApp session exchange failed, falling back to redirect-only flow:', err);
|
|
14326
|
+
}
|
|
14327
|
+
}
|
|
14269
14328
|
performRedirect(target, 'magic-link');
|
|
14270
14329
|
};
|
|
14271
14330
|
// Show processing state for URL-based auth (verification, magic link, password reset)
|