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