@passkeyme/react-auth 1.0.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 (70) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/LICENSE +21 -0
  3. package/README.md +951 -0
  4. package/dist/components/DevToolsDashboard.d.ts +18 -0
  5. package/dist/components/DevToolsDashboard.d.ts.map +1 -0
  6. package/dist/components/LazyComponents.d.ts +22 -0
  7. package/dist/components/LazyComponents.d.ts.map +1 -0
  8. package/dist/components/NotificationProvider.d.ts +31 -0
  9. package/dist/components/NotificationProvider.d.ts.map +1 -0
  10. package/dist/components/PasskeymeAuthPanel.d.ts +121 -0
  11. package/dist/components/PasskeymeAuthPanel.d.ts.map +1 -0
  12. package/dist/components/PasskeymeButton.d.ts +64 -0
  13. package/dist/components/PasskeymeButton.d.ts.map +1 -0
  14. package/dist/components/PasskeymeCallbackHandler.d.ts +50 -0
  15. package/dist/components/PasskeymeCallbackHandler.d.ts.map +1 -0
  16. package/dist/components/PasskeymeErrorDisplay.d.ts +28 -0
  17. package/dist/components/PasskeymeErrorDisplay.d.ts.map +1 -0
  18. package/dist/components/PasskeymeLoadingIndicator.d.ts +34 -0
  19. package/dist/components/PasskeymeLoadingIndicator.d.ts.map +1 -0
  20. package/dist/components/PasskeymeOAuthButton.d.ts +8 -0
  21. package/dist/components/PasskeymeOAuthButton.d.ts.map +1 -0
  22. package/dist/components/PasskeymeProtectedRoute.d.ts +16 -0
  23. package/dist/components/PasskeymeProtectedRoute.d.ts.map +1 -0
  24. package/dist/components/PasskeymeProvider.d.ts +24 -0
  25. package/dist/components/PasskeymeProvider.d.ts.map +1 -0
  26. package/dist/components/PasskeymeUserProfile.d.ts +31 -0
  27. package/dist/components/PasskeymeUserProfile.d.ts.map +1 -0
  28. package/dist/components/PerformanceDashboard.d.ts +30 -0
  29. package/dist/components/PerformanceDashboard.d.ts.map +1 -0
  30. package/dist/components/VirtualScrollList.d.ts +105 -0
  31. package/dist/components/VirtualScrollList.d.ts.map +1 -0
  32. package/dist/hooks/useAuthUtils.d.ts +26 -0
  33. package/dist/hooks/useAuthUtils.d.ts.map +1 -0
  34. package/dist/hooks/useLoadingState.d.ts +31 -0
  35. package/dist/hooks/useLoadingState.d.ts.map +1 -0
  36. package/dist/hooks/usePerformanceMonitor.d.ts +41 -0
  37. package/dist/hooks/usePerformanceMonitor.d.ts.map +1 -0
  38. package/dist/hooks/useUsernameManager.d.ts +62 -0
  39. package/dist/hooks/useUsernameManager.d.ts.map +1 -0
  40. package/dist/index.d.ts +85 -0
  41. package/dist/index.d.ts.map +1 -0
  42. package/dist/index.esm.js +8734 -0
  43. package/dist/index.esm.js.map +1 -0
  44. package/dist/index.js +8827 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/types.d.ts +249 -0
  47. package/dist/types.d.ts.map +1 -0
  48. package/dist/utils/analytics.d.ts +112 -0
  49. package/dist/utils/analytics.d.ts.map +1 -0
  50. package/dist/utils/debug.d.ts +9 -0
  51. package/dist/utils/debug.d.ts.map +1 -0
  52. package/dist/utils/devUtils.d.ts +53 -0
  53. package/dist/utils/devUtils.d.ts.map +1 -0
  54. package/dist/utils/enhancedButtonStyles.d.ts +70 -0
  55. package/dist/utils/enhancedButtonStyles.d.ts.map +1 -0
  56. package/dist/utils/importOptimizer.d.ts +79 -0
  57. package/dist/utils/importOptimizer.d.ts.map +1 -0
  58. package/dist/utils/loadingStates.d.ts +35 -0
  59. package/dist/utils/loadingStates.d.ts.map +1 -0
  60. package/dist/utils/logger.d.ts +41 -0
  61. package/dist/utils/logger.d.ts.map +1 -0
  62. package/dist/utils/performanceProfiler.d.ts +80 -0
  63. package/dist/utils/performanceProfiler.d.ts.map +1 -0
  64. package/dist/utils/reactCompat.d.ts +6 -0
  65. package/dist/utils/reactCompat.d.ts.map +1 -0
  66. package/dist/utils/storageOptimization.d.ts +173 -0
  67. package/dist/utils/storageOptimization.d.ts.map +1 -0
  68. package/dist/utils/testingUtils.d.ts +163 -0
  69. package/dist/utils/testingUtils.d.ts.map +1 -0
  70. package/package.json +93 -0
package/README.md ADDED
@@ -0,0 +1,951 @@
1
+ # @passkeyme/react-auth
2
+
3
+ React integration for PasskeyMe Authentication SDK. Build secure authentication into your React apps with hooks and components.
4
+
5
+ ## ๐Ÿš€ Features
6
+
7
+ - **๐Ÿช React Hooks**: `usePasskeyme()`, `useAuth()`, `useAuthState()`
8
+ - **๐Ÿงฉ Pre-built Components**: Login buttons, user profiles, protected routes
9
+ - **๐Ÿ”„ Automatic State Management**: Context-based state with automatic updates
10
+ - **๐ŸŽจ Customizable UI**: Styled components with full customization options
11
+ - **๐Ÿ›ก๏ธ Type Safety**: Full TypeScript support
12
+ - **โšก Zero Config**: Works out of the box with sensible defaults
13
+
14
+ ## ๐Ÿ“ฆ Installation
15
+
16
+ ```bash
17
+ npm install @passkeyme/auth @passkeyme/react-auth
18
+ ```
19
+
20
+ **Peer Dependencies:**
21
+
22
+ - React >= 16.8.0
23
+ - React DOM >= 16.8.0
24
+
25
+ ## ๐Ÿ”ง Quick Start
26
+
27
+ ### 1. Setup Provider
28
+
29
+ Wrap your app with the `PasskeymeProvider`:
30
+
31
+ ```tsx
32
+ import { PasskeymeProvider } from "@passkeyme/react-auth";
33
+
34
+ function App() {
35
+ return (
36
+ <PasskeymeProvider
37
+ config={{
38
+ appId: "your-passkeyme-app-id",
39
+ redirectUri: "http://localhost:3000/auth/callback",
40
+ debug: process.env.NODE_ENV === "development", // Enable debug logging in development
41
+ }}
42
+ >
43
+ <YourApp />
44
+ </PasskeymeProvider>
45
+ );
46
+ }
47
+ ```
48
+
49
+ ### 2. Use Authentication Hooks
50
+
51
+ ```tsx
52
+ import { usePasskeyme, PasskeymeCallbackHandler } from "@passkeyme/react-auth";
53
+ import { BrowserRouter, Routes, Route } from "react-router-dom";
54
+
55
+ function App() {
56
+ return (
57
+ <BrowserRouter>
58
+ <Routes>
59
+ {/* Built-in callback handler - handles all authentication flows */}
60
+ <Route path="/auth/callback" element={<PasskeymeCallbackHandler />} />
61
+ <Route path="/" element={<Dashboard />} />
62
+ </Routes>
63
+ </BrowserRouter>
64
+ );
65
+ }
66
+
67
+ function Dashboard() {
68
+ const { user, loginWithOAuth, logout, loading } = usePasskeyme();
69
+
70
+ if (loading) {
71
+ return <div>Loading...</div>;
72
+ }
73
+
74
+ if (!user) {
75
+ return (
76
+ <button onClick={() => loginWithOAuth("google")}>
77
+ Login with Google
78
+ </button>
79
+ );
80
+ }
81
+
82
+ return (
83
+ <div>
84
+ <h1>Welcome, {user.name}!</h1>
85
+ <button onClick={logout}>Logout</button>
86
+ </div>
87
+ );
88
+ }
89
+ ```
90
+
91
+ ### 3. Use Pre-built Components
92
+
93
+ ```tsx
94
+ import {
95
+ PasskeymeOAuthButton,
96
+ PasskeymeButton,
97
+ PasskeymeUserProfile,
98
+ PasskeymeProtectedRoute,
99
+ } from "@passkeyme/react-auth";
100
+
101
+ function LoginPage() {
102
+ return (
103
+ <div>
104
+ <PasskeymeOAuthButton provider="google" />
105
+ <PasskeymeOAuthButton provider="github" />
106
+ <PasskeymeButton />
107
+ </div>
108
+ );
109
+ }
110
+
111
+ function Dashboard() {
112
+ return (
113
+ <PasskeymeProtectedRoute fallback={<LoginPage />}>
114
+ <div>
115
+ <PasskeymeUserProfile showLogout />
116
+ <h1>Protected Content</h1>
117
+ </div>
118
+ </PasskeymeProtectedRoute>
119
+ );
120
+ }
121
+ ```
122
+
123
+ ## ๐Ÿ“š API Reference
124
+
125
+ ### PasskeymeProvider
126
+
127
+ The main provider component that manages authentication state.
128
+
129
+ ```tsx
130
+ <PasskeymeProvider
131
+ config={{
132
+ appId: "your-app-id",
133
+ baseUrl: "https://auth.passkeyme.com",
134
+ redirectUri: "http://localhost:3000/callback",
135
+ debug: true, // Enable debug logging for development
136
+ }}
137
+ loadingComponent={<div>Loading...</div>}
138
+ errorComponent={(error) => <div>Error: {error}</div>}
139
+ onAuthChange={(user) => console.log("User changed:", user)}
140
+ onError={(error) => console.error("Auth error:", error)}
141
+ >
142
+ <App />
143
+ </PasskeymeProvider>
144
+ ```
145
+
146
+ > **Note**: Set `debug: false` or omit the debug flag in production to prevent debug messages from appearing in the browser console.
147
+
148
+ ### usePasskeyme Hook
149
+
150
+ Main hook providing full authentication functionality.
151
+
152
+ ```tsx
153
+ const {
154
+ user, // Current user or null
155
+ loading, // Loading state
156
+ error, // Error message or null
157
+ isAuthenticated, // Boolean auth status
158
+
159
+ // Login methods
160
+ login, // Generic login with options
161
+ loginWithOAuth, // OAuth login (redirects)
162
+ loginWithPasskey, // Passkey authentication
163
+ loginWithPassword, // Username/password login
164
+ handleCallback, // Handle OAuth callback
165
+
166
+ // Other methods
167
+ logout, // Logout user
168
+ getAccessToken, // Get current token
169
+ refreshToken, // Refresh token
170
+ auth, // Direct access to auth instance
171
+ } = usePasskeyme();
172
+ ```
173
+
174
+ ### useAuthState Hook
175
+
176
+ Get only the authentication state (no methods).
177
+
178
+ ```tsx
179
+ const { user, loading, error, isAuthenticated } = useAuthState();
180
+ ```
181
+
182
+ ### useAuth Hook
183
+
184
+ Get only the authentication methods (no state).
185
+
186
+ ```tsx
187
+ const {
188
+ login,
189
+ loginWithOAuth,
190
+ loginWithPasskey,
191
+ loginWithPassword,
192
+ logout,
193
+ getAccessToken,
194
+ refreshToken,
195
+ } = useAuth();
196
+ ```
197
+
198
+ ## ๐Ÿงฉ Components
199
+
200
+ ### PasskeymeCallbackHandler
201
+
202
+ **โญ NEW**: Built-in component that automatically handles all authentication callbacks.
203
+
204
+ The `PasskeymeCallbackHandler` eliminates the need to write custom callback logic. Just add it to your routing:
205
+
206
+ ```tsx
207
+ import { PasskeymeCallbackHandler } from '@passkeyme/react-auth';
208
+
209
+ // Basic usage - handles everything automatically
210
+ <Route path="/callback" element={<PasskeymeCallbackHandler />} />
211
+
212
+ // With custom redirects and components
213
+ <Route
214
+ path="/callback"
215
+ element={
216
+ <PasskeymeCallbackHandler
217
+ successRedirect="/dashboard"
218
+ errorRedirect="/login"
219
+ loadingComponent={CustomSpinner}
220
+ errorComponent={CustomError}
221
+ />
222
+ }
223
+ />
224
+ ```
225
+
226
+ **Features:**
227
+
228
+ - โœ… Handles both OAuth (`?code=...`) and hosted auth (`?token=...`) flows
229
+ - โœ… Built-in loading and error states with customizable components
230
+ - โœ… Automatic token validation and user state management
231
+ - โœ… **NEW**: Automatic passkey registration prompting (enabled by default)
232
+ - โœ… Configurable success/error redirects
233
+ - โœ… Custom event callbacks for advanced use cases
234
+ - โœ… URL cleanup after processing
235
+
236
+ See [CALLBACK_HANDLER.md](./CALLBACK_HANDLER.md) for complete documentation.
237
+
238
+ ### PasskeymeOAuthButton
239
+
240
+ OAuth login button with built-in styling.
241
+
242
+ ```tsx
243
+ <PasskeymeOAuthButton
244
+ provider="google" // 'google' | 'github' | 'facebook'
245
+ variant="default" // 'default' | 'outlined' | 'text'
246
+ size="medium" // 'small' | 'medium' | 'large'
247
+ redirectUri="/custom/callback"
248
+ disabled={false}
249
+ loading={false}
250
+ onClick={() => console.log("Clicked")}
251
+ className="custom-class"
252
+ >
253
+ Custom Button Text
254
+ </PasskeymeOAuthButton>
255
+ ```
256
+
257
+ ### PasskeymeButton
258
+
259
+ Passkey authentication button.
260
+
261
+ ```tsx
262
+ <PasskeymeButton
263
+ username="user@example.com" // Optional username hint
264
+ variant="default"
265
+ size="medium"
266
+ onSuccess={(user) => console.log("Success:", user)}
267
+ onError={(error) => console.log("Error:", error)}
268
+ >
269
+ Sign in with Passkey
270
+ </PasskeymeButton>
271
+ ```
272
+
273
+ ### PasskeymeUserProfile
274
+
275
+ User profile display with avatar and logout.
276
+
277
+ ```tsx
278
+ <PasskeymeUserProfile
279
+ showAvatar={true}
280
+ showName={true}
281
+ showEmail={true}
282
+ showLogout={true}
283
+ avatarSize={40}
284
+ logoutText="Sign Out"
285
+ onLogout={() => console.log("Logged out")}
286
+ className="profile-container"
287
+ />
288
+ ```
289
+
290
+ ### PasskeymeProtectedRoute
291
+
292
+ Protect content for authenticated users.
293
+
294
+ ```tsx
295
+ <PasskeymeProtectedRoute
296
+ fallback={<LoginPage />}
297
+ redirectTo="/login"
298
+ requiredRoles={["admin", "user"]}
299
+ hasAccess={(user) => user.emailVerified}
300
+ >
301
+ <SecretContent />
302
+ </PasskeymeProtectedRoute>
303
+ ```
304
+
305
+ ### withAuth HOC
306
+
307
+ Higher-order component for protecting components.
308
+
309
+ ```tsx
310
+ const ProtectedComponent = withAuth(MyComponent, {
311
+ fallback: <div>Please login</div>,
312
+ requiredRoles: ["admin"],
313
+ });
314
+ ```
315
+
316
+ ## ๐Ÿš€ Programmatic Authentication
317
+
318
+ ### triggerPasskeymeAuth
319
+
320
+ **โญ NEW**: Enhanced programmatic authentication with mode support for hosted vs inline OAuth flows.
321
+
322
+ ```tsx
323
+ // Simple hosted flow (default - redirects to hosted auth pages)
324
+ const { triggerPasskeymeAuth } = usePasskeyme();
325
+ triggerPasskeymeAuth();
326
+
327
+ // Custom success/error handling
328
+ triggerPasskeymeAuth({
329
+ username: "user@example.com",
330
+ onSuccess: (user, method) => console.log(`Authenticated via ${method}`, user),
331
+ onError: (error) => console.error("Auth failed:", error),
332
+ });
333
+
334
+ // Inline mode - developer controls OAuth UI
335
+ triggerPasskeymeAuth({
336
+ mode: "inline",
337
+ onOAuthRequired: (providers) => {
338
+ // Show your custom OAuth UI
339
+ setShowOAuthModal(true);
340
+ setAvailableProviders(providers);
341
+ },
342
+ onSuccess: (user, method) => {
343
+ setShowOAuthModal(false);
344
+ console.log(`Success via ${method}`, user);
345
+ },
346
+ });
347
+
348
+ // Passkey-only (no fallback)
349
+ triggerPasskeymeAuth({
350
+ forcePasskeyOnly: true,
351
+ onError: (error) => alert("Passkey authentication failed"),
352
+ });
353
+ ```
354
+
355
+ **Mode Options:**
356
+
357
+ - **`hosted`** (default): Try passkey โ†’ redirect to hosted auth pages
358
+ - **`inline`**: Try passkey โ†’ callback with OAuth providers for custom UI
359
+
360
+ This gives you maximum flexibility - use hosted mode for simple setup, or inline mode for full UI control.
361
+
362
+ ### Complete Inline Mode Example
363
+
364
+ ```tsx
365
+ function CustomAuthFlow() {
366
+ const { triggerPasskeymeAuth } = usePasskeyme();
367
+ const [showOAuth, setShowOAuth] = useState(false);
368
+ const [providers, setProviders] = useState([]);
369
+
370
+ const handleLogin = () => {
371
+ triggerPasskeymeAuth({
372
+ mode: "inline",
373
+ onOAuthRequired: (availableProviders) => {
374
+ setProviders(availableProviders);
375
+ setShowOAuth(true);
376
+ },
377
+ onSuccess: (user, method) => {
378
+ setShowOAuth(false);
379
+ console.log(`Authenticated via ${method}`);
380
+ },
381
+ });
382
+ };
383
+
384
+ return (
385
+ <div>
386
+ <button onClick={handleLogin}>Login</button>
387
+
388
+ {showOAuth && (
389
+ <div className="oauth-modal">
390
+ <h3>Choose OAuth Provider</h3>
391
+ {providers.map((provider) => (
392
+ <PasskeymeOAuthButton key={provider} provider={provider}>
393
+ Login with {provider}
394
+ </PasskeymeOAuthButton>
395
+ ))}
396
+ <button onClick={() => setShowOAuth(false)}>Cancel</button>
397
+ </div>
398
+ )}
399
+ </div>
400
+ );
401
+ }
402
+ ```
403
+
404
+ ## ๐ŸŽจ Customization
405
+
406
+ ### Custom Styling
407
+
408
+ All components accept `className` and can be styled with CSS:
409
+
410
+ ```css
411
+ .login-button {
412
+ background: linear-gradient(45deg, #fe6b8b 30%, #ff8e53 90%);
413
+ border-radius: 20px;
414
+ }
415
+
416
+ .user-profile {
417
+ border: 2px solid #f0f0f0;
418
+ border-radius: 8px;
419
+ padding: 16px;
420
+ }
421
+ ```
422
+
423
+ ### Custom Components
424
+
425
+ Create your own components using the hooks:
426
+
427
+ ```tsx
428
+ function CustomLoginForm() {
429
+ const { loginWithPassword, loading, error } = usePasskeyme();
430
+ const [email, setEmail] = useState("");
431
+ const [password, setPassword] = useState("");
432
+
433
+ const handleSubmit = async (e) => {
434
+ e.preventDefault();
435
+ try {
436
+ await loginWithPassword(email, password);
437
+ } catch (err) {
438
+ // Error handled by hook
439
+ }
440
+ };
441
+
442
+ return (
443
+ <form onSubmit={handleSubmit}>
444
+ <input
445
+ value={email}
446
+ onChange={(e) => setEmail(e.target.value)}
447
+ placeholder="Email"
448
+ />
449
+ <input
450
+ type="password"
451
+ value={password}
452
+ onChange={(e) => setPassword(e.target.value)}
453
+ placeholder="Password"
454
+ />
455
+ <button type="submit" disabled={loading}>
456
+ {loading ? "Signing in..." : "Sign In"}
457
+ </button>
458
+ {error && <div>Error: {error}</div>}
459
+ </form>
460
+ );
461
+ }
462
+ ```
463
+
464
+ ## ๐Ÿ”— Integration Examples
465
+
466
+ ### Next.js App Router
467
+
468
+ ```tsx
469
+ // app/layout.tsx
470
+ import { PasskeymeProvider } from "@passkeyme/react-auth";
471
+
472
+ export default function RootLayout({ children }) {
473
+ return (
474
+ <html>
475
+ <body>
476
+ <PasskeymeProvider
477
+ config={{ appId: process.env.NEXT_PUBLIC_PASSKEYME_APP_ID }}
478
+ >
479
+ {children}
480
+ </PasskeymeProvider>
481
+ </body>
482
+ </html>
483
+ );
484
+ }
485
+
486
+ // app/dashboard/page.tsx
487
+ import { ProtectedRoute, UserProfile } from "@passkeyme/react-auth";
488
+
489
+ export default function Dashboard() {
490
+ return (
491
+ <ProtectedRoute>
492
+ <div>
493
+ <UserProfile />
494
+ <h1>Dashboard</h1>
495
+ </div>
496
+ </ProtectedRoute>
497
+ );
498
+ }
499
+ ```
500
+
501
+ ### React Router
502
+
503
+ ```tsx
504
+ import { BrowserRouter, Route, Routes, Navigate } from "react-router-dom";
505
+ import { usePasskeyme } from "@passkeyme/react-auth";
506
+
507
+ function AppRouter() {
508
+ const { isAuthenticated, loading } = usePasskeyme();
509
+
510
+ if (loading) return <div>Loading...</div>;
511
+
512
+ return (
513
+ <BrowserRouter>
514
+ <Routes>
515
+ <Route path="/login" element={<LoginPage />} />
516
+ <Route
517
+ path="/dashboard"
518
+ element={isAuthenticated ? <Dashboard /> : <Navigate to="/login" />}
519
+ />
520
+ </Routes>
521
+ </BrowserRouter>
522
+ );
523
+ }
524
+ ```
525
+
526
+ ## ๐Ÿ”— TypeScript Support
527
+
528
+ This package provides comprehensive TypeScript definitions for type-safe authentication.
529
+
530
+ ### Type Imports
531
+
532
+ ```typescript
533
+ import type {
534
+ User,
535
+ AuthConfig,
536
+ AuthState,
537
+ PasskeyCredential,
538
+ OAuthProvider,
539
+ UsePasskeymeReturn,
540
+ } from "@passkeyme/react-auth";
541
+
542
+ // User object structure
543
+ interface User {
544
+ id: string;
545
+ email: string;
546
+ name?: string;
547
+ avatar?: string;
548
+ metadata?: Record<string, any>;
549
+ }
550
+
551
+ // Authentication configuration
552
+ interface AuthConfig {
553
+ appId: string;
554
+ baseUrl?: string;
555
+ redirectUri: string;
556
+ debug?: boolean;
557
+ }
558
+
559
+ // Hook return type
560
+ interface UsePasskeymeReturn {
561
+ // State
562
+ user: User | null;
563
+ isAuthenticated: boolean;
564
+ loading: boolean;
565
+ error: Error | null;
566
+
567
+ // Actions
568
+ loginWithOAuth: (provider: OAuthProvider) => Promise<void>;
569
+ loginWithPasskey: () => Promise<void>;
570
+ loginWithCredentials: (email: string, password: string) => Promise<void>;
571
+ register: (email: string, password: string, name?: string) => Promise<void>;
572
+ logout: () => Promise<void>;
573
+
574
+ // Utility
575
+ refreshToken: () => Promise<void>;
576
+ getAuthHeader: () => string | null;
577
+ }
578
+ ```
579
+
580
+ ### Type-Safe Configuration
581
+
582
+ ```typescript
583
+ import { PasskeymeProvider } from "@passkeyme/react-auth";
584
+ import type { AuthConfig } from "@passkeyme/react-auth";
585
+
586
+ const authConfig: AuthConfig = {
587
+ appId: process.env.REACT_APP_PASSKEYME_APP_ID!,
588
+ redirectUri: `${window.location.origin}/auth/callback`,
589
+ debug: process.env.NODE_ENV === "development",
590
+ };
591
+
592
+ function App() {
593
+ return (
594
+ <PasskeymeProvider config={authConfig}>
595
+ <AppContent />
596
+ </PasskeymeProvider>
597
+ );
598
+ }
599
+ ```
600
+
601
+ ### Custom Hook with TypeScript
602
+
603
+ ```typescript
604
+ import { usePasskeyme } from "@passkeyme/react-auth";
605
+ import type { User } from "@passkeyme/react-auth";
606
+
607
+ // Custom hook for user profile management
608
+ function useUserProfile() {
609
+ const { user, updateProfile } = usePasskeyme();
610
+
611
+ const updateUserProfile = async (updates: Partial<User>) => {
612
+ if (!user) throw new Error("User not authenticated");
613
+
614
+ return updateProfile({
615
+ ...user,
616
+ ...updates,
617
+ });
618
+ };
619
+
620
+ return {
621
+ user,
622
+ updateUserProfile,
623
+ hasProfile: Boolean(user?.name),
624
+ isEmailVerified: user?.emailVerified ?? false,
625
+ };
626
+ }
627
+ ```
628
+
629
+ ## ๐Ÿ”„ Integration Guides
630
+
631
+ ### Next.js Integration
632
+
633
+ ```typescript
634
+ // pages/_app.tsx
635
+ import { AppProps } from "next/app";
636
+ import { PasskeymeProvider } from "@passkeyme/react-auth";
637
+
638
+ export default function App({ Component, pageProps }: AppProps) {
639
+ return (
640
+ <PasskeymeProvider
641
+ config={{
642
+ appId: process.env.NEXT_PUBLIC_PASSKEYME_APP_ID!,
643
+ redirectUri: `${process.env.NEXT_PUBLIC_BASE_URL}/auth/callback`,
644
+ }}
645
+ >
646
+ <Component {...pageProps} />
647
+ </PasskeymeProvider>
648
+ );
649
+ }
650
+
651
+ // pages/auth/callback.tsx
652
+ import { PasskeymeCallbackHandler } from "@passkeyme/react-auth";
653
+
654
+ export default function AuthCallback() {
655
+ return <PasskeymeCallbackHandler />;
656
+ }
657
+
658
+ // Custom hook for Next.js router integration
659
+ import { useRouter } from "next/router";
660
+ import { usePasskeyme } from "@passkeyme/react-auth";
661
+ import { useEffect } from "react";
662
+
663
+ export function useAuthRedirect() {
664
+ const { isAuthenticated, loading } = usePasskeyme();
665
+ const router = useRouter();
666
+
667
+ useEffect(() => {
668
+ if (!loading && !isAuthenticated) {
669
+ router.push("/login");
670
+ }
671
+ }, [isAuthenticated, loading, router]);
672
+
673
+ return { isAuthenticated, loading };
674
+ }
675
+ ```
676
+
677
+ ### Vite/React Integration
678
+
679
+ ```typescript
680
+ // main.tsx
681
+ import React from "react";
682
+ import ReactDOM from "react-dom/client";
683
+ import { PasskeymeProvider } from "@passkeyme/react-auth";
684
+ import App from "./App";
685
+
686
+ ReactDOM.createRoot(document.getElementById("root")!).render(
687
+ <React.StrictMode>
688
+ <PasskeymeProvider
689
+ config={{
690
+ appId: import.meta.env.VITE_PASSKEYME_APP_ID,
691
+ redirectUri: `${window.location.origin}/auth/callback`,
692
+ debug: import.meta.env.DEV,
693
+ }}
694
+ >
695
+ <App />
696
+ </PasskeymeProvider>
697
+ </React.StrictMode>
698
+ );
699
+
700
+ // vite-env.d.ts - Environment variables
701
+ interface ImportMetaEnv {
702
+ readonly VITE_PASSKEYME_APP_ID: string;
703
+ // Add other env variables as needed
704
+ }
705
+
706
+ interface ImportMeta {
707
+ readonly env: ImportMetaEnv;
708
+ }
709
+ ```
710
+
711
+ ### React Router Integration
712
+
713
+ ```typescript
714
+ import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
715
+ import { PasskeymeCallbackHandler, usePasskeyme } from "@passkeyme/react-auth";
716
+
717
+ // Protected Route Component
718
+ interface ProtectedRouteProps {
719
+ children: React.ReactNode;
720
+ fallback?: React.ReactNode;
721
+ }
722
+
723
+ function ProtectedRoute({ children, fallback }: ProtectedRouteProps) {
724
+ const { isAuthenticated, loading } = usePasskeyme();
725
+
726
+ if (loading) return <div>Loading...</div>;
727
+
728
+ if (!isAuthenticated) {
729
+ return fallback ? <>{fallback}</> : <Navigate to="/login" replace />;
730
+ }
731
+
732
+ return <>{children}</>;
733
+ }
734
+
735
+ function AppRouter() {
736
+ return (
737
+ <BrowserRouter>
738
+ <Routes>
739
+ <Route path="/login" element={<LoginPage />} />
740
+ <Route path="/auth/callback" element={<PasskeymeCallbackHandler />} />
741
+ <Route
742
+ path="/dashboard"
743
+ element={
744
+ <ProtectedRoute>
745
+ <Dashboard />
746
+ </ProtectedRoute>
747
+ }
748
+ />
749
+ <Route path="/" element={<Navigate to="/dashboard" replace />} />
750
+ </Routes>
751
+ </BrowserRouter>
752
+ );
753
+ }
754
+ ```
755
+
756
+ ### Error Handling Best Practices
757
+
758
+ ```typescript
759
+ import { usePasskeyme } from "@passkeyme/react-auth";
760
+ import { useEffect, useState } from "react";
761
+
762
+ function LoginWithErrorHandling() {
763
+ const { loginWithOAuth, error, loading } = usePasskeyme();
764
+ const [loginError, setLoginError] = useState<string | null>(null);
765
+
766
+ const handleLogin = async (provider: "google" | "github") => {
767
+ try {
768
+ setLoginError(null);
769
+ await loginWithOAuth(provider);
770
+ } catch (err) {
771
+ setLoginError(err instanceof Error ? err.message : "Login failed");
772
+ }
773
+ };
774
+
775
+ // Handle authentication errors
776
+ useEffect(() => {
777
+ if (error) {
778
+ console.error("Authentication error:", error);
779
+ setLoginError(error.message);
780
+ }
781
+ }, [error]);
782
+
783
+ return (
784
+ <div>
785
+ {loginError && (
786
+ <div className="error-banner" role="alert">
787
+ {loginError}
788
+ <button onClick={() => setLoginError(null)}>ร—</button>
789
+ </div>
790
+ )}
791
+
792
+ <button onClick={() => handleLogin("google")} disabled={loading}>
793
+ {loading ? "Signing in..." : "Sign in with Google"}
794
+ </button>
795
+ </div>
796
+ );
797
+ }
798
+ ```
799
+
800
+ ## ๐Ÿงช Testing Integration
801
+
802
+ ### Jest Testing Setup
803
+
804
+ ```typescript
805
+ // test-utils.tsx
806
+ import React from "react";
807
+ import { render, RenderOptions } from "@testing-library/react";
808
+ import { PasskeymeProvider } from "@passkeyme/react-auth";
809
+ import type { AuthConfig } from "@passkeyme/react-auth";
810
+
811
+ const mockAuthConfig: AuthConfig = {
812
+ appId: "test-app-id",
813
+ redirectUri: "http://localhost:3000/callback",
814
+ debug: false,
815
+ };
816
+
817
+ interface CustomRenderOptions extends Omit<RenderOptions, "wrapper"> {
818
+ authConfig?: Partial<AuthConfig>;
819
+ }
820
+
821
+ export function renderWithAuth(
822
+ ui: React.ReactElement,
823
+ { authConfig = {}, ...options }: CustomRenderOptions = {}
824
+ ) {
825
+ const config = { ...mockAuthConfig, ...authConfig };
826
+
827
+ function Wrapper({ children }: { children: React.ReactNode }) {
828
+ return <PasskeymeProvider config={config}>{children}</PasskeymeProvider>;
829
+ }
830
+
831
+ return render(ui, { wrapper: Wrapper, ...options });
832
+ }
833
+
834
+ // Component test example
835
+ import { screen, fireEvent, waitFor } from "@testing-library/react";
836
+ import { usePasskeyme } from "@passkeyme/react-auth";
837
+
838
+ function TestComponent() {
839
+ const { user, loginWithOAuth, loading } = usePasskeyme();
840
+
841
+ return (
842
+ <div>
843
+ {user ? (
844
+ <div data-testid="user-info">{user.email}</div>
845
+ ) : (
846
+ <button onClick={() => loginWithOAuth("google")}>
847
+ {loading ? "Loading..." : "Login"}
848
+ </button>
849
+ )}
850
+ </div>
851
+ );
852
+ }
853
+
854
+ test("handles login flow", async () => {
855
+ renderWithAuth(<TestComponent />);
856
+
857
+ const loginButton = screen.getByText("Login");
858
+ fireEvent.click(loginButton);
859
+
860
+ expect(screen.getByText("Loading...")).toBeInTheDocument();
861
+
862
+ await waitFor(() => {
863
+ expect(screen.getByTestId("user-info")).toBeInTheDocument();
864
+ });
865
+ });
866
+ ```
867
+
868
+ ## ๐Ÿ”— Package Integration
869
+
870
+ ### Using with @passkeyme/react-core
871
+
872
+ This package builds on top of `@passkeyme/react-core` for shared patterns:
873
+
874
+ ```typescript
875
+ // Access core patterns directly
876
+ import {
877
+ usePasskeymeContext,
878
+ PasskeymeProvider as CoreProvider,
879
+ } from "@passkeyme/react-core";
880
+
881
+ // Or use the enhanced React-specific provider
882
+ import { PasskeymeProvider } from "@passkeyme/react-auth";
883
+
884
+ // Custom hook using core patterns
885
+ function useCustomAuth() {
886
+ const { state, dispatch } = usePasskeymeContext();
887
+
888
+ const customLogin = async () => {
889
+ dispatch({ type: "SET_LOADING", payload: true });
890
+ try {
891
+ // Custom authentication logic
892
+ const user = await customAuthFlow();
893
+ dispatch({ type: "SET_USER", payload: user });
894
+ } catch (error) {
895
+ dispatch({ type: "SET_ERROR", payload: error });
896
+ } finally {
897
+ dispatch({ type: "SET_LOADING", payload: false });
898
+ }
899
+ };
900
+
901
+ return { ...state, customLogin };
902
+ }
903
+ ```
904
+
905
+ ### Using with @passkeyme/auth Core
906
+
907
+ ```typescript
908
+ import { PasskeymeAuth } from "@passkeyme/auth";
909
+ import { usePasskeyme } from "@passkeyme/react-auth";
910
+
911
+ // Access the core auth instance
912
+ function AdvancedAuthComponent() {
913
+ const { auth } = usePasskeyme();
914
+
915
+ const handleAdvancedFlow = async () => {
916
+ // Direct access to core SDK methods
917
+ const tokens = await auth.getTokens();
918
+ const userInfo = await auth.getUserInfo();
919
+
920
+ // Custom API calls with auth headers
921
+ const response = await fetch("/api/protected", {
922
+ headers: {
923
+ Authorization: `Bearer ${tokens.accessToken}`,
924
+ },
925
+ });
926
+ };
927
+
928
+ return <button onClick={handleAdvancedFlow}>Advanced Flow</button>;
929
+ }
930
+ ```
931
+
932
+ ## ๐Ÿ†š vs Other Solutions
933
+
934
+ | Feature | **@passkeyme/react-auth** | **Firebase Auth** | **Auth0 React** |
935
+ | --------------------- | ------------------------- | ----------------- | ------------------ |
936
+ | **Setup Lines** | โœ… 5 lines | โŒ 20+ lines | โŒ 15+ lines |
937
+ | **Built-in Passkeys** | โœ… Native support | โŒ Manual setup | โŒ Manual setup |
938
+ | **Type Safety** | โœ… Full TypeScript | โš ๏ธ Partial | โœ… Full TypeScript |
939
+ | **Bundle Size** | โœ… Small | โŒ Large | โŒ Large |
940
+ | **Self-Hosting** | โœ… Yes | โŒ No | โŒ No |
941
+ | **Vendor Lock-in** | โœ… None | โŒ High | โŒ High |
942
+
943
+ ## ๐Ÿ“„ License
944
+
945
+ MIT - see LICENSE file for details.
946
+
947
+ ## ๐Ÿค Support
948
+
949
+ - **Documentation**: https://passkeyme.com/docs/react
950
+ - **Issues**: https://github.com/passkeyme/passkeyme/issues
951
+ - **Discord**: https://discord.gg/passkeyme