@proveanything/smartlinks-auth-ui 0.1.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.
Files changed (48) hide show
  1. package/README.md +323 -0
  2. package/dist/api.d.ts +44 -0
  3. package/dist/api.d.ts.map +1 -0
  4. package/dist/api.js +105 -0
  5. package/dist/components/AuthContainer.d.ts +12 -0
  6. package/dist/components/AuthContainer.d.ts.map +1 -0
  7. package/dist/components/AuthContainer.js +46 -0
  8. package/dist/components/AuthUIPreview.d.ts +11 -0
  9. package/dist/components/AuthUIPreview.d.ts.map +1 -0
  10. package/dist/components/AuthUIPreview.js +10 -0
  11. package/dist/components/EmailAuthForm.d.ts +14 -0
  12. package/dist/components/EmailAuthForm.d.ts.map +1 -0
  13. package/dist/components/EmailAuthForm.js +20 -0
  14. package/dist/components/PasswordResetForm.d.ts +13 -0
  15. package/dist/components/PasswordResetForm.d.ts.map +1 -0
  16. package/dist/components/PasswordResetForm.js +37 -0
  17. package/dist/components/PhoneAuthForm.d.ts +11 -0
  18. package/dist/components/PhoneAuthForm.d.ts.map +1 -0
  19. package/dist/components/PhoneAuthForm.js +20 -0
  20. package/dist/components/ProtectedRoute.d.ts +9 -0
  21. package/dist/components/ProtectedRoute.d.ts.map +1 -0
  22. package/dist/components/ProtectedRoute.js +20 -0
  23. package/dist/components/ProviderButtons.d.ts +12 -0
  24. package/dist/components/ProviderButtons.d.ts.map +1 -0
  25. package/dist/components/ProviderButtons.js +8 -0
  26. package/dist/context/AuthContext.d.ts +20 -0
  27. package/dist/context/AuthContext.d.ts.map +1 -0
  28. package/dist/context/AuthContext.js +113 -0
  29. package/dist/index.css +2 -0
  30. package/dist/index.css.map +1 -0
  31. package/dist/index.d.ts +10 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.esm.css +2 -0
  34. package/dist/index.esm.css.map +1 -0
  35. package/dist/index.esm.js +840 -0
  36. package/dist/index.esm.js.map +1 -0
  37. package/dist/index.js +867 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/smartlinks.d.ts +65 -0
  40. package/dist/smartlinks.d.ts.map +1 -0
  41. package/dist/smartlinks.js +141 -0
  42. package/dist/types.d.ts +101 -0
  43. package/dist/types.d.ts.map +1 -0
  44. package/dist/types.js +1 -0
  45. package/dist/utils/tokenStorage.d.ts +14 -0
  46. package/dist/utils/tokenStorage.d.ts.map +1 -0
  47. package/dist/utils/tokenStorage.js +71 -0
  48. package/package.json +57 -0
package/dist/index.js ADDED
@@ -0,0 +1,867 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+ var react = require('react');
5
+ var smartlinks = require('@proveanything/smartlinks');
6
+
7
+ function _interopNamespaceDefault(e) {
8
+ var n = Object.create(null);
9
+ if (e) {
10
+ Object.keys(e).forEach(function (k) {
11
+ if (k !== 'default') {
12
+ var d = Object.getOwnPropertyDescriptor(e, k);
13
+ Object.defineProperty(n, k, d.get ? d : {
14
+ enumerable: true,
15
+ get: function () { return e[k]; }
16
+ });
17
+ }
18
+ });
19
+ }
20
+ n.default = e;
21
+ return Object.freeze(n);
22
+ }
23
+
24
+ var smartlinks__namespace = /*#__PURE__*/_interopNamespaceDefault(smartlinks);
25
+
26
+ const AuthContainer = ({ children, theme = 'light', className = '', config, }) => {
27
+ // Apply CSS variables for customization
28
+ react.useEffect(() => {
29
+ if (!config?.branding)
30
+ return;
31
+ const root = document.documentElement;
32
+ const { branding } = config;
33
+ if (branding.primaryColor) {
34
+ root.style.setProperty('--auth-primary-color', branding.primaryColor);
35
+ }
36
+ if (branding.secondaryColor) {
37
+ root.style.setProperty('--auth-secondary-color', branding.secondaryColor);
38
+ }
39
+ if (branding.backgroundColor) {
40
+ root.style.setProperty('--auth-bg-color', branding.backgroundColor);
41
+ }
42
+ if (branding.fontFamily) {
43
+ root.style.setProperty('--auth-font-family', branding.fontFamily);
44
+ }
45
+ // Inject custom CSS if provided
46
+ if (branding.customCss) {
47
+ const styleId = 'auth-custom-styles';
48
+ let styleEl = document.getElementById(styleId);
49
+ if (!styleEl) {
50
+ styleEl = document.createElement('style');
51
+ styleEl.id = styleId;
52
+ document.head.appendChild(styleEl);
53
+ }
54
+ styleEl.textContent = branding.customCss;
55
+ }
56
+ return () => {
57
+ // Cleanup on unmount
58
+ root.style.removeProperty('--auth-primary-color');
59
+ root.style.removeProperty('--auth-secondary-color');
60
+ root.style.removeProperty('--auth-bg-color');
61
+ root.style.removeProperty('--auth-font-family');
62
+ };
63
+ }, [config]);
64
+ const title = config?.branding?.title || 'Smartlinks Auth';
65
+ const subtitle = config?.branding?.subtitle || 'Sign in to your account';
66
+ const logoUrl = config?.branding?.logoUrl;
67
+ return (jsxRuntime.jsx("div", { className: `auth-container auth-theme-${theme} ${className}`, children: jsxRuntime.jsxs("div", { className: "auth-card", style: { borderRadius: config?.branding?.buttonStyle === 'square' ? '4px' : '12px' }, children: [jsxRuntime.jsxs("div", { className: "auth-header", children: [jsxRuntime.jsx("div", { className: "auth-logo", children: logoUrl ? (jsxRuntime.jsx("img", { src: logoUrl, alt: "Logo", style: { width: 40, height: 40, objectFit: 'contain' } })) : (jsxRuntime.jsxs("svg", { width: "40", height: "40", viewBox: "0 0 40 40", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [jsxRuntime.jsx("rect", { width: "40", height: "40", rx: "8", fill: "url(#gradient)" }), jsxRuntime.jsx("path", { d: "M20 10L12 18L20 26L28 18L20 10Z", fill: "white", fillOpacity: "0.9" }), jsxRuntime.jsx("path", { d: "M20 18L16 22L20 26L24 22L20 18Z", fill: "white" }), jsxRuntime.jsx("defs", { children: jsxRuntime.jsxs("linearGradient", { id: "gradient", x1: "0", y1: "0", x2: "40", y2: "40", gradientUnits: "userSpaceOnUse", children: [jsxRuntime.jsx("stop", { stopColor: "var(--auth-primary-color, #3B82F6)" }), jsxRuntime.jsx("stop", { offset: "1", stopColor: "var(--auth-secondary-color, #1D4ED8)" })] }) })] })) }), jsxRuntime.jsx("h1", { className: "auth-title", children: title }), jsxRuntime.jsx("p", { className: "auth-subtitle", children: subtitle })] }), jsxRuntime.jsx("div", { className: "auth-content", children: children }), (config?.branding?.termsUrl || config?.branding?.privacyUrl) && (jsxRuntime.jsxs("div", { className: "auth-footer", children: [config.branding.termsUrl && jsxRuntime.jsx("a", { href: config.branding.termsUrl, target: "_blank", rel: "noopener noreferrer", children: "Terms" }), config.branding.termsUrl && config.branding.privacyUrl && jsxRuntime.jsx("span", { children: "\u2022" }), config.branding.privacyUrl && jsxRuntime.jsx("a", { href: config.branding.privacyUrl, target: "_blank", rel: "noopener noreferrer", children: "Privacy" })] }))] }) }));
68
+ };
69
+
70
+ const EmailAuthForm = ({ mode, onSubmit, onModeSwitch, onForgotPassword, loading, error, }) => {
71
+ const [formData, setFormData] = react.useState({
72
+ email: '',
73
+ password: '',
74
+ displayName: '',
75
+ });
76
+ const handleSubmit = async (e) => {
77
+ e.preventDefault();
78
+ await onSubmit(formData);
79
+ };
80
+ const handleChange = (field, value) => {
81
+ setFormData(prev => ({ ...prev, [field]: value }));
82
+ };
83
+ return (jsxRuntime.jsxs("form", { className: "auth-form", onSubmit: handleSubmit, children: [jsxRuntime.jsxs("div", { className: "auth-form-header", children: [jsxRuntime.jsx("h2", { className: "auth-form-title", children: mode === 'login' ? 'Sign in' : 'Create account' }), jsxRuntime.jsx("p", { className: "auth-form-subtitle", children: mode === 'login'
84
+ ? 'Welcome back! Please enter your credentials.'
85
+ : 'Get started by creating your account.' })] }), error && (jsxRuntime.jsxs("div", { className: "auth-error", role: "alert", children: [jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "currentColor", 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" }) }), error] })), mode === 'register' && (jsxRuntime.jsxs("div", { className: "auth-form-group", children: [jsxRuntime.jsx("label", { htmlFor: "displayName", className: "auth-label", children: "Full Name" }), jsxRuntime.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 Doe" })] })), jsxRuntime.jsxs("div", { className: "auth-form-group", children: [jsxRuntime.jsx("label", { htmlFor: "email", className: "auth-label", children: "Email address" }), jsxRuntime.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" })] }), jsxRuntime.jsxs("div", { className: "auth-form-group", children: [jsxRuntime.jsx("label", { htmlFor: "password", className: "auth-label", children: "Password" }), jsxRuntime.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 === 'login' && (jsxRuntime.jsx("div", { className: "auth-form-footer", children: jsxRuntime.jsx("button", { type: "button", className: "auth-link", onClick: onForgotPassword, disabled: loading, children: "Forgot password?" }) })), jsxRuntime.jsx("button", { type: "submit", className: "auth-button auth-button-primary", disabled: loading, children: loading ? (jsxRuntime.jsx("span", { className: "auth-spinner" })) : mode === 'login' ? ('Sign in') : ('Create account') }), jsxRuntime.jsxs("div", { className: "auth-divider", children: [jsxRuntime.jsx("span", { children: mode === 'login' ? "Don't have an account?" : 'Already have an account?' }), jsxRuntime.jsx("button", { type: "button", className: "auth-link auth-link-bold", onClick: onModeSwitch, disabled: loading, children: mode === 'login' ? 'Sign up' : 'Sign in' })] })] }));
86
+ };
87
+
88
+ const ProviderButtons = ({ enabledProviders, onGoogleLogin, onPhoneLogin, loading, }) => {
89
+ if (enabledProviders.length === 0)
90
+ return null;
91
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: "auth-or-divider", children: jsxRuntime.jsx("span", { children: "or continue with" }) }), jsxRuntime.jsxs("div", { className: "auth-provider-buttons", children: [enabledProviders.includes('google') && (jsxRuntime.jsxs("button", { type: "button", className: "auth-provider-button", onClick: onGoogleLogin, disabled: loading, children: [jsxRuntime.jsxs("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", children: [jsxRuntime.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" }), jsxRuntime.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" }), jsxRuntime.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" }), jsxRuntime.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" })] }), "Continue with Google"] })), enabledProviders.includes('phone') && (jsxRuntime.jsxs("button", { type: "button", className: "auth-provider-button", onClick: onPhoneLogin, disabled: loading, children: [jsxRuntime.jsx("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", children: jsxRuntime.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" }) }), "Continue with Phone"] }))] })] }));
92
+ };
93
+
94
+ const PhoneAuthForm = ({ onSubmit, onBack, loading, error, }) => {
95
+ const [phoneNumber, setPhoneNumber] = react.useState('');
96
+ const [verificationCode, setVerificationCode] = react.useState('');
97
+ const [codeSent, setCodeSent] = react.useState(false);
98
+ const handleSendCode = async (e) => {
99
+ e.preventDefault();
100
+ await onSubmit(phoneNumber);
101
+ setCodeSent(true);
102
+ };
103
+ const handleVerifyCode = async (e) => {
104
+ e.preventDefault();
105
+ await onSubmit(phoneNumber, verificationCode);
106
+ };
107
+ return (jsxRuntime.jsxs("form", { className: "auth-form", onSubmit: codeSent ? handleVerifyCode : handleSendCode, children: [jsxRuntime.jsxs("div", { className: "auth-form-header", children: [jsxRuntime.jsx("h2", { className: "auth-form-title", children: "Phone Authentication" }), jsxRuntime.jsx("p", { className: "auth-form-subtitle", children: codeSent
108
+ ? 'Enter the verification code sent to your phone.'
109
+ : 'Enter your phone number to receive a verification code.' })] }), error && (jsxRuntime.jsxs("div", { className: "auth-error", role: "alert", children: [jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "currentColor", 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" }) }), error] })), !codeSent ? (jsxRuntime.jsxs("div", { className: "auth-form-group", children: [jsxRuntime.jsx("label", { htmlFor: "phoneNumber", className: "auth-label", children: "Phone Number" }), jsxRuntime.jsx("input", { type: "tel", id: "phoneNumber", className: "auth-input", value: phoneNumber, onChange: (e) => setPhoneNumber(e.target.value), required: true, disabled: loading, placeholder: "+1 (555) 123-4567" })] })) : (jsxRuntime.jsxs("div", { className: "auth-form-group", children: [jsxRuntime.jsx("label", { htmlFor: "verificationCode", className: "auth-label", children: "Verification Code" }), jsxRuntime.jsx("input", { type: "text", id: "verificationCode", className: "auth-input", value: verificationCode, onChange: (e) => setVerificationCode(e.target.value), required: true, disabled: loading, placeholder: "123456", maxLength: 6, pattern: "[0-9]{6}" })] })), jsxRuntime.jsx("button", { type: "submit", className: "auth-button auth-button-primary", disabled: loading, children: loading ? (jsxRuntime.jsx("span", { className: "auth-spinner" })) : codeSent ? ('Verify Code') : ('Send Code') }), jsxRuntime.jsx("div", { className: "auth-divider", children: jsxRuntime.jsx("button", { type: "button", className: "auth-link auth-link-bold", onClick: onBack, disabled: loading, children: "\u2190 Back to login" }) })] }));
110
+ };
111
+
112
+ const PasswordResetForm = ({ onSubmit, onBack, loading, error, success, token, }) => {
113
+ const [email, setEmail] = react.useState('');
114
+ const [password, setPassword] = react.useState('');
115
+ const [confirmPassword, setConfirmPassword] = react.useState('');
116
+ const [passwordError, setPasswordError] = react.useState('');
117
+ const handleSubmit = async (e) => {
118
+ e.preventDefault();
119
+ if (token) {
120
+ // Reset password with token
121
+ if (password !== confirmPassword) {
122
+ setPasswordError('Passwords do not match');
123
+ return;
124
+ }
125
+ if (password.length < 6) {
126
+ setPasswordError('Password must be at least 6 characters');
127
+ return;
128
+ }
129
+ setPasswordError('');
130
+ await onSubmit(password, confirmPassword);
131
+ }
132
+ else {
133
+ // Request password reset
134
+ await onSubmit(email);
135
+ }
136
+ };
137
+ if (success) {
138
+ return (jsxRuntime.jsxs("div", { className: "auth-form", children: [jsxRuntime.jsxs("div", { className: "auth-form-header", children: [jsxRuntime.jsx("div", { style: { textAlign: 'center', marginBottom: '1rem' }, children: jsxRuntime.jsxs("svg", { width: "64", height: "64", viewBox: "0 0 64 64", fill: "none", style: { margin: '0 auto' }, children: [jsxRuntime.jsx("circle", { cx: "32", cy: "32", r: "32", fill: "#10b981", fillOpacity: "0.1" }), jsxRuntime.jsx("path", { d: "M20 32l8 8 16-16", stroke: "#10b981", strokeWidth: "4", strokeLinecap: "round", strokeLinejoin: "round" })] }) }), jsxRuntime.jsx("h2", { className: "auth-form-title", children: token ? 'Password reset!' : 'Check your email' }), jsxRuntime.jsx("p", { className: "auth-form-subtitle", children: token
139
+ ? 'Your password has been successfully reset. You can now sign in with your new password.'
140
+ : `We've sent password reset instructions to ${email}` })] }), jsxRuntime.jsx("button", { type: "button", className: "auth-button auth-button-primary", onClick: onBack, children: "Back to Sign in" })] }));
141
+ }
142
+ return (jsxRuntime.jsxs("form", { className: "auth-form", onSubmit: handleSubmit, children: [jsxRuntime.jsxs("div", { className: "auth-form-header", children: [jsxRuntime.jsx("h2", { className: "auth-form-title", children: token ? 'Set new password' : 'Reset your password' }), jsxRuntime.jsx("p", { className: "auth-form-subtitle", children: token
143
+ ? 'Enter your new password below.'
144
+ : "Enter your email address and we'll send you instructions to reset your password." })] }), (error || passwordError) && (jsxRuntime.jsxs("div", { className: "auth-error", role: "alert", children: [jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "currentColor", 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" }) }), error || passwordError] })), token ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: "auth-form-group", children: [jsxRuntime.jsx("label", { htmlFor: "password", className: "auth-label", children: "New Password" }), jsxRuntime.jsx("input", { type: "password", id: "password", className: "auth-input", value: password, onChange: (e) => setPassword(e.target.value), required: true, disabled: loading, placeholder: "Enter new password", autoComplete: "new-password", minLength: 6 })] }), jsxRuntime.jsxs("div", { className: "auth-form-group", children: [jsxRuntime.jsx("label", { htmlFor: "confirmPassword", className: "auth-label", children: "Confirm Password" }), jsxRuntime.jsx("input", { type: "password", id: "confirmPassword", className: "auth-input", value: confirmPassword, onChange: (e) => setConfirmPassword(e.target.value), required: true, disabled: loading, placeholder: "Confirm new password", autoComplete: "new-password", minLength: 6 })] })] })) : (jsxRuntime.jsxs("div", { className: "auth-form-group", children: [jsxRuntime.jsx("label", { htmlFor: "email", className: "auth-label", children: "Email address" }), jsxRuntime.jsx("input", { type: "email", id: "email", className: "auth-input", value: email, onChange: (e) => setEmail(e.target.value), required: true, disabled: loading, placeholder: "you@example.com", autoComplete: "email" })] })), jsxRuntime.jsx("button", { type: "submit", className: "auth-button auth-button-primary", disabled: loading, children: loading ? (jsxRuntime.jsx("span", { className: "auth-spinner" })) : token ? ('Reset password') : ('Send reset instructions') }), jsxRuntime.jsx("div", { className: "auth-divider", children: jsxRuntime.jsx("button", { type: "button", className: "auth-link auth-link-bold", onClick: onBack, disabled: loading, children: "\u2190 Back to Sign in" }) })] }));
145
+ };
146
+
147
+ /**
148
+ * AuthAPI - Thin wrapper around Smartlinks SDK authKit namespace
149
+ * All authentication operations now use the global Smartlinks SDK
150
+ */
151
+ class AuthAPI {
152
+ constructor(_apiEndpoint, clientId, clientName) {
153
+ // apiEndpoint is kept for backward compatibility but not used
154
+ // Smartlinks SDK is pre-configured globally
155
+ this.clientId = clientId;
156
+ this.clientName = clientName;
157
+ }
158
+ async login(email, password) {
159
+ return smartlinks__namespace.authKit.login(this.clientId, email, password);
160
+ }
161
+ async register(data) {
162
+ return smartlinks__namespace.authKit.register(this.clientId, {
163
+ email: data.email,
164
+ password: data.password,
165
+ displayName: data.displayName,
166
+ accountData: data.accountData,
167
+ });
168
+ }
169
+ async loginWithGoogle(idToken) {
170
+ return smartlinks__namespace.authKit.googleLogin(this.clientId, idToken);
171
+ }
172
+ async sendPhoneCode(phoneNumber) {
173
+ return smartlinks__namespace.authKit.sendPhoneCode(this.clientId, phoneNumber);
174
+ }
175
+ async verifyPhoneCode(verificationId, code) {
176
+ return smartlinks__namespace.authKit.verifyPhoneCode(this.clientId, verificationId, code);
177
+ }
178
+ async requestPasswordReset(email, redirectUrl) {
179
+ return smartlinks__namespace.authKit.requestPasswordReset(this.clientId, {
180
+ email,
181
+ redirectUrl,
182
+ clientName: this.clientName
183
+ });
184
+ }
185
+ async verifyResetToken(token) {
186
+ return smartlinks__namespace.authKit.verifyResetToken(this.clientId, token);
187
+ }
188
+ async completePasswordReset(token, newPassword) {
189
+ return smartlinks__namespace.authKit.completePasswordReset(this.clientId, token, newPassword);
190
+ }
191
+ async sendEmailVerification(userId, email, redirectUrl) {
192
+ return smartlinks__namespace.authKit.sendEmailVerification(this.clientId, {
193
+ userId,
194
+ email,
195
+ redirectUrl,
196
+ clientName: this.clientName
197
+ });
198
+ }
199
+ async verifyEmailWithToken(token) {
200
+ return smartlinks__namespace.authKit.verifyEmail(this.clientId, token);
201
+ }
202
+ async resendVerification(userId, email, redirectUrl) {
203
+ return smartlinks__namespace.authKit.resendEmailVerification(this.clientId, {
204
+ userId,
205
+ email,
206
+ redirectUrl,
207
+ clientName: this.clientName
208
+ });
209
+ }
210
+ async fetchConfig() {
211
+ try {
212
+ return await smartlinks__namespace.authKit.load(this.clientId);
213
+ }
214
+ catch (error) {
215
+ console.warn('Failed to fetch UI config, using defaults:', error);
216
+ return {
217
+ branding: {
218
+ title: 'Smartlinks Auth',
219
+ subtitle: 'Sign in to your account',
220
+ primaryColor: '#3B82F6',
221
+ secondaryColor: '#1D4ED8',
222
+ backgroundColor: '#e0f2fe',
223
+ buttonStyle: 'rounded',
224
+ fontFamily: 'Inter, sans-serif'
225
+ }
226
+ };
227
+ }
228
+ }
229
+ }
230
+
231
+ const TOKEN_KEY = 'smartlinks_auth_token';
232
+ const USER_KEY = 'smartlinks_auth_user';
233
+ const ACCOUNT_DATA_KEY = 'smartlinks_account_data';
234
+ const tokenStorage = {
235
+ saveToken(token, expiresAt) {
236
+ const authToken = {
237
+ token,
238
+ expiresAt: expiresAt || Date.now() + 3600000, // Default 1 hour
239
+ };
240
+ localStorage.setItem(TOKEN_KEY, JSON.stringify(authToken));
241
+ },
242
+ getToken() {
243
+ const stored = localStorage.getItem(TOKEN_KEY);
244
+ if (!stored)
245
+ return null;
246
+ try {
247
+ const authToken = JSON.parse(stored);
248
+ // Check if token is expired
249
+ if (authToken.expiresAt && authToken.expiresAt < Date.now()) {
250
+ this.clearToken();
251
+ return null;
252
+ }
253
+ return authToken;
254
+ }
255
+ catch {
256
+ return null;
257
+ }
258
+ },
259
+ clearToken() {
260
+ localStorage.removeItem(TOKEN_KEY);
261
+ },
262
+ saveUser(user) {
263
+ localStorage.setItem(USER_KEY, JSON.stringify(user));
264
+ },
265
+ getUser() {
266
+ const stored = localStorage.getItem(USER_KEY);
267
+ if (!stored)
268
+ return null;
269
+ try {
270
+ return JSON.parse(stored);
271
+ }
272
+ catch {
273
+ return null;
274
+ }
275
+ },
276
+ clearUser() {
277
+ localStorage.removeItem(USER_KEY);
278
+ },
279
+ clearAll() {
280
+ this.clearToken();
281
+ this.clearUser();
282
+ this.clearAccountData();
283
+ },
284
+ saveAccountData(data) {
285
+ localStorage.setItem(ACCOUNT_DATA_KEY, JSON.stringify(data));
286
+ },
287
+ getAccountData() {
288
+ const stored = localStorage.getItem(ACCOUNT_DATA_KEY);
289
+ if (!stored)
290
+ return null;
291
+ try {
292
+ return JSON.parse(stored);
293
+ }
294
+ catch {
295
+ return null;
296
+ }
297
+ },
298
+ clearAccountData() {
299
+ localStorage.removeItem(ACCOUNT_DATA_KEY);
300
+ },
301
+ };
302
+
303
+ const AuthContext = react.createContext(undefined);
304
+ const AuthProvider = ({ children }) => {
305
+ const [user, setUser] = react.useState(null);
306
+ const [token, setToken] = react.useState(null);
307
+ const [accountData, setAccountData] = react.useState(null);
308
+ const [isLoading, setIsLoading] = react.useState(true);
309
+ // Initialize auth state from localStorage
310
+ react.useEffect(() => {
311
+ const storedToken = tokenStorage.getToken();
312
+ const storedUser = tokenStorage.getUser();
313
+ const storedAccountData = tokenStorage.getAccountData();
314
+ if (storedToken && storedUser) {
315
+ setToken(storedToken.token);
316
+ setUser(storedUser);
317
+ setAccountData(storedAccountData);
318
+ // Set bearer token in global Smartlinks SDK
319
+ // @ts-expect-error - setBearerToken exists in runtime but may not be in type definitions
320
+ smartlinks__namespace.setBearerToken(storedToken.token);
321
+ }
322
+ setIsLoading(false);
323
+ }, []);
324
+ const login = react.useCallback((authToken, authUser, authAccountData) => {
325
+ // Store token, user, and account data
326
+ tokenStorage.saveToken(authToken);
327
+ tokenStorage.saveUser(authUser);
328
+ if (authAccountData) {
329
+ tokenStorage.saveAccountData(authAccountData);
330
+ }
331
+ setToken(authToken);
332
+ setUser(authUser);
333
+ setAccountData(authAccountData || null);
334
+ // Set bearer token in global Smartlinks SDK
335
+ // @ts-expect-error - setBearerToken exists in runtime but may not be in type definitions
336
+ smartlinks__namespace.setBearerToken(authToken);
337
+ }, []);
338
+ const logout = react.useCallback(async () => {
339
+ // Clear local storage
340
+ tokenStorage.clearAll();
341
+ setToken(null);
342
+ setUser(null);
343
+ setAccountData(null);
344
+ // Clear bearer token from global Smartlinks SDK
345
+ // @ts-expect-error - setBearerToken exists in runtime but may not be in type definitions
346
+ smartlinks__namespace.setBearerToken(undefined);
347
+ }, []);
348
+ const getToken = react.useCallback(() => {
349
+ const storedToken = tokenStorage.getToken();
350
+ return storedToken ? storedToken.token : null;
351
+ }, []);
352
+ const refreshToken = react.useCallback(async () => {
353
+ throw new Error('Token refresh must be implemented via your backend API');
354
+ }, []);
355
+ const value = {
356
+ user,
357
+ token,
358
+ accountData,
359
+ isAuthenticated: !!token && !!user,
360
+ isLoading,
361
+ login,
362
+ logout,
363
+ getToken,
364
+ refreshToken,
365
+ };
366
+ return jsxRuntime.jsx(AuthContext.Provider, { value: value, children: children });
367
+ };
368
+ const useAuth = () => {
369
+ const context = react.useContext(AuthContext);
370
+ if (context === undefined) {
371
+ throw new Error('useAuth must be used within an AuthProvider');
372
+ }
373
+ return context;
374
+ };
375
+
376
+ const ProtectedRoute = ({ children, fallback, redirectTo, }) => {
377
+ const { isAuthenticated, isLoading } = useAuth();
378
+ // Show loading state
379
+ if (isLoading) {
380
+ return jsxRuntime.jsx("div", { children: "Loading..." });
381
+ }
382
+ // If not authenticated, redirect or show fallback
383
+ if (!isAuthenticated) {
384
+ if (redirectTo) {
385
+ window.location.href = redirectTo;
386
+ return null;
387
+ }
388
+ return fallback ? jsxRuntime.jsx(jsxRuntime.Fragment, { children: fallback }) : jsxRuntime.jsx("div", { children: "Access denied. Please log in." });
389
+ }
390
+ // Render protected content
391
+ return jsxRuntime.jsx(jsxRuntime.Fragment, { children: children });
392
+ };
393
+
394
+ const AuthUIPreview = ({ customization, enabledProviders = ['email', 'google', 'phone'], theme = 'light', className, }) => {
395
+ const showEmail = enabledProviders.includes('email');
396
+ const showGoogle = enabledProviders.includes('google');
397
+ const showPhone = enabledProviders.includes('phone');
398
+ const hasProviders = showGoogle || showPhone;
399
+ return (jsxRuntime.jsxs(AuthContainer, { theme: theme, className: className, config: customization, children: [showEmail && (jsxRuntime.jsxs("div", { className: "auth-form", children: [jsxRuntime.jsxs("div", { className: "auth-form-group", children: [jsxRuntime.jsx("label", { className: "auth-label", children: "Email" }), jsxRuntime.jsx("input", { type: "email", className: "auth-input", placeholder: "Enter your email", disabled: true })] }), jsxRuntime.jsxs("div", { className: "auth-form-group", children: [jsxRuntime.jsx("label", { className: "auth-label", children: "Password" }), jsxRuntime.jsx("input", { type: "password", className: "auth-input", placeholder: "Enter your password", disabled: true })] }), jsxRuntime.jsx("button", { className: "auth-button auth-button-primary", disabled: true, children: "Sign In" }), jsxRuntime.jsx("div", { style: { textAlign: 'center', marginTop: '1rem' }, children: jsxRuntime.jsx("button", { className: "auth-link", disabled: true, children: "Forgot password?" }) }), jsxRuntime.jsxs("div", { style: { textAlign: 'center', marginTop: '0.5rem', fontSize: '0.875rem', color: 'var(--auth-text-muted, #6B7280)' }, children: ["Don't have an account?", ' ', jsxRuntime.jsx("button", { className: "auth-link", disabled: true, children: "Sign up" })] })] })), hasProviders && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [showEmail && (jsxRuntime.jsx("div", { className: "auth-or-divider", children: jsxRuntime.jsx("span", { children: "or continue with" }) })), jsxRuntime.jsxs("div", { className: "auth-provider-buttons", children: [showGoogle && (jsxRuntime.jsxs("button", { className: "auth-provider-button", disabled: true, children: [jsxRuntime.jsxs("svg", { width: "18", height: "18", viewBox: "0 0 18 18", fill: "none", children: [jsxRuntime.jsx("path", { d: "M17.64 9.2c0-.637-.057-1.251-.164-1.84H9v3.481h4.844c-.209 1.125-.843 2.078-1.796 2.717v2.258h2.908c1.702-1.567 2.684-3.874 2.684-6.615z", fill: "#4285F4" }), jsxRuntime.jsx("path", { d: "M9 18c2.43 0 4.467-.806 5.956-2.183l-2.908-2.259c-.806.54-1.837.86-3.048.86-2.344 0-4.328-1.584-5.036-3.711H.957v2.332C2.438 15.983 5.482 18 9 18z", fill: "#34A853" }), jsxRuntime.jsx("path", { d: "M3.964 10.707c-.18-.54-.282-1.117-.282-1.707 0-.593.102-1.167.282-1.707V4.961H.957C.347 6.175 0 7.548 0 9s.348 2.825.957 4.039l3.007-2.332z", fill: "#FBBC05" }), jsxRuntime.jsx("path", { d: "M9 3.58c1.321 0 2.508.454 3.44 1.345l2.582-2.58C13.463.891 11.426 0 9 0 5.482 0 2.438 2.017.957 4.958L3.964 7.29C4.672 5.163 6.656 3.58 9 3.58z", fill: "#EA4335" })] }), jsxRuntime.jsx("span", { children: "Google" })] })), showPhone && (jsxRuntime.jsxs("button", { className: "auth-provider-button", disabled: true, children: [jsxRuntime.jsx("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: jsxRuntime.jsx("path", { d: "M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z" }) }), jsxRuntime.jsx("span", { children: "Phone" })] }))] })] }))] }));
400
+ };
401
+
402
+ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAuthSuccess, onAuthError, enabledProviders = ['email', 'google', 'phone'], redirectUrl, theme = 'light', className, customization, skipConfigFetch = false, }) => {
403
+ const [mode, setMode] = react.useState('login');
404
+ const [loading, setLoading] = react.useState(false);
405
+ const [error, setError] = react.useState();
406
+ const [resetSuccess, setResetSuccess] = react.useState(false);
407
+ const [authSuccess, setAuthSuccess] = react.useState(false);
408
+ const [successMessage, setSuccessMessage] = react.useState();
409
+ const [verificationId, setVerificationId] = react.useState();
410
+ const [showResendVerification, setShowResendVerification] = react.useState(false);
411
+ const [resendEmail, setResendEmail] = react.useState();
412
+ const [showRequestNewReset, setShowRequestNewReset] = react.useState(false);
413
+ const [resetRequestEmail, setResetRequestEmail] = react.useState();
414
+ const [resetToken, setResetToken] = react.useState(); // Store the reset token from URL
415
+ const [config, setConfig] = react.useState(null);
416
+ const [configLoading, setConfigLoading] = react.useState(!skipConfigFetch);
417
+ const api = new AuthAPI(apiEndpoint, clientId, clientName);
418
+ const auth = useAuth();
419
+ // Get the effective redirect URL (use prop or default to current page)
420
+ const getRedirectUrl = () => {
421
+ if (redirectUrl)
422
+ return redirectUrl;
423
+ // Get the full current URL including hash routes
424
+ // Remove any existing query parameters to avoid duplication
425
+ const currentUrl = window.location.href.split('?')[0];
426
+ return currentUrl;
427
+ };
428
+ // Fetch UI configuration
429
+ react.useEffect(() => {
430
+ if (skipConfigFetch) {
431
+ setConfig(customization || {});
432
+ return;
433
+ }
434
+ const fetchConfig = async () => {
435
+ try {
436
+ // Check localStorage cache first
437
+ const cacheKey = `auth_ui_config_${clientId || 'default'}`;
438
+ const cached = localStorage.getItem(cacheKey);
439
+ if (cached) {
440
+ const { config: cachedConfig, timestamp } = JSON.parse(cached);
441
+ const age = Date.now() - timestamp;
442
+ // Use cache if less than 1 hour old
443
+ if (age < 3600000) {
444
+ setConfig({ ...cachedConfig, ...customization });
445
+ setConfigLoading(false);
446
+ // Fetch in background to update cache
447
+ api.fetchConfig().then(freshConfig => {
448
+ localStorage.setItem(cacheKey, JSON.stringify({
449
+ config: freshConfig,
450
+ timestamp: Date.now()
451
+ }));
452
+ });
453
+ return;
454
+ }
455
+ }
456
+ // Fetch from API
457
+ const fetchedConfig = await api.fetchConfig();
458
+ // Merge with customization props (props take precedence)
459
+ const mergedConfig = { ...fetchedConfig, ...customization };
460
+ setConfig(mergedConfig);
461
+ // Cache the fetched config
462
+ localStorage.setItem(cacheKey, JSON.stringify({
463
+ config: fetchedConfig,
464
+ timestamp: Date.now()
465
+ }));
466
+ }
467
+ catch (err) {
468
+ console.error('Failed to fetch config:', err);
469
+ setConfig(customization || {});
470
+ }
471
+ finally {
472
+ setConfigLoading(false);
473
+ }
474
+ };
475
+ fetchConfig();
476
+ }, [apiEndpoint, clientId, customization, skipConfigFetch]);
477
+ // Handle URL-based auth flows (email verification, password reset)
478
+ react.useEffect(() => {
479
+ // Helper to get URL parameters from either hash or search
480
+ const getUrlParams = () => {
481
+ // First check if there are params in the hash (for hash routing)
482
+ const hash = window.location.hash;
483
+ const hashQueryIndex = hash.indexOf('?');
484
+ if (hashQueryIndex !== -1) {
485
+ // Extract query string from hash (e.g., #/test?mode=verifyEmail&token=abc)
486
+ const hashQuery = hash.substring(hashQueryIndex + 1);
487
+ return new URLSearchParams(hashQuery);
488
+ }
489
+ // Fall back to regular search params (for non-hash routing)
490
+ return new URLSearchParams(window.location.search);
491
+ };
492
+ const params = getUrlParams();
493
+ const urlMode = params.get('mode');
494
+ const token = params.get('token');
495
+ console.log('URL params detected:', { urlMode, token, hash: window.location.hash, search: window.location.search });
496
+ if (urlMode && token) {
497
+ handleURLBasedAuth(urlMode, token);
498
+ }
499
+ }, []);
500
+ const handleURLBasedAuth = async (urlMode, token) => {
501
+ setLoading(true);
502
+ setError(undefined);
503
+ try {
504
+ if (urlMode === 'verifyEmail') {
505
+ console.log('Verifying email with token:', token);
506
+ const response = await api.verifyEmailWithToken(token);
507
+ // Get email verification mode from config (default: verify-then-auto-login)
508
+ const verificationMode = config?.emailVerification?.mode || 'verify-then-auto-login';
509
+ if (verificationMode === 'verify-then-auto-login' || verificationMode === 'immediate') {
510
+ // Auto-login modes: Log the user in immediately
511
+ auth.login(response.token, response.user, response.accountData);
512
+ setAuthSuccess(true);
513
+ setSuccessMessage('Email verified successfully! You are now logged in.');
514
+ onAuthSuccess(response.token, response.user, response.accountData);
515
+ // Clear the URL parameters
516
+ const cleanUrl = window.location.href.split('?')[0];
517
+ window.history.replaceState({}, document.title, cleanUrl);
518
+ // Redirect after a brief delay to show success message
519
+ if (redirectUrl) {
520
+ setTimeout(() => {
521
+ window.location.href = redirectUrl;
522
+ }, 2000);
523
+ }
524
+ }
525
+ else {
526
+ // verify-then-manual-login mode: Show success but require manual login
527
+ setAuthSuccess(true);
528
+ setSuccessMessage('Email verified successfully! Please log in with your credentials.');
529
+ // Clear the URL parameters
530
+ const cleanUrl = window.location.href.split('?')[0];
531
+ window.history.replaceState({}, document.title, cleanUrl);
532
+ // Switch back to login mode after a delay
533
+ setTimeout(() => {
534
+ setAuthSuccess(false);
535
+ setMode('login');
536
+ }, 3000);
537
+ }
538
+ }
539
+ else if (urlMode === 'resetPassword') {
540
+ console.log('Verifying reset token:', token);
541
+ // Verify token is valid, then show password reset form
542
+ await api.verifyResetToken(token);
543
+ setResetToken(token); // Store token for use in password reset
544
+ setMode('reset-password');
545
+ // Clear the URL parameters
546
+ const cleanUrl = window.location.href.split('?')[0];
547
+ window.history.replaceState({}, document.title, cleanUrl);
548
+ }
549
+ }
550
+ catch (err) {
551
+ console.error('URL-based auth error:', err);
552
+ const errorMessage = err instanceof Error ? err.message : 'An error occurred';
553
+ // If it's an email verification error (expired/invalid token), show resend option
554
+ if (urlMode === 'verifyEmail') {
555
+ setError(`${errorMessage} - Please enter your email below to receive a new verification link.`);
556
+ setShowResendVerification(true);
557
+ setMode('login'); // Show the login form UI
558
+ // Clear the URL parameters
559
+ const cleanUrl = window.location.href.split('?')[0];
560
+ window.history.replaceState({}, document.title, cleanUrl);
561
+ }
562
+ else if (urlMode === 'resetPassword') {
563
+ // If password reset token is invalid/expired, show request new reset link option
564
+ setError(`${errorMessage} - Please enter your email below to receive a new password reset link.`);
565
+ setShowRequestNewReset(true);
566
+ setMode('login');
567
+ // Clear the URL parameters
568
+ const cleanUrl = window.location.href.split('?')[0];
569
+ window.history.replaceState({}, document.title, cleanUrl);
570
+ }
571
+ else {
572
+ setError(errorMessage);
573
+ }
574
+ onAuthError?.(err instanceof Error ? err : new Error('An error occurred'));
575
+ }
576
+ finally {
577
+ setLoading(false);
578
+ }
579
+ };
580
+ const handleEmailAuth = async (data) => {
581
+ setLoading(true);
582
+ setError(undefined);
583
+ setAuthSuccess(false);
584
+ try {
585
+ const response = mode === 'login'
586
+ ? await api.login(data.email, data.password)
587
+ : await api.register({
588
+ ...data,
589
+ accountData: mode === 'register' ? accountData : undefined,
590
+ redirectUrl: getRedirectUrl(), // Include redirect URL for email verification
591
+ });
592
+ // Get email verification mode from config (default: verify-then-auto-login)
593
+ const verificationMode = config?.emailVerification?.mode || 'verify-then-auto-login';
594
+ const gracePeriodHours = config?.emailVerification?.gracePeriodHours || 24;
595
+ if (mode === 'register') {
596
+ // Handle different verification modes
597
+ if (verificationMode === 'immediate') {
598
+ // Immediate mode: Log in right away
599
+ auth.login(response.token, response.user, response.accountData);
600
+ setAuthSuccess(true);
601
+ setSuccessMessage(`Account created! You're logged in now. Please verify your email within ${gracePeriodHours} hours to keep your account active.`);
602
+ onAuthSuccess(response.token, response.user, response.accountData);
603
+ if (redirectUrl) {
604
+ setTimeout(() => {
605
+ window.location.href = redirectUrl;
606
+ }, 2000);
607
+ }
608
+ }
609
+ else if (verificationMode === 'verify-then-auto-login') {
610
+ // Verify-then-auto-login mode: Don't log in yet, but will auto-login after email verification
611
+ setAuthSuccess(true);
612
+ setSuccessMessage('Account created! Please check your email and click the verification link to complete your registration.');
613
+ }
614
+ else {
615
+ // verify-then-manual-login mode: Traditional flow
616
+ setAuthSuccess(true);
617
+ setSuccessMessage('Account created successfully! Please check your email to verify your account, then log in.');
618
+ }
619
+ }
620
+ else {
621
+ // Login mode - always log in
622
+ auth.login(response.token, response.user, response.accountData);
623
+ setAuthSuccess(true);
624
+ setSuccessMessage('Login successful!');
625
+ onAuthSuccess(response.token, response.user, response.accountData);
626
+ if (redirectUrl) {
627
+ setTimeout(() => {
628
+ window.location.href = redirectUrl;
629
+ }, 2000);
630
+ }
631
+ }
632
+ }
633
+ catch (err) {
634
+ const errorMessage = err instanceof Error ? err.message : 'Authentication failed';
635
+ // Check if error is about email already registered
636
+ if (mode === 'register' && errorMessage.toLowerCase().includes('already') && errorMessage.toLowerCase().includes('email')) {
637
+ setShowResendVerification(true);
638
+ setResendEmail(data.email);
639
+ setError('This email is already registered. If you didn\'t receive the verification email, you can resend it below.');
640
+ }
641
+ else {
642
+ setError(errorMessage);
643
+ }
644
+ onAuthError?.(err instanceof Error ? err : new Error(errorMessage));
645
+ }
646
+ finally {
647
+ setLoading(false);
648
+ }
649
+ };
650
+ const handleResendVerification = async () => {
651
+ if (!resendEmail)
652
+ return;
653
+ setLoading(true);
654
+ setError(undefined);
655
+ try {
656
+ // For resend, we need the userId. If we don't have it, we need to handle this differently
657
+ // The backend should ideally handle this case
658
+ await api.resendVerification('unknown', resendEmail, getRedirectUrl());
659
+ setAuthSuccess(true);
660
+ setSuccessMessage('Verification email sent! Please check your inbox.');
661
+ setShowResendVerification(false);
662
+ }
663
+ catch (err) {
664
+ const errorMessage = err instanceof Error ? err.message : 'Failed to resend verification email';
665
+ setError(errorMessage);
666
+ onAuthError?.(err instanceof Error ? err : new Error(errorMessage));
667
+ }
668
+ finally {
669
+ setLoading(false);
670
+ }
671
+ };
672
+ const handleRequestNewReset = async () => {
673
+ if (!resetRequestEmail)
674
+ return;
675
+ setLoading(true);
676
+ setError(undefined);
677
+ try {
678
+ await api.requestPasswordReset(resetRequestEmail, getRedirectUrl());
679
+ setAuthSuccess(true);
680
+ setSuccessMessage('Password reset email sent! Please check your inbox.');
681
+ setShowRequestNewReset(false);
682
+ setResetRequestEmail('');
683
+ }
684
+ catch (err) {
685
+ const errorMessage = err instanceof Error ? err.message : 'Failed to send password reset email';
686
+ setError(errorMessage);
687
+ onAuthError?.(err instanceof Error ? err : new Error(errorMessage));
688
+ }
689
+ finally {
690
+ setLoading(false);
691
+ }
692
+ };
693
+ const handleGoogleLogin = async () => {
694
+ setLoading(true);
695
+ setError(undefined);
696
+ try {
697
+ // In a real implementation, this would open Google OAuth flow
698
+ // For now, this is a placeholder that the consuming app would implement
699
+ throw new Error('Google OAuth flow must be implemented in your application');
700
+ }
701
+ catch (err) {
702
+ const errorMessage = err instanceof Error ? err.message : 'Google login failed';
703
+ setError(errorMessage);
704
+ onAuthError?.(err instanceof Error ? err : new Error(errorMessage));
705
+ }
706
+ finally {
707
+ setLoading(false);
708
+ }
709
+ };
710
+ const handlePhoneAuth = async (phoneNumber, verificationCode) => {
711
+ setLoading(true);
712
+ setError(undefined);
713
+ try {
714
+ if (!verificationCode) {
715
+ // Send verification code
716
+ const result = await api.sendPhoneCode(phoneNumber);
717
+ setVerificationId(result.verificationId);
718
+ }
719
+ else {
720
+ // Verify code
721
+ if (!verificationId) {
722
+ throw new Error('Verification ID not found');
723
+ }
724
+ const response = await api.verifyPhoneCode(verificationId, verificationCode);
725
+ // Update auth context with account data
726
+ auth.login(response.token, response.user, response.accountData);
727
+ onAuthSuccess(response.token, response.user, response.accountData);
728
+ if (redirectUrl) {
729
+ window.location.href = redirectUrl;
730
+ }
731
+ }
732
+ }
733
+ catch (err) {
734
+ const errorMessage = err instanceof Error ? err.message : 'Phone authentication failed';
735
+ setError(errorMessage);
736
+ onAuthError?.(err instanceof Error ? err : new Error(errorMessage));
737
+ }
738
+ finally {
739
+ setLoading(false);
740
+ }
741
+ };
742
+ const handlePasswordReset = async (emailOrPassword, confirmPassword) => {
743
+ setLoading(true);
744
+ setError(undefined);
745
+ try {
746
+ if (resetToken && confirmPassword) {
747
+ // Complete password reset with token
748
+ await api.completePasswordReset(resetToken, emailOrPassword);
749
+ setResetSuccess(true);
750
+ setResetToken(undefined); // Clear token after successful reset
751
+ }
752
+ else {
753
+ // Request password reset email
754
+ await api.requestPasswordReset(emailOrPassword, getRedirectUrl());
755
+ setResetSuccess(true);
756
+ }
757
+ }
758
+ catch (err) {
759
+ const errorMessage = err instanceof Error ? err.message : 'Password reset failed';
760
+ setError(errorMessage);
761
+ onAuthError?.(err instanceof Error ? err : new Error(errorMessage));
762
+ }
763
+ finally {
764
+ setLoading(false);
765
+ }
766
+ };
767
+ if (configLoading) {
768
+ return (jsxRuntime.jsx(AuthContainer, { theme: theme, className: className, children: jsxRuntime.jsx("div", { style: { textAlign: 'center', padding: '2rem' }, children: jsxRuntime.jsx("div", { className: "auth-spinner" }) }) }));
769
+ }
770
+ return (jsxRuntime.jsx(AuthContainer, { theme: theme, className: className, config: config, children: authSuccess ? (jsxRuntime.jsxs("div", { style: { textAlign: 'center', padding: '2rem' }, children: [jsxRuntime.jsx("div", { style: {
771
+ color: 'var(--auth-primary-color, #4F46E5)',
772
+ fontSize: '3rem',
773
+ marginBottom: '1rem'
774
+ }, children: "\u2713" }), jsxRuntime.jsx("h2", { style: {
775
+ marginBottom: '0.5rem',
776
+ fontSize: '1.5rem',
777
+ fontWeight: 600
778
+ }, children: successMessage?.includes('verified') ? 'Email Verified!' :
779
+ mode === 'register' ? 'Account Created!' : 'Login Successful!' }), jsxRuntime.jsx("p", { style: {
780
+ color: '#6B7280',
781
+ fontSize: '0.875rem'
782
+ }, children: successMessage })] })) : mode === 'phone' ? (jsxRuntime.jsx(PhoneAuthForm, { onSubmit: handlePhoneAuth, onBack: () => setMode('login'), loading: loading, error: error })) : mode === 'reset-password' ? (jsxRuntime.jsx(PasswordResetForm, { onSubmit: handlePasswordReset, onBack: () => {
783
+ setMode('login');
784
+ setResetSuccess(false);
785
+ setResetToken(undefined); // Clear token when going back
786
+ }, loading: loading, error: error, success: resetSuccess, token: resetToken })) : (mode === 'login' || mode === 'register') ? (jsxRuntime.jsx(jsxRuntime.Fragment, { children: showResendVerification ? (jsxRuntime.jsxs("div", { style: { marginTop: '1rem', padding: '1.5rem', backgroundColor: 'rgba(79, 70, 229, 0.05)', borderRadius: '0.5rem' }, children: [jsxRuntime.jsx("h3", { style: { marginBottom: '0.75rem', fontSize: '1rem', fontWeight: 600, color: 'var(--auth-text-color, #374151)' }, children: "Verification Link Expired" }), jsxRuntime.jsx("p", { style: { marginBottom: '1rem', fontSize: '0.875rem', color: 'var(--auth-text-color, #6B7280)', lineHeight: '1.5' }, children: "Your verification link has expired or is no longer valid. Please enter your email address below and we'll send you a new verification link." }), jsxRuntime.jsx("input", { type: "email", value: resendEmail || '', onChange: (e) => setResendEmail(e.target.value), placeholder: "your@email.com", style: {
787
+ width: '100%',
788
+ padding: '0.625rem',
789
+ marginBottom: '1rem',
790
+ border: '1px solid var(--auth-border-color, #D1D5DB)',
791
+ borderRadius: '0.375rem',
792
+ fontSize: '0.875rem',
793
+ boxSizing: 'border-box'
794
+ } }), jsxRuntime.jsxs("div", { style: { display: 'flex', gap: '0.75rem' }, children: [jsxRuntime.jsx("button", { onClick: handleResendVerification, disabled: loading || !resendEmail, style: {
795
+ flex: 1,
796
+ padding: '0.625rem 1rem',
797
+ backgroundColor: 'var(--auth-primary-color, #4F46E5)',
798
+ color: 'white',
799
+ border: 'none',
800
+ borderRadius: '0.375rem',
801
+ cursor: (loading || !resendEmail) ? 'not-allowed' : 'pointer',
802
+ fontSize: '0.875rem',
803
+ fontWeight: 500,
804
+ opacity: (loading || !resendEmail) ? 0.6 : 1
805
+ }, children: loading ? 'Sending...' : 'Send New Verification Link' }), jsxRuntime.jsx("button", { onClick: () => {
806
+ setShowResendVerification(false);
807
+ setResendEmail('');
808
+ setError(undefined);
809
+ }, disabled: loading, style: {
810
+ padding: '0.625rem 1rem',
811
+ backgroundColor: 'transparent',
812
+ color: 'var(--auth-text-color, #6B7280)',
813
+ border: '1px solid var(--auth-border-color, #D1D5DB)',
814
+ borderRadius: '0.375rem',
815
+ cursor: loading ? 'not-allowed' : 'pointer',
816
+ fontSize: '0.875rem',
817
+ fontWeight: 500,
818
+ opacity: loading ? 0.6 : 1
819
+ }, children: "Cancel" })] })] })) : showRequestNewReset ? (jsxRuntime.jsxs("div", { style: { marginTop: '1rem', padding: '1.5rem', backgroundColor: 'rgba(239, 68, 68, 0.05)', borderRadius: '0.5rem' }, children: [jsxRuntime.jsx("h3", { style: { marginBottom: '0.75rem', fontSize: '1rem', fontWeight: 600, color: 'var(--auth-text-color, #374151)' }, children: "Password Reset Link Expired" }), jsxRuntime.jsx("p", { style: { marginBottom: '1rem', fontSize: '0.875rem', color: 'var(--auth-text-color, #6B7280)', lineHeight: '1.5' }, children: "Your password reset link has expired or is no longer valid. Please enter your email address below and we'll send you a new password reset link." }), jsxRuntime.jsx("input", { type: "email", value: resetRequestEmail || '', onChange: (e) => setResetRequestEmail(e.target.value), placeholder: "your@email.com", style: {
820
+ width: '100%',
821
+ padding: '0.625rem',
822
+ marginBottom: '1rem',
823
+ border: '1px solid var(--auth-border-color, #D1D5DB)',
824
+ borderRadius: '0.375rem',
825
+ fontSize: '0.875rem',
826
+ boxSizing: 'border-box'
827
+ } }), jsxRuntime.jsxs("div", { style: { display: 'flex', gap: '0.75rem' }, children: [jsxRuntime.jsx("button", { onClick: handleRequestNewReset, disabled: loading || !resetRequestEmail, style: {
828
+ flex: 1,
829
+ padding: '0.625rem 1rem',
830
+ backgroundColor: '#EF4444',
831
+ color: 'white',
832
+ border: 'none',
833
+ borderRadius: '0.375rem',
834
+ cursor: (loading || !resetRequestEmail) ? 'not-allowed' : 'pointer',
835
+ fontSize: '0.875rem',
836
+ fontWeight: 500,
837
+ opacity: (loading || !resetRequestEmail) ? 0.6 : 1
838
+ }, children: loading ? 'Sending...' : 'Send New Reset Link' }), jsxRuntime.jsx("button", { onClick: () => {
839
+ setShowRequestNewReset(false);
840
+ setResetRequestEmail('');
841
+ setError(undefined);
842
+ }, disabled: loading, style: {
843
+ padding: '0.625rem 1rem',
844
+ backgroundColor: 'transparent',
845
+ color: 'var(--auth-text-color, #6B7280)',
846
+ border: '1px solid var(--auth-border-color, #D1D5DB)',
847
+ borderRadius: '0.375rem',
848
+ cursor: loading ? 'not-allowed' : 'pointer',
849
+ fontSize: '0.875rem',
850
+ fontWeight: 500,
851
+ opacity: loading ? 0.6 : 1
852
+ }, children: "Cancel" })] })] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(EmailAuthForm, { mode: mode, onSubmit: handleEmailAuth, onModeSwitch: () => {
853
+ setMode(mode === 'login' ? 'register' : 'login');
854
+ setShowResendVerification(false);
855
+ setShowRequestNewReset(false);
856
+ setError(undefined);
857
+ }, onForgotPassword: () => setMode('reset-password'), loading: loading, error: error }), ((config?.enabledProviders || enabledProviders).length > 1) && (jsxRuntime.jsx(ProviderButtons, { enabledProviders: (config?.enabledProviders || enabledProviders).filter((p) => p !== 'email'), onGoogleLogin: handleGoogleLogin, onPhoneLogin: () => setMode('phone'), loading: loading }))] })) })) : null }));
858
+ };
859
+
860
+ exports.AuthProvider = AuthProvider;
861
+ exports.AuthUIPreview = AuthUIPreview;
862
+ exports.FirebaseAuthUI = SmartlinksAuthUI;
863
+ exports.ProtectedRoute = ProtectedRoute;
864
+ exports.SmartlinksAuthUI = SmartlinksAuthUI;
865
+ exports.tokenStorage = tokenStorage;
866
+ exports.useAuth = useAuth;
867
+ //# sourceMappingURL=index.js.map