@proveanything/smartlinks-auth-ui 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -23,10 +23,21 @@ npm install @smartlinks/auth-ui
23
23
 
24
24
  ## Quick Start
25
25
 
26
- ### Basic Usage with Session Management
26
+ ### 1. Import the CSS (Required!)
27
+
28
+ Add this import to your app's entry point (e.g., `main.tsx`, `index.tsx`, or `App.tsx`):
29
+
30
+ ```tsx
31
+ import '@smartlinks/auth-ui/dist/index.css';
32
+ ```
33
+
34
+ **Important**: Without this CSS import, the authentication UI will not be styled correctly.
35
+
36
+ ### 2. Basic Usage with Session Management
27
37
 
28
38
  ```tsx
29
39
  import { SmartlinksAuthUI, AuthProvider, useAuth } from '@smartlinks/auth-ui';
40
+ import '@smartlinks/auth-ui/dist/index.css'; // Don't forget this!
30
41
 
31
42
  function App() {
32
43
  return (
@@ -298,24 +309,111 @@ Response:
298
309
 
299
310
  ## Styling
300
311
 
301
- The component uses CSS modules and can be customized:
312
+ ### CSS Import (Required)
313
+
314
+ The module requires you to import the CSS file for proper styling:
315
+
316
+ ```tsx
317
+ import '@smartlinks/auth-ui/dist/index.css';
318
+ ```
319
+
320
+ Add this to your app's entry point (`main.tsx`, `index.tsx`, or `App.tsx`).
321
+
322
+ ### Theme Support
323
+
324
+ The component supports light and dark themes:
302
325
 
303
326
  ```tsx
304
327
  <SmartlinksAuthUI
305
328
  apiEndpoint="https://api.smartlinks.com"
306
329
  clientId="your-client-123"
307
330
  onAuthSuccess={handleAuth}
308
- className="custom-auth-container"
309
- theme="dark"
331
+ theme="dark" // or "light" (default)
310
332
  />
311
333
  ```
312
334
 
335
+ ### Custom Styling
336
+
337
+ You can customize the appearance by:
338
+
339
+ 1. **Using the customization prop** (recommended):
340
+ ```tsx
341
+ <SmartlinksAuthUI
342
+ customization={{
343
+ branding: {
344
+ primaryColor: "#6366f1",
345
+ secondaryColor: "#4f46e5",
346
+ backgroundColor: "#f0f9ff",
347
+ fontFamily: "Inter, sans-serif"
348
+ }
349
+ }}
350
+ />
351
+ ```
352
+
353
+ 2. **Overriding CSS classes** in your own stylesheet:
354
+ ```css
355
+ /* In your app's CSS file */
356
+ .auth-container {
357
+ /* Your custom styles */
358
+ }
359
+
360
+ .auth-button-primary {
361
+ /* Your custom button styles */
362
+ }
363
+ ```
364
+
365
+ 3. **Using CSS custom properties** for quick theming:
366
+ ```css
367
+ .auth-container {
368
+ --auth-primary-color: #6366f1;
369
+ --auth-bg-color: #f0f9ff;
370
+ --auth-font-family: "Inter", sans-serif;
371
+ }
372
+ ```
373
+
374
+ ## Troubleshooting
375
+
376
+ ### Styles Not Appearing
377
+
378
+ **Problem**: The authentication UI appears unstyled or has no layout.
379
+
380
+ **Solution**: Make sure you've imported the CSS file:
381
+ ```tsx
382
+ import '@smartlinks/auth-ui/dist/index.css';
383
+ ```
384
+
385
+ This import should be at the top of your app's entry point (before your component imports).
386
+
387
+ ### Build Errors
388
+
389
+ **Problem**: `Cannot find module '@smartlinks/auth-ui/dist/index.css'`
390
+
391
+ **Solution**:
392
+ 1. Verify the package is installed: `npm install @smartlinks/auth-ui`
393
+ 2. Ensure your bundler (Vite, Webpack) supports CSS imports
394
+ 3. Try clearing your node_modules and reinstalling
395
+
396
+ ### TypeScript Errors
397
+
398
+ **Problem**: Type errors when using the components.
399
+
400
+ **Solution**: The package includes TypeScript definitions. Make sure:
401
+ ```tsx
402
+ import type { AuthUser, AuthToken, AuthProvider } from '@smartlinks/auth-ui';
403
+ ```
404
+
313
405
  ## TypeScript Support
314
406
 
315
407
  Full TypeScript definitions included:
316
408
 
317
409
  ```tsx
318
- import type { AuthUser, AuthToken, AuthProvider } from '@smartlinks/auth-ui';
410
+ import type {
411
+ AuthUser,
412
+ AuthToken,
413
+ AuthProvider,
414
+ AuthUIConfig,
415
+ SmartlinksAuthUIProps
416
+ } from '@smartlinks/auth-ui';
319
417
  ```
320
418
 
321
419
  ## License
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAQnD,OAAO,KAAK,EAAE,qBAAqB,EAAyC,MAAM,SAAS,CAAC;AAE5F,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAwnB5D,CAAC;AAGF,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAGpD,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAG3D,mBAAmB,SAAS,CAAC;AAG7B,OAAO,EAAE,gBAAgB,IAAI,cAAc,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAQnD,OAAO,KAAK,EAAE,qBAAqB,EAAyC,MAAM,SAAS,CAAC;AAK5F,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CA+sB5D,CAAC;AAGF,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAGpD,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAG3D,mBAAmB,SAAS,CAAC;AAG7B,OAAO,EAAE,gBAAgB,IAAI,cAAc,EAAE,CAAC"}
package/dist/index.esm.js CHANGED
@@ -378,6 +378,8 @@ const AuthUIPreview = ({ customization, enabledProviders = ['email', 'google', '
378
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
379
  };
380
380
 
381
+ // Default Smartlinks Google OAuth Client ID (public - safe to expose)
382
+ const DEFAULT_GOOGLE_CLIENT_ID = '696509063554-jdlbjl8vsjt7cr0vgkjkjf3ffnvi3a70.apps.googleusercontent.com';
381
383
  const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAuthSuccess, onAuthError, enabledProviders = ['email', 'google', 'phone'], redirectUrl, theme = 'light', className, customization, skipConfigFetch = false, }) => {
382
384
  const [mode, setMode] = useState('login');
383
385
  const [loading, setLoading] = useState(false);
@@ -670,19 +672,94 @@ const SmartlinksAuthUI = ({ apiEndpoint, clientId, clientName, accountData, onAu
670
672
  }
671
673
  };
672
674
  const handleGoogleLogin = async () => {
675
+ // Use custom client ID from config, or fall back to default Smartlinks client ID
676
+ const googleClientId = config?.googleClientId || DEFAULT_GOOGLE_CLIENT_ID;
677
+ // Determine OAuth flow: default to 'oneTap' for better UX, but allow 'popup' for iframe compatibility
678
+ const oauthFlow = config?.googleOAuthFlow || 'oneTap';
673
679
  setLoading(true);
674
680
  setError(undefined);
675
681
  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');
682
+ const google = window.google;
683
+ if (!google) {
684
+ throw new Error('Google Identity Services not loaded. Please check your internet connection.');
685
+ }
686
+ if (oauthFlow === 'popup') {
687
+ // Use OAuth2 popup flow (works in iframes but requires popup permission)
688
+ if (!google.accounts.oauth2) {
689
+ throw new Error('Google OAuth2 not available');
690
+ }
691
+ const client = google.accounts.oauth2.initTokenClient({
692
+ client_id: googleClientId,
693
+ scope: 'openid email profile',
694
+ callback: async (response) => {
695
+ try {
696
+ if (response.error) {
697
+ throw new Error(response.error_description || response.error);
698
+ }
699
+ const accessToken = response.access_token;
700
+ // Send access token to backend
701
+ const authResponse = await api.loginWithGoogle(accessToken);
702
+ auth.login(authResponse.token, authResponse.user, authResponse.accountData);
703
+ setAuthSuccess(true);
704
+ setSuccessMessage('Google login successful!');
705
+ onAuthSuccess(authResponse.token, authResponse.user, authResponse.accountData);
706
+ if (redirectUrl) {
707
+ setTimeout(() => {
708
+ window.location.href = redirectUrl;
709
+ }, 2000);
710
+ }
711
+ setLoading(false);
712
+ }
713
+ catch (err) {
714
+ const errorMessage = err instanceof Error ? err.message : 'Google login failed';
715
+ setError(errorMessage);
716
+ onAuthError?.(err instanceof Error ? err : new Error(errorMessage));
717
+ setLoading(false);
718
+ }
719
+ },
720
+ });
721
+ client.requestAccessToken();
722
+ }
723
+ else {
724
+ // Use One Tap / Sign-In button flow (smoother UX but doesn't work in iframes)
725
+ google.accounts.id.initialize({
726
+ client_id: googleClientId,
727
+ callback: async (response) => {
728
+ try {
729
+ const idToken = response.credential;
730
+ const authResponse = await api.loginWithGoogle(idToken);
731
+ auth.login(authResponse.token, authResponse.user, authResponse.accountData);
732
+ setAuthSuccess(true);
733
+ setSuccessMessage('Google login successful!');
734
+ onAuthSuccess(authResponse.token, authResponse.user, authResponse.accountData);
735
+ if (redirectUrl) {
736
+ setTimeout(() => {
737
+ window.location.href = redirectUrl;
738
+ }, 2000);
739
+ }
740
+ setLoading(false);
741
+ }
742
+ catch (err) {
743
+ const errorMessage = err instanceof Error ? err.message : 'Google login failed';
744
+ setError(errorMessage);
745
+ onAuthError?.(err instanceof Error ? err : new Error(errorMessage));
746
+ setLoading(false);
747
+ }
748
+ },
749
+ auto_select: false,
750
+ cancel_on_tap_outside: true,
751
+ });
752
+ google.accounts.id.prompt((notification) => {
753
+ if (notification.isNotDisplayed() || notification.isSkippedMoment()) {
754
+ setLoading(false);
755
+ }
756
+ });
757
+ }
679
758
  }
680
759
  catch (err) {
681
760
  const errorMessage = err instanceof Error ? err.message : 'Google login failed';
682
761
  setError(errorMessage);
683
762
  onAuthError?.(err instanceof Error ? err : new Error(errorMessage));
684
- }
685
- finally {
686
763
  setLoading(false);
687
764
  }
688
765
  };