@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 +103 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +82 -5
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +82 -5
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -23,10 +23,21 @@ npm install @smartlinks/auth-ui
|
|
|
23
23
|
|
|
24
24
|
## Quick Start
|
|
25
25
|
|
|
26
|
-
###
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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;
|
|
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
|
-
|
|
677
|
-
|
|
678
|
-
|
|
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
|
};
|