@insforge/react 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.
- package/README.md +849 -0
- package/dist/index.d.mts +1386 -0
- package/dist/index.d.ts +1386 -0
- package/dist/index.js +1887 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1846 -0
- package/dist/index.mjs.map +1 -0
- package/dist/lib.d.mts +50 -0
- package/dist/lib.d.ts +50 -0
- package/dist/lib.js +90 -0
- package/dist/lib.js.map +1 -0
- package/dist/lib.mjs +82 -0
- package/dist/lib.mjs.map +1 -0
- package/package.json +78 -0
- package/src/styles.css +8 -0
package/README.md
ADDED
|
@@ -0,0 +1,849 @@
|
|
|
1
|
+
# @insforge/react
|
|
2
|
+
|
|
3
|
+
**Complete authentication solution for React applications.** Framework-agnostic components with full business logic included.
|
|
4
|
+
|
|
5
|
+
## Why @insforge/react?
|
|
6
|
+
|
|
7
|
+
✅ **Complete Package** - Not just UI, includes SDK integration, providers, and hooks
|
|
8
|
+
✅ **Framework Agnostic** - Works with Next.js, Vite, Remix, or any React framework
|
|
9
|
+
✅ **5-Minute Setup** - Provider + Components = done
|
|
10
|
+
✅ **Full TypeScript** - Complete type safety out of the box
|
|
11
|
+
✅ **Customizable** - Every component supports appearance props and text customization
|
|
12
|
+
|
|
13
|
+
**Need just UI?** All components are exported separately for maximum flexibility.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @insforge/react
|
|
19
|
+
# or
|
|
20
|
+
yarn add @insforge/react
|
|
21
|
+
# or
|
|
22
|
+
pnpm add @insforge/react
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Dependencies automatically include `@insforge/sdk` for authentication logic.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Quick Start
|
|
30
|
+
|
|
31
|
+
### 1. Setup Provider
|
|
32
|
+
|
|
33
|
+
Wrap your app with `InsforgeProvider` in the root:
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
// React / Vite
|
|
37
|
+
import { InsforgeProvider } from '@insforge/react';
|
|
38
|
+
import '@insforge/react/styles.css';
|
|
39
|
+
|
|
40
|
+
function App() {
|
|
41
|
+
return (
|
|
42
|
+
<InsforgeProvider baseUrl={import.meta.env.VITE_INSFORGE_BASE_URL}>
|
|
43
|
+
{/* Your app */}
|
|
44
|
+
</InsforgeProvider>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
// Next.js App Router
|
|
51
|
+
'use client';
|
|
52
|
+
import { InsforgeProvider } from '@insforge/react';
|
|
53
|
+
import '@insforge/react/styles.css';
|
|
54
|
+
|
|
55
|
+
export default function RootLayout({ children }) {
|
|
56
|
+
return (
|
|
57
|
+
<html>
|
|
58
|
+
<body>
|
|
59
|
+
<InsforgeProvider baseUrl={process.env.NEXT_PUBLIC_INSFORGE_BASE_URL}>
|
|
60
|
+
{children}
|
|
61
|
+
</InsforgeProvider>
|
|
62
|
+
</body>
|
|
63
|
+
</html>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Props:**
|
|
69
|
+
- `baseUrl` (required): Your Insforge backend URL
|
|
70
|
+
- `onAuthChange` (optional): Callback when auth state changes
|
|
71
|
+
- `syncTokenToCookie` (optional): Custom function to sync token to cookies (for Next.js SSR)
|
|
72
|
+
- `clearCookie` (optional): Custom function to clear cookie on sign out
|
|
73
|
+
|
|
74
|
+
### 2. Use Pre-built Components
|
|
75
|
+
|
|
76
|
+
The easiest way to add authentication:
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
'use client'; // for Next.js
|
|
80
|
+
import { SignIn, SignUp } from '@insforge/react';
|
|
81
|
+
|
|
82
|
+
// Sign In Page
|
|
83
|
+
export default function SignInPage() {
|
|
84
|
+
return (
|
|
85
|
+
<div className="min-h-screen flex items-center justify-center bg-gray-50">
|
|
86
|
+
<SignIn
|
|
87
|
+
afterSignInUrl="/dashboard"
|
|
88
|
+
signUpUrl="/sign-up"
|
|
89
|
+
/>
|
|
90
|
+
</div>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Sign Up Page
|
|
95
|
+
export default function SignUpPage() {
|
|
96
|
+
return (
|
|
97
|
+
<div className="min-h-screen flex items-center justify-center bg-gray-50">
|
|
98
|
+
<SignUp
|
|
99
|
+
afterSignUpUrl="/dashboard"
|
|
100
|
+
signInUrl="/sign-in"
|
|
101
|
+
/>
|
|
102
|
+
</div>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### 3. Create Callback Page (for OAuth)
|
|
108
|
+
|
|
109
|
+
Handle OAuth redirects:
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
'use client'; // for Next.js
|
|
113
|
+
import { InsforgeCallback } from '@insforge/react';
|
|
114
|
+
|
|
115
|
+
export default function CallbackPage() {
|
|
116
|
+
return <InsforgeCallback />;
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### 4. Use Hooks & Components
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
'use client'; // for Next.js
|
|
124
|
+
import { SignedIn, SignedOut, UserButton, useAuth, useUser } from '@insforge/react';
|
|
125
|
+
|
|
126
|
+
export default function Home() {
|
|
127
|
+
const { isSignedIn } = useAuth();
|
|
128
|
+
const { user } = useUser();
|
|
129
|
+
|
|
130
|
+
return (
|
|
131
|
+
<div>
|
|
132
|
+
<SignedOut>
|
|
133
|
+
<a href="/sign-in">Sign In</a>
|
|
134
|
+
</SignedOut>
|
|
135
|
+
|
|
136
|
+
<SignedIn>
|
|
137
|
+
<UserButton afterSignOutUrl="/" />
|
|
138
|
+
<h1>Welcome, {user?.email}!</h1>
|
|
139
|
+
</SignedIn>
|
|
140
|
+
</div>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**That's it!** 🎉 Your React app now has production-ready authentication.
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## How It Works
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
1. User visits Sign In page → Renders <SignIn> component
|
|
153
|
+
↓
|
|
154
|
+
2. User enters credentials → Component calls SDK methods
|
|
155
|
+
↓
|
|
156
|
+
3. SDK communicates with backend → Returns auth token
|
|
157
|
+
↓
|
|
158
|
+
4. Provider updates auth state → Components re-render
|
|
159
|
+
↓
|
|
160
|
+
5. User sees authenticated UI → Redirect to dashboard
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**Architecture:**
|
|
164
|
+
- **InsforgeProvider**: Manages authentication state globally
|
|
165
|
+
- **SDK Integration**: All auth operations go through `@insforge/sdk`
|
|
166
|
+
- **React Context**: Provides auth state to all child components
|
|
167
|
+
- **Hooks**: Easy access to auth methods and user data
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Complete Components (with Business Logic)
|
|
172
|
+
|
|
173
|
+
These components include full authentication logic:
|
|
174
|
+
|
|
175
|
+
### `<SignIn />`
|
|
176
|
+
|
|
177
|
+
Complete sign-in component with email/password and OAuth:
|
|
178
|
+
|
|
179
|
+
```tsx
|
|
180
|
+
import { SignIn } from '@insforge/react';
|
|
181
|
+
import { useNavigate } from 'react-router-dom';
|
|
182
|
+
|
|
183
|
+
function SignInPage() {
|
|
184
|
+
const navigate = useNavigate();
|
|
185
|
+
|
|
186
|
+
return (
|
|
187
|
+
<SignIn
|
|
188
|
+
afterSignInUrl="/dashboard"
|
|
189
|
+
signUpUrl="/sign-up"
|
|
190
|
+
forgotPasswordUrl="/forgot-password"
|
|
191
|
+
onSuccess={(user, accessToken) => {
|
|
192
|
+
console.log('Signed in:', user);
|
|
193
|
+
}}
|
|
194
|
+
onError={(error) => {
|
|
195
|
+
console.error('Error:', error);
|
|
196
|
+
}}
|
|
197
|
+
onRedirect={(url) => navigate(url)}
|
|
198
|
+
// Customization
|
|
199
|
+
title="Welcome Back"
|
|
200
|
+
subtitle="Sign in to continue"
|
|
201
|
+
appearance={{
|
|
202
|
+
containerClassName: "shadow-xl",
|
|
203
|
+
buttonClassName: "bg-blue-600"
|
|
204
|
+
}}
|
|
205
|
+
/>
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**Key Features:**
|
|
211
|
+
- Email/password authentication
|
|
212
|
+
- OAuth provider buttons (auto-detected from backend)
|
|
213
|
+
- Password visibility toggle
|
|
214
|
+
- Error handling
|
|
215
|
+
- Loading states
|
|
216
|
+
- Customizable text and styling
|
|
217
|
+
|
|
218
|
+
### `<SignUp />`
|
|
219
|
+
|
|
220
|
+
Complete sign-up component with password strength validation:
|
|
221
|
+
|
|
222
|
+
```tsx
|
|
223
|
+
import { SignUp } from '@insforge/react';
|
|
224
|
+
|
|
225
|
+
function SignUpPage() {
|
|
226
|
+
return (
|
|
227
|
+
<SignUp
|
|
228
|
+
afterSignUpUrl="/onboarding"
|
|
229
|
+
signInUrl="/sign-in"
|
|
230
|
+
onSuccess={(user, accessToken) => {
|
|
231
|
+
// Track sign-up event
|
|
232
|
+
analytics.track('user_signed_up', { userId: user.id });
|
|
233
|
+
}}
|
|
234
|
+
/>
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
**Key Features:**
|
|
240
|
+
- Email/password registration
|
|
241
|
+
- Real-time password strength indicator
|
|
242
|
+
- OAuth provider buttons
|
|
243
|
+
- Form validation
|
|
244
|
+
- Customizable requirements
|
|
245
|
+
|
|
246
|
+
### `<UserButton />`
|
|
247
|
+
|
|
248
|
+
User profile dropdown with sign-out:
|
|
249
|
+
|
|
250
|
+
```tsx
|
|
251
|
+
import { UserButton } from '@insforge/react';
|
|
252
|
+
|
|
253
|
+
function Header() {
|
|
254
|
+
return (
|
|
255
|
+
<header>
|
|
256
|
+
<nav>
|
|
257
|
+
{/* Your navigation */}
|
|
258
|
+
</nav>
|
|
259
|
+
<UserButton
|
|
260
|
+
afterSignOutUrl="/"
|
|
261
|
+
mode="detailed" // or "simple"
|
|
262
|
+
appearance={{
|
|
263
|
+
buttonClassName: "hover:bg-gray-100",
|
|
264
|
+
nameClassName: "text-gray-900",
|
|
265
|
+
emailClassName: "text-gray-600"
|
|
266
|
+
}}
|
|
267
|
+
/>
|
|
268
|
+
</header>
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
**Modes:**
|
|
274
|
+
- `detailed`: Shows avatar + name + email
|
|
275
|
+
- `simple`: Shows avatar only
|
|
276
|
+
|
|
277
|
+
### `<Protect />`
|
|
278
|
+
|
|
279
|
+
Protected content with conditional rendering:
|
|
280
|
+
|
|
281
|
+
```tsx
|
|
282
|
+
import { Protect } from '@insforge/react';
|
|
283
|
+
|
|
284
|
+
function Dashboard() {
|
|
285
|
+
return (
|
|
286
|
+
<div>
|
|
287
|
+
<h1>Dashboard</h1>
|
|
288
|
+
|
|
289
|
+
{/* Simple protection */}
|
|
290
|
+
<Protect redirectTo="/sign-in">
|
|
291
|
+
<UserContent />
|
|
292
|
+
</Protect>
|
|
293
|
+
|
|
294
|
+
{/* Role-based protection */}
|
|
295
|
+
<Protect
|
|
296
|
+
redirectTo="/unauthorized"
|
|
297
|
+
condition={(user) => user.role === 'admin'}
|
|
298
|
+
>
|
|
299
|
+
<AdminPanel />
|
|
300
|
+
</Protect>
|
|
301
|
+
</div>
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### `<SignedIn>` / `<SignedOut>`
|
|
307
|
+
|
|
308
|
+
Conditional rendering based on auth state:
|
|
309
|
+
|
|
310
|
+
```tsx
|
|
311
|
+
import { SignedIn, SignedOut } from '@insforge/react';
|
|
312
|
+
|
|
313
|
+
function NavBar() {
|
|
314
|
+
return (
|
|
315
|
+
<nav>
|
|
316
|
+
<SignedOut>
|
|
317
|
+
<a href="/sign-in">Sign In</a>
|
|
318
|
+
<a href="/sign-up">Sign Up</a>
|
|
319
|
+
</SignedOut>
|
|
320
|
+
|
|
321
|
+
<SignedIn>
|
|
322
|
+
<a href="/dashboard">Dashboard</a>
|
|
323
|
+
<UserButton />
|
|
324
|
+
</SignedIn>
|
|
325
|
+
</nav>
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### `<InsforgeCallback />`
|
|
331
|
+
|
|
332
|
+
OAuth callback handler (3 lines instead of 70+):
|
|
333
|
+
|
|
334
|
+
```tsx
|
|
335
|
+
'use client';
|
|
336
|
+
import { InsforgeCallback } from '@insforge/react';
|
|
337
|
+
|
|
338
|
+
export default function CallbackPage() {
|
|
339
|
+
return <InsforgeCallback redirectTo="/dashboard" />;
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
## Hooks
|
|
346
|
+
|
|
347
|
+
### `useAuth()`
|
|
348
|
+
|
|
349
|
+
Access authentication methods:
|
|
350
|
+
|
|
351
|
+
```tsx
|
|
352
|
+
import { useAuth } from '@insforge/react';
|
|
353
|
+
|
|
354
|
+
function LoginButton() {
|
|
355
|
+
const { signIn, signUp, signOut, isSignedIn, isLoaded } = useAuth();
|
|
356
|
+
|
|
357
|
+
const handleSignIn = async () => {
|
|
358
|
+
try {
|
|
359
|
+
await signIn('user@example.com', 'password');
|
|
360
|
+
// Redirect or update UI
|
|
361
|
+
} catch (error) {
|
|
362
|
+
console.error('Sign in failed:', error);
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
if (!isLoaded) return <div>Loading...</div>;
|
|
367
|
+
|
|
368
|
+
return (
|
|
369
|
+
<button onClick={isSignedIn ? signOut : handleSignIn}>
|
|
370
|
+
{isSignedIn ? 'Sign Out' : 'Sign In'}
|
|
371
|
+
</button>
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
**Returns:**
|
|
377
|
+
- `signIn(email, password)` - Sign in with email/password
|
|
378
|
+
- `signUp(email, password)` - Sign up new user
|
|
379
|
+
- `signOut()` - Sign out current user
|
|
380
|
+
- `isSignedIn` - Boolean auth state
|
|
381
|
+
- `isLoaded` - Boolean loading state
|
|
382
|
+
|
|
383
|
+
### `useUser()`
|
|
384
|
+
|
|
385
|
+
Access user data:
|
|
386
|
+
|
|
387
|
+
```tsx
|
|
388
|
+
import { useUser } from '@insforge/react';
|
|
389
|
+
|
|
390
|
+
function UserProfile() {
|
|
391
|
+
const { user, isLoaded, updateUser } = useUser();
|
|
392
|
+
|
|
393
|
+
if (!isLoaded) return <div>Loading...</div>;
|
|
394
|
+
if (!user) return <div>Not signed in</div>;
|
|
395
|
+
|
|
396
|
+
const handleUpdate = async () => {
|
|
397
|
+
await updateUser({ name: 'New Name' });
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
return (
|
|
401
|
+
<div>
|
|
402
|
+
<p>Email: {user.email}</p>
|
|
403
|
+
<p>Name: {user.name}</p>
|
|
404
|
+
<img src={user.avatarUrl} alt="Avatar" />
|
|
405
|
+
<button onClick={handleUpdate}>Update Name</button>
|
|
406
|
+
</div>
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
**Returns:**
|
|
412
|
+
- `user` - User object with id, email, name, avatarUrl
|
|
413
|
+
- `isLoaded` - Boolean loading state
|
|
414
|
+
- `updateUser(data)` - Update user profile
|
|
415
|
+
- `setUser(user)` - Manually set user state
|
|
416
|
+
|
|
417
|
+
### `usePublicAuthConfig()`
|
|
418
|
+
|
|
419
|
+
Get OAuth providers and password requirements:
|
|
420
|
+
|
|
421
|
+
```tsx
|
|
422
|
+
import { usePublicAuthConfig } from '@insforge/react';
|
|
423
|
+
|
|
424
|
+
function SignInPage() {
|
|
425
|
+
const { oauthProviders, emailConfig, isLoaded } = usePublicAuthConfig();
|
|
426
|
+
|
|
427
|
+
if (!isLoaded) return <div>Loading...</div>;
|
|
428
|
+
|
|
429
|
+
return (
|
|
430
|
+
<div>
|
|
431
|
+
<p>Available OAuth: {oauthProviders.join(', ')}</p>
|
|
432
|
+
<p>Password min length: {emailConfig?.passwordMinLength}</p>
|
|
433
|
+
</div>
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
**⚠️ Important:** Only use this hook in SignIn/SignUp components to avoid unnecessary API calls.
|
|
439
|
+
|
|
440
|
+
---
|
|
441
|
+
|
|
442
|
+
## UI Form Components (Pure UI)
|
|
443
|
+
|
|
444
|
+
Build custom auth flows with pre-built forms:
|
|
445
|
+
|
|
446
|
+
### `<SignInForm />`
|
|
447
|
+
|
|
448
|
+
```tsx
|
|
449
|
+
import { SignInForm } from '@insforge/react';
|
|
450
|
+
import { useState } from 'react';
|
|
451
|
+
|
|
452
|
+
function CustomSignIn() {
|
|
453
|
+
const [email, setEmail] = useState('');
|
|
454
|
+
const [password, setPassword] = useState('');
|
|
455
|
+
const [error, setError] = useState('');
|
|
456
|
+
const [loading, setLoading] = useState(false);
|
|
457
|
+
|
|
458
|
+
const handleSubmit = async (e) => {
|
|
459
|
+
e.preventDefault();
|
|
460
|
+
setLoading(true);
|
|
461
|
+
// Your auth logic
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
return (
|
|
465
|
+
<SignInForm
|
|
466
|
+
email={email}
|
|
467
|
+
password={password}
|
|
468
|
+
onEmailChange={setEmail}
|
|
469
|
+
onPasswordChange={setPassword}
|
|
470
|
+
onSubmit={handleSubmit}
|
|
471
|
+
error={error}
|
|
472
|
+
loading={loading}
|
|
473
|
+
availableProviders={['google', 'github']}
|
|
474
|
+
onOAuthClick={(provider) => handleOAuth(provider)}
|
|
475
|
+
/>
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
**Other Form Components:**
|
|
481
|
+
- `<SignUpForm />` - Sign up with password strength
|
|
482
|
+
- `<ForgotPasswordForm />` - Request password reset
|
|
483
|
+
- `<ResetPasswordForm />` - Reset password with token
|
|
484
|
+
- `<VerifyEmailStatus />` - Email verification status
|
|
485
|
+
|
|
486
|
+
---
|
|
487
|
+
|
|
488
|
+
## Atomic Components (Maximum Flexibility)
|
|
489
|
+
|
|
490
|
+
Build completely custom UIs:
|
|
491
|
+
|
|
492
|
+
```tsx
|
|
493
|
+
import {
|
|
494
|
+
AuthContainer,
|
|
495
|
+
AuthHeader,
|
|
496
|
+
AuthFormField,
|
|
497
|
+
AuthPasswordField,
|
|
498
|
+
AuthSubmitButton,
|
|
499
|
+
AuthErrorBanner,
|
|
500
|
+
AuthDivider,
|
|
501
|
+
AuthOAuthProviders,
|
|
502
|
+
AuthLink,
|
|
503
|
+
} from '@insforge/react';
|
|
504
|
+
|
|
505
|
+
function CompletelyCustomAuth() {
|
|
506
|
+
return (
|
|
507
|
+
<AuthContainer
|
|
508
|
+
appearance={{
|
|
509
|
+
containerClassName: "max-w-md",
|
|
510
|
+
cardClassName: "bg-white shadow-2xl"
|
|
511
|
+
}}
|
|
512
|
+
>
|
|
513
|
+
<AuthHeader
|
|
514
|
+
title="Welcome to MyApp"
|
|
515
|
+
subtitle="Sign in to continue"
|
|
516
|
+
appearance={{
|
|
517
|
+
titleClassName: "text-3xl text-blue-900"
|
|
518
|
+
}}
|
|
519
|
+
/>
|
|
520
|
+
|
|
521
|
+
<AuthErrorBanner error={error} />
|
|
522
|
+
|
|
523
|
+
<form onSubmit={handleSubmit}>
|
|
524
|
+
<AuthFormField
|
|
525
|
+
id="email"
|
|
526
|
+
type="email"
|
|
527
|
+
label="Email"
|
|
528
|
+
value={email}
|
|
529
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
530
|
+
appearance={{
|
|
531
|
+
inputClassName: "border-blue-500"
|
|
532
|
+
}}
|
|
533
|
+
/>
|
|
534
|
+
|
|
535
|
+
<AuthPasswordField
|
|
536
|
+
id="password"
|
|
537
|
+
label="Password"
|
|
538
|
+
value={password}
|
|
539
|
+
onChange={(e) => setPassword(e.target.value)}
|
|
540
|
+
emailAuthConfig={config}
|
|
541
|
+
showStrengthIndicator
|
|
542
|
+
/>
|
|
543
|
+
|
|
544
|
+
<AuthSubmitButton isLoading={loading}>
|
|
545
|
+
Sign In
|
|
546
|
+
</AuthSubmitButton>
|
|
547
|
+
</form>
|
|
548
|
+
|
|
549
|
+
<AuthDivider text="or" />
|
|
550
|
+
|
|
551
|
+
<AuthOAuthProviders
|
|
552
|
+
providers={['google', 'github', 'discord']}
|
|
553
|
+
onClick={handleOAuth}
|
|
554
|
+
loading={oauthLoading}
|
|
555
|
+
/>
|
|
556
|
+
|
|
557
|
+
<AuthLink
|
|
558
|
+
text="Don't have an account?"
|
|
559
|
+
linkText="Sign up"
|
|
560
|
+
href="/sign-up"
|
|
561
|
+
/>
|
|
562
|
+
</AuthContainer>
|
|
563
|
+
);
|
|
564
|
+
}
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
**Available Atomic Components:**
|
|
568
|
+
- `AuthContainer` - Main wrapper with branding
|
|
569
|
+
- `AuthHeader` - Title and subtitle
|
|
570
|
+
- `AuthErrorBanner` - Error messages
|
|
571
|
+
- `AuthFormField` - Standard input
|
|
572
|
+
- `AuthPasswordField` - Password with toggle
|
|
573
|
+
- `AuthPasswordStrengthIndicator` - Password checklist
|
|
574
|
+
- `AuthSubmitButton` - Loading button
|
|
575
|
+
- `AuthLink` - Navigation link
|
|
576
|
+
- `AuthDivider` - Visual separator
|
|
577
|
+
- `AuthOAuthButton` - Single OAuth button
|
|
578
|
+
- `AuthOAuthProviders` - OAuth grid
|
|
579
|
+
- `AuthVerificationCodeInput` - 6-digit OTP
|
|
580
|
+
- `AuthBranding` - Insforge branding
|
|
581
|
+
|
|
582
|
+
---
|
|
583
|
+
|
|
584
|
+
## Customization
|
|
585
|
+
|
|
586
|
+
### Appearance Props
|
|
587
|
+
|
|
588
|
+
All components support Tailwind className overrides:
|
|
589
|
+
|
|
590
|
+
```tsx
|
|
591
|
+
<SignIn
|
|
592
|
+
appearance={{
|
|
593
|
+
containerClassName: "shadow-2xl max-w-lg",
|
|
594
|
+
cardClassName: "bg-gradient-to-br from-blue-50 to-white",
|
|
595
|
+
formClassName: "space-y-6",
|
|
596
|
+
buttonClassName: "bg-blue-600 hover:bg-blue-700 h-12"
|
|
597
|
+
}}
|
|
598
|
+
/>
|
|
599
|
+
|
|
600
|
+
<UserButton
|
|
601
|
+
appearance={{
|
|
602
|
+
buttonClassName: "hover:bg-gray-100 rounded-full",
|
|
603
|
+
nameClassName: "text-gray-900 font-semibold",
|
|
604
|
+
emailClassName: "text-gray-500",
|
|
605
|
+
dropdownClassName: "shadow-xl"
|
|
606
|
+
}}
|
|
607
|
+
/>
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
### Text Customization
|
|
611
|
+
|
|
612
|
+
All text is customizable:
|
|
613
|
+
|
|
614
|
+
```tsx
|
|
615
|
+
<SignIn
|
|
616
|
+
title="Welcome Back!"
|
|
617
|
+
subtitle="We're happy to see you again"
|
|
618
|
+
emailLabel="Your Email Address"
|
|
619
|
+
emailPlaceholder="you@company.com"
|
|
620
|
+
passwordLabel="Your Password"
|
|
621
|
+
submitButtonText="Login Now"
|
|
622
|
+
loadingButtonText="Signing you in..."
|
|
623
|
+
signUpText="New to our platform?"
|
|
624
|
+
signUpLinkText="Create an account"
|
|
625
|
+
dividerText="or continue with"
|
|
626
|
+
/>
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
---
|
|
630
|
+
|
|
631
|
+
## Framework Integration
|
|
632
|
+
|
|
633
|
+
### Next.js (App Router)
|
|
634
|
+
|
|
635
|
+
```tsx
|
|
636
|
+
// app/layout.tsx
|
|
637
|
+
'use client';
|
|
638
|
+
import { InsforgeProvider } from '@insforge/react';
|
|
639
|
+
import '@insforge/react/styles.css';
|
|
640
|
+
|
|
641
|
+
export default function RootLayout({ children }) {
|
|
642
|
+
return (
|
|
643
|
+
<html>
|
|
644
|
+
<body>
|
|
645
|
+
<InsforgeProvider
|
|
646
|
+
baseUrl={process.env.NEXT_PUBLIC_INSFORGE_BASE_URL}
|
|
647
|
+
syncTokenToCookie={async (token) => {
|
|
648
|
+
await fetch('/api/auth', {
|
|
649
|
+
method: 'POST',
|
|
650
|
+
body: JSON.stringify({ token })
|
|
651
|
+
});
|
|
652
|
+
return true;
|
|
653
|
+
}}
|
|
654
|
+
clearCookie={async () => {
|
|
655
|
+
await fetch('/api/auth', { method: 'DELETE' });
|
|
656
|
+
}}
|
|
657
|
+
>
|
|
658
|
+
{children}
|
|
659
|
+
</InsforgeProvider>
|
|
660
|
+
</body>
|
|
661
|
+
</html>
|
|
662
|
+
);
|
|
663
|
+
}
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
### Vite / React
|
|
667
|
+
|
|
668
|
+
```tsx
|
|
669
|
+
// src/main.tsx
|
|
670
|
+
import { StrictMode } from 'react';
|
|
671
|
+
import { createRoot } from 'react-dom/client';
|
|
672
|
+
import { BrowserRouter } from 'react-router-dom';
|
|
673
|
+
import { InsforgeProvider } from '@insforge/react';
|
|
674
|
+
import '@insforge/react/styles.css';
|
|
675
|
+
import App from './App';
|
|
676
|
+
|
|
677
|
+
createRoot(document.getElementById('root')!).render(
|
|
678
|
+
<StrictMode>
|
|
679
|
+
<BrowserRouter>
|
|
680
|
+
<InsforgeProvider baseUrl={import.meta.env.VITE_INSFORGE_BASE_URL}>
|
|
681
|
+
<App />
|
|
682
|
+
</InsforgeProvider>
|
|
683
|
+
</BrowserRouter>
|
|
684
|
+
</StrictMode>
|
|
685
|
+
);
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
### Remix
|
|
689
|
+
|
|
690
|
+
```tsx
|
|
691
|
+
// app/root.tsx
|
|
692
|
+
import { InsforgeProvider } from '@insforge/react';
|
|
693
|
+
import '@insforge/react/styles.css';
|
|
694
|
+
|
|
695
|
+
export default function App() {
|
|
696
|
+
return (
|
|
697
|
+
<html>
|
|
698
|
+
<body>
|
|
699
|
+
<InsforgeProvider baseUrl={process.env.INSFORGE_BASE_URL}>
|
|
700
|
+
<Outlet />
|
|
701
|
+
</InsforgeProvider>
|
|
702
|
+
</body>
|
|
703
|
+
</html>
|
|
704
|
+
);
|
|
705
|
+
}
|
|
706
|
+
```
|
|
707
|
+
|
|
708
|
+
---
|
|
709
|
+
|
|
710
|
+
## TypeScript
|
|
711
|
+
|
|
712
|
+
Full TypeScript support with exported types:
|
|
713
|
+
|
|
714
|
+
```tsx
|
|
715
|
+
import type {
|
|
716
|
+
InsforgeUser,
|
|
717
|
+
SignInProps,
|
|
718
|
+
SignUpProps,
|
|
719
|
+
UserButtonProps,
|
|
720
|
+
ProtectProps,
|
|
721
|
+
ConditionalProps,
|
|
722
|
+
InsforgeCallbackProps,
|
|
723
|
+
SignInFormProps,
|
|
724
|
+
SignUpFormProps,
|
|
725
|
+
AuthFormFieldProps,
|
|
726
|
+
OAuthProvider,
|
|
727
|
+
EmailAuthConfig,
|
|
728
|
+
InsforgeProviderProps,
|
|
729
|
+
} from '@insforge/react';
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
---
|
|
733
|
+
|
|
734
|
+
## API Reference
|
|
735
|
+
|
|
736
|
+
### InsforgeProvider Props
|
|
737
|
+
|
|
738
|
+
```tsx
|
|
739
|
+
interface InsforgeProviderProps {
|
|
740
|
+
baseUrl: string; // Insforge backend URL
|
|
741
|
+
onAuthChange?: (user: InsforgeUser | null) => void; // Auth state callback
|
|
742
|
+
syncTokenToCookie?: (token: string) => Promise<boolean>; // Custom cookie sync
|
|
743
|
+
clearCookie?: () => Promise<void>; // Custom cookie clear
|
|
744
|
+
}
|
|
745
|
+
```
|
|
746
|
+
|
|
747
|
+
### SignIn / SignUp Props
|
|
748
|
+
|
|
749
|
+
```tsx
|
|
750
|
+
interface SignInProps {
|
|
751
|
+
afterSignInUrl?: string; // Redirect after sign in
|
|
752
|
+
signUpUrl?: string; // Link to sign up page
|
|
753
|
+
forgotPasswordUrl?: string; // Link to forgot password
|
|
754
|
+
onSuccess?: (user, token) => void; // Success callback
|
|
755
|
+
onError?: (error: Error) => void; // Error callback
|
|
756
|
+
onRedirect?: (url: string) => void; // Custom redirect handler
|
|
757
|
+
title?: string; // Custom title
|
|
758
|
+
subtitle?: string; // Custom subtitle
|
|
759
|
+
appearance?: { // Custom styling
|
|
760
|
+
containerClassName?: string;
|
|
761
|
+
cardClassName?: string;
|
|
762
|
+
formClassName?: string;
|
|
763
|
+
buttonClassName?: string;
|
|
764
|
+
};
|
|
765
|
+
// ... more text customization props
|
|
766
|
+
}
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
### InsforgeCallback Props
|
|
770
|
+
|
|
771
|
+
```tsx
|
|
772
|
+
interface InsforgeCallbackProps {
|
|
773
|
+
redirectTo?: string; // Custom redirect destination
|
|
774
|
+
onSuccess?: () => void; // Success callback
|
|
775
|
+
onError?: (error: string) => void; // Error callback
|
|
776
|
+
loadingComponent?: ReactNode; // Custom loading UI
|
|
777
|
+
onRedirect?: (url: string) => void; // Custom redirect handler
|
|
778
|
+
}
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
---
|
|
782
|
+
|
|
783
|
+
## Validation Utilities
|
|
784
|
+
|
|
785
|
+
```tsx
|
|
786
|
+
import { emailSchema, cn } from '@insforge/react';
|
|
787
|
+
|
|
788
|
+
// Validate email with Zod
|
|
789
|
+
const result = emailSchema.safeParse('user@example.com');
|
|
790
|
+
|
|
791
|
+
// Merge Tailwind classes
|
|
792
|
+
const className = cn('px-4 py-2', 'bg-blue-500', conditionalClass);
|
|
793
|
+
```
|
|
794
|
+
|
|
795
|
+
---
|
|
796
|
+
|
|
797
|
+
## OAuth Providers
|
|
798
|
+
|
|
799
|
+
Built-in support for 10+ OAuth providers:
|
|
800
|
+
- Google
|
|
801
|
+
- GitHub
|
|
802
|
+
- Discord
|
|
803
|
+
- Apple
|
|
804
|
+
- Microsoft
|
|
805
|
+
- Facebook
|
|
806
|
+
- LinkedIn
|
|
807
|
+
- Instagram
|
|
808
|
+
- TikTok
|
|
809
|
+
- Spotify
|
|
810
|
+
- X (Twitter)
|
|
811
|
+
|
|
812
|
+
Providers are auto-detected from your backend configuration.
|
|
813
|
+
|
|
814
|
+
---
|
|
815
|
+
|
|
816
|
+
## Why @insforge/react?
|
|
817
|
+
|
|
818
|
+
**vs. Building Custom Auth:**
|
|
819
|
+
- ⚡️ 5 minutes vs 2+ days of development
|
|
820
|
+
- 🔒 Production-ready security built-in
|
|
821
|
+
- 🎨 Customizable when needed, works out of the box
|
|
822
|
+
- 🚀 No framework lock-in
|
|
823
|
+
|
|
824
|
+
**vs. Other Auth Libraries:**
|
|
825
|
+
- 📦 Complete package (not just UI)
|
|
826
|
+
- 🎯 Framework agnostic (works everywhere)
|
|
827
|
+
- 🤖 SDK-first approach (consistent API)
|
|
828
|
+
- 💰 Self-hosted (no vendor lock-in)
|
|
829
|
+
|
|
830
|
+
---
|
|
831
|
+
|
|
832
|
+
## Examples
|
|
833
|
+
|
|
834
|
+
Check out example integrations:
|
|
835
|
+
- [Next.js App Router](./examples/nextjs)
|
|
836
|
+
- [Vite + React Router](./examples/vite)
|
|
837
|
+
- [Remix](./examples/remix)
|
|
838
|
+
|
|
839
|
+
---
|
|
840
|
+
|
|
841
|
+
## Support
|
|
842
|
+
|
|
843
|
+
- **Documentation**: https://docs.insforge.dev
|
|
844
|
+
- **GitHub Issues**: https://github.com/InsForge/InsForge/issues
|
|
845
|
+
- **Discord Community**: https://discord.com/invite/DvBtaEc9Jz
|
|
846
|
+
|
|
847
|
+
## License
|
|
848
|
+
|
|
849
|
+
MIT © Insforge
|