@insforge/nextjs 0.6.5 → 0.6.6

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 (2) hide show
  1. package/README.md +151 -898
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,84 +1,35 @@
1
1
  # @insforge/nextjs
2
2
 
3
- Pre-built authentication UI components for Next.js applications using Insforge backend. This package provides a Clerk-like experience with drop-in components that connect directly to your Insforge backend, reducing token usage for AI agents.
3
+ **Zero-configuration authentication for Next.js** using Insforge backend. Authentication pages are hosted on your backend by default—no UI code needed.
4
+
5
+ ## Why Built-in Auth?
6
+
7
+ ✅ **Zero UI Code** - No SignIn/SignUp pages to create
8
+ ✅ **5-Minute Setup** - Provider + API route + callback page = done
9
+ ✅ **Production Ready** - Battle-tested auth UI maintained by Insforge
10
+ ✅ **Auto OAuth** - 11 providers configured automatically from backend
11
+ ✅ **AI-Friendly** - Minimal code generation = fewer tokens
12
+
13
+ **Use custom components only if you need highly customized branding or custom fields.**
4
14
 
5
15
  ## Features
6
16
 
7
- - **Built-in Authentication (v0.6.0+)**: Zero-config authentication using backend-hosted pages
8
- - **Drop-in UI Components**: Pre-styled SignIn, SignUp, and UserButton components
9
- - **Auth Primitives (v0.5.0+)**: 10 low-level components for building custom auth UIs
10
- - **React Hooks**: Easy access to auth state with `useAuth()`, `useUser()`, `useSession()`
11
- - **Control Components**: `<SignedIn>`, `<SignedOut>`, `<Protect>` for conditional rendering
17
+ - **Built-in Authentication**: Backend-hosted sign-in/sign-up pages (default)
18
+ - **OAuth Support**: Google, GitHub, Discord, Facebook, LinkedIn, Microsoft, Apple, X, Instagram, TikTok, Spotify
19
+ - **React Hooks**: `useAuth()`, `useUser()`, `useSession()`
20
+ - **Control Components**: `<SignedIn>`, `<SignedOut>`, `<Protect>`
12
21
  - **Next.js Middleware**: Server-side route protection
13
- - **OAuth Support**: 11 providers (Google, GitHub, Discord, Facebook, LinkedIn, Microsoft, Apple, X, Instagram, TikTok, Spotify)
14
- - **Smart OAuth Layout**: Adaptive grid (1/2/3 columns) based on provider count
15
- - **TypeScript**: Full type safety out of the box
16
- - **Customizable**: Override styles with the `appearance` prop or build from primitives
22
+ - **TypeScript**: Full type safety
17
23
 
18
24
  ## Installation
19
25
 
20
26
  ```bash
21
27
  npm install @insforge/nextjs
22
- # or
23
- yarn add @insforge/nextjs
24
28
  ```
25
29
 
26
-
27
30
  ## Quick Start
28
31
 
29
- ### 1. Wrap your app with InsforgeProvider
30
-
31
- ```tsx
32
- // app/layout.tsx
33
- import { InsforgeProvider } from '@insforge/nextjs';
34
- import '@insforge/nextjs/styles.css';
35
-
36
- export default function RootLayout({ children }: { children: React.ReactNode }) {
37
- const baseUrl = process.env.NEXT_PUBLIC_INSFORGE_BASE_URL!;
38
-
39
- return (
40
- <html lang="en">
41
- <body>
42
- <InsforgeProvider baseUrl={baseUrl}>
43
- {children}
44
- </InsforgeProvider>
45
- </body>
46
- </html>
47
- );
48
- }
49
- ```
50
-
51
- **That's it!** By default, the package uses **built-in authentication** - users will be automatically redirected to your backend's authentication pages when not signed in. No additional setup required!
52
-
53
- ## Built-in Authentication (Default)
54
-
55
- The package now includes **built-in authentication** by default, which means:
56
-
57
- - ✅ **Zero UI Code**: No need to create SignIn/SignUp pages
58
- - ✅ **Automatic Redirects**: Users are redirected to backend auth pages when not signed in
59
- - ✅ **Same-Domain Performance**: Faster API calls without CORS overhead
60
- - ✅ **Unified Experience**: Consistent auth UI across all Insforge apps
61
-
62
- ### How it works
63
-
64
- 1. User visits a protected page (e.g., `/dashboard`)
65
- 2. Provider detects user is not signed in
66
- 3. Automatically redirects to: `https://your-backend.insforge.app/auth/signin?redirect=https://yourapp.com/auth/callback`
67
- 4. User signs in on the backend's authentication page
68
- 5. Backend redirects back to: `https://yourapp.com/auth/callback?access_token=xxx`
69
- 6. Your callback page stores the token and redirects to the original destination
70
-
71
- ### Local Development Setup
72
-
73
- For local development, your backend API and frontend typically run on different ports. You can configure this in two ways:
74
-
75
- **Option 1: Using environment variables (recommended)**
76
-
77
- ```bash
78
- # .env.local
79
- NEXT_PUBLIC_INSFORGE_BASE_URL=http://localhost:7130 # Backend API
80
- NEXT_PUBLIC_INSFORGE_FRONTEND_URL=http://localhost:7131 # Backend Frontend
81
- ```
32
+ ### 1. Add InsforgeProvider
82
33
 
83
34
  ```tsx
84
35
  // app/layout.tsx
@@ -97,59 +48,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
97
48
  }
98
49
  ```
99
50
 
100
- **Option 2: Using the `frontendUrl` prop**
101
-
102
- ```tsx
103
- // app/layout.tsx
104
- import { InsforgeProvider } from '@insforge/nextjs';
105
-
106
- export default function RootLayout({ children }: { children: React.ReactNode }) {
107
- return (
108
- <html lang="en">
109
- <body>
110
- <InsforgeProvider
111
- baseUrl={process.env.NEXT_PUBLIC_INSFORGE_BASE_URL!}
112
- frontendUrl={process.env.NEXT_PUBLIC_INSFORGE_FRONTEND_URL}
113
- >
114
- {children}
115
- </InsforgeProvider>
116
- </body>
117
- </html>
118
- );
119
- }
120
- ```
121
-
122
- **Priority:** `frontendUrl` prop > `NEXT_PUBLIC_INSFORGE_FRONTEND_URL` env var > `baseUrl` (fallback)
123
-
124
- **Production:** If your API and frontend share the same URL, you only need `NEXT_PUBLIC_INSFORGE_BASE_URL`.
125
-
126
- ### Using Custom SignIn/SignUp Components
127
-
128
- If you prefer to use your own authentication UI, set `useBuiltInAuth={false}`:
129
-
130
- ```tsx
131
- // app/layout.tsx
132
- import { InsforgeProvider } from '@insforge/nextjs';
133
-
134
- export default function RootLayout({ children }: { children: React.ReactNode }) {
135
- return (
136
- <html lang="en">
137
- <body>
138
- <InsforgeProvider
139
- baseUrl={process.env.NEXT_PUBLIC_INSFORGE_BASE_URL!}
140
- useBuiltInAuth={false} // Use custom components instead
141
- >
142
- {children}
143
- </InsforgeProvider>
144
- </body>
145
- </html>
146
- );
147
- }
148
- ```
149
-
150
- ### 2. Set up API route for authentication (Required for SSR)
151
-
152
- Create an API route to handle authentication cookies. This is **essential** for Next.js server-side rendering and middleware to work properly.
51
+ ### 2. Create API route (enables SSR)
153
52
 
154
53
  ```tsx
155
54
  // app/api/auth/route.ts
@@ -165,16 +64,9 @@ export const GET = handlers.GET;
165
64
  export const DELETE = handlers.DELETE;
166
65
  ```
167
66
 
168
- **Why is this needed?**
169
-
170
- Next.js middleware runs on the server and cannot access `localStorage` (which is browser-only). This API route:
171
- - Syncs tokens from `localStorage` to HTTP-only cookies
172
- - Enables server-side authentication in middleware
173
- - Provides server-side sign-in/sign-up endpoints
174
-
175
- ### 3. Add authentication callback page (Required for Next.js SSR)
67
+ > **Why?** Syncs auth tokens to HTTP-only cookies for server-side middleware.
176
68
 
177
- Create a callback page to handle post-authentication token synchronization. This is **required for all authentication methods** (email/password and OAuth) when using Next.js middleware:
69
+ ### 3. Create callback page
178
70
 
179
71
  ```tsx
180
72
  // app/auth/callback/page.tsx
@@ -189,8 +81,7 @@ function CallbackContent() {
189
81
  const isProcessingRef = useRef(false);
190
82
 
191
83
  useEffect(() => {
192
- const processOAuthCallback = async () => {
193
- // Prevent double-processing in React Strict Mode
84
+ const processCallback = async () => {
194
85
  if (isProcessingRef.current) return;
195
86
  isProcessingRef.current = true;
196
87
 
@@ -198,59 +89,27 @@ function CallbackContent() {
198
89
  const error = searchParams.get('error');
199
90
 
200
91
  if (error) {
201
- console.error('OAuth error:', error);
202
- router.push('/sign-in?error=' + encodeURIComponent(error));
92
+ router.push('/?error=' + encodeURIComponent(error));
203
93
  return;
204
94
  }
205
95
 
206
96
  if (accessToken) {
207
- // 1. Store token in localStorage (for SDK client-side operations)
208
97
  localStorage.setItem('insforge-auth-token', accessToken);
209
98
 
210
- console.log(' OAuth token stored in localStorage');
211
-
212
- // 2. Sync token to HTTP-only cookie (for server-side middleware)
213
- try {
214
- const syncResponse = await fetch('/api/auth', {
215
- method: 'POST',
216
- headers: {
217
- 'Content-Type': 'application/json',
218
- },
219
- body: JSON.stringify({
220
- action: 'sync-token',
221
- token: accessToken,
222
- }),
223
- });
224
-
225
- if (syncResponse.ok) {
226
- console.log('✅ OAuth token synced to cookie');
227
- } else {
228
- console.warn('⚠️ Failed to sync token to cookie, but localStorage has it');
229
- }
230
- } catch (syncError) {
231
- console.warn('⚠️ Cookie sync failed:', syncError);
232
- }
233
-
234
- // 3. Get the final destination from sessionStorage
235
- const finalDestination = sessionStorage.getItem('oauth_final_destination') || '/dashboard';
236
- sessionStorage.removeItem('oauth_final_destination');
99
+ await fetch('/api/auth', {
100
+ method: 'POST',
101
+ headers: { 'Content-Type': 'application/json' },
102
+ body: JSON.stringify({ action: 'sync-token', token: accessToken }),
103
+ });
237
104
 
238
- // 4. Clean up URL to remove OAuth params before redirect
239
- window.history.replaceState({}, '', '/auth/callback');
105
+ const destination = sessionStorage.getItem('auth_destination') || '/dashboard';
106
+ sessionStorage.removeItem('auth_destination');
240
107
 
241
- // 5. Small delay to ensure cookie is set before redirect
242
- setTimeout(() => {
243
- router.push(finalDestination);
244
- }, 100);
245
- } else {
246
- if (searchParams.toString()) {
247
- console.error('No token received from OAuth');
248
- router.push('/sign-in?error=no_token');
249
- }
108
+ setTimeout(() => router.push(destination), 100);
250
109
  }
251
110
  };
252
111
 
253
- processOAuthCallback();
112
+ processCallback();
254
113
  }, [searchParams, router]);
255
114
 
256
115
  return (
@@ -263,64 +122,35 @@ function CallbackContent() {
263
122
  );
264
123
  }
265
124
 
266
- export default function OAuthCallbackPage() {
125
+ export default function CallbackPage() {
267
126
  return (
268
- <Suspense fallback={
269
- <div className="flex items-center justify-center min-h-screen">
270
- <div>Loading...</div>
271
- </div>
272
- }>
127
+ <Suspense fallback={<div>Loading...</div>}>
273
128
  <CallbackContent />
274
129
  </Suspense>
275
130
  );
276
131
  }
277
132
  ```
278
133
 
279
- **What this does:**
280
- 1. Receives authentication callback with `access_token` from backend (works for both OAuth and email/password)
281
- 2. Stores token in `localStorage` for SDK client-side operations
282
- 3. Syncs token to HTTP-only cookie via `/api/auth` for server-side middleware
283
- 4. Redirects user to their intended destination
284
-
285
- **Note:** This callback page handles returns from:
286
- - OAuth providers (Google, GitHub, etc.)
287
- - Email/password sign-in/sign-up when using the pre-built components
288
-
289
- ### 4. Add sign-in page
290
-
291
- ```tsx
292
- // app/sign-in/page.tsx
293
- import { SignIn } from '@insforge/nextjs';
294
-
295
- export default function SignInPage() {
296
- return (
297
- <SignIn
298
- afterSignInUrl="/dashboard"
299
- title="Welcome to MyApp"
300
- subtitle="Sign in to continue"
301
- />
302
- );
303
- }
304
- ```
134
+ > **Why?** Receives token from backend auth pages and syncs to localStorage + cookie.
305
135
 
306
- **OAuth Providers:** The package automatically detects which OAuth providers are configured on your backend. You don't need to specify them manually!
136
+ ### 4. Add middleware (protects routes)
307
137
 
308
- ### 5. Add sign-up page
138
+ ```ts
139
+ // middleware.ts
140
+ import { InsforgeMiddleware } from '@insforge/nextjs/middleware';
309
141
 
310
- ```tsx
311
- // app/sign-up/page.tsx
312
- import { SignUp } from '@insforge/nextjs';
142
+ export default InsforgeMiddleware({
143
+ baseUrl: process.env.INSFORGE_BASE_URL!,
144
+ publicRoutes: ['/auth/callback', '/'],
145
+ cookieName: 'insforge_token',
146
+ });
313
147
 
314
- export default function SignUpPage() {
315
- return (
316
- <SignUp
317
- afterSignUpUrl="/dashboard"
318
- />
319
- );
320
- }
148
+ export const config = {
149
+ matcher: ['/((?!api|_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)'],
150
+ };
321
151
  ```
322
152
 
323
- ### 6. Use conditional components
153
+ ### 5. Use in components
324
154
 
325
155
  ```tsx
326
156
  // app/page.tsx
@@ -329,832 +159,255 @@ import { SignedIn, SignedOut, UserButton } from '@insforge/nextjs';
329
159
  export default function Home() {
330
160
  return (
331
161
  <div>
332
- <nav>
333
- <SignedOut>
334
- <a href="/sign-in">Sign In</a>
335
- </SignedOut>
336
-
337
- <SignedIn>
338
- <UserButton afterSignOutUrl="/" />
339
- </SignedIn>
340
- </nav>
162
+ <SignedOut>
163
+ <a href="/sign-in">Sign In</a>
164
+ </SignedOut>
341
165
 
342
166
  <SignedIn>
167
+ <UserButton afterSignOutUrl="/" />
343
168
  <h1>Welcome back!</h1>
344
169
  </SignedIn>
345
-
346
- <SignedOut>
347
- <h1>Please sign in</h1>
348
- </SignedOut>
349
170
  </div>
350
171
  );
351
172
  }
352
173
  ```
353
174
 
354
- ### 7. Protect routes with middleware
355
-
356
- ```ts
357
- // middleware.ts - Built-in authentication (default)
358
- import { InsforgeMiddleware } from '@insforge/nextjs/middleware';
359
-
360
- export default InsforgeMiddleware({
361
- baseUrl: process.env.INSFORGE_BASE_URL!,
362
- publicRoutes: ['/auth/callback', '/'], // Include callback!
363
- cookieName: 'insforge_token', // Must match API route cookie name
364
- });
365
-
366
- export const config = {
367
- matcher: ['/((?!api|_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)'],
368
- };
369
- ```
370
-
371
- ```ts
372
- // middleware.ts - Custom SignIn/SignUp components
373
- import { InsforgeMiddleware } from '@insforge/nextjs/middleware';
374
-
375
- export default InsforgeMiddleware({
376
- baseUrl: process.env.INSFORGE_BASE_URL!,
377
- publicRoutes: ['/sign-in', '/sign-up', '/', '/auth/callback'],
378
- signInUrl: '/sign-in',
379
- cookieName: 'insforge_token',
380
- useBuiltInAuth: false, // Use custom components
381
- });
382
-
383
- export const config = {
384
- matcher: ['/((?!api|_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)'],
385
- };
386
- ```
387
-
388
- **Important:**
389
- - **Must include** `/auth/callback` in `publicRoutes` to allow authentication returns
390
- - Use the same `cookieName` in both middleware and API route
391
- - With built-in auth, middleware won't redirect to local sign-in pages
392
-
393
- **Alternative imports:**
394
- - `InsforgeMiddleware` - Recommended, most descriptive
395
- - `withInsforgeAuth` - Alternative with Insforge branding
396
- - `withAuth` - Deprecated but still supported for backward compatibility
175
+ **That's it!** When users click "Sign In", they'll be redirected to your backend's auth page automatically.
397
176
 
398
177
  ---
399
178
 
400
- ## Architecture: Why Callback + API Route?
401
-
402
- This package is specifically designed for **Next.js App Router with Server-Side Rendering (SSR)**. Here's why both the callback page and API route are essential:
403
-
404
- ### The Challenge
405
-
406
- Next.js has two execution environments:
407
- - **Client-side**: Browser, can access `localStorage`
408
- - **Server-side**: Middleware, Server Components, cannot access `localStorage`
409
-
410
- The Insforge SDK stores authentication tokens in `localStorage` (client-side only), but Next.js middleware needs to verify authentication on the server.
411
-
412
- ### The Solution
413
-
414
- **Two-Storage Architecture:**
179
+ ## How It Works
415
180
 
416
181
  ```
417
- ┌─────────────────────────────────────────────────────────────┐
418
- │ Authentication Flow (Email/Password & OAuth) │
419
- └─────────────────────────────────────────────────────────────┘
420
-
421
- 1. User signs in (email/password OR OAuth)
182
+ 1. User clicks "Sign In" → Middleware detects no auth
422
183
 
423
- 2. Backend redirects to: /auth/callback?access_token=xxx
184
+ 2. Redirects to: https://backend.insforge.app/auth/signin?redirect=yourapp.com/auth/callback
424
185
 
425
- 3. Callback Page (Client-side)
426
- ├─ Stores token in localStorage (for SDK)
427
- └─ Calls /api/auth with "sync-token" action
186
+ 3. User signs in on backend's hosted page
428
187
 
429
- 4. API Route (Server-side)
430
- ├─ Validates token with backend
431
- └─ Sets HTTP-only cookie
188
+ 4. Backend redirects: yourapp.com/auth/callback?access_token=xxx
432
189
 
433
- 5. Middleware (Server-side)
434
- └─ Reads cookie to protect routes
190
+ 5. Callback stores token (localStorage + cookie) → User sees /dashboard
435
191
  ```
436
192
 
437
- **Why this matters:**
438
- - **Client-side operations** (SDK calls) use `localStorage`
439
- - **Server-side operations** (middleware, SSR) use HTTP-only cookies
440
- - Both storages stay synchronized through the `/api/auth` endpoint
441
-
442
- ### What Each Component Does
443
-
444
- | Component | Purpose | Runs On |
445
- |-----------|---------|---------|
446
- | **Callback Page** | Receives auth token (any method), stores in localStorage, syncs to cookie | Client |
447
- | **API Route** | Validates tokens, manages cookies, provides server-side auth | Server |
448
- | **Middleware** | Protects routes, reads cookies, validates sessions | Server |
449
- | **SDK** | Makes API calls, reads from localStorage | Client |
193
+ **Why API route + callback?**
194
+ - **API route**: Syncs tokens to HTTP-only cookies (enables server-side middleware)
195
+ - **Callback page**: Receives tokens from backend auth pages (OAuth + email/password)
450
196
 
451
- This architecture enables full-stack authentication in Next.js while maintaining security with HTTP-only cookies.
452
-
453
- **Note:** All authentication methods (email/password, OAuth) use this same flow for consistency and SSR support.
454
-
455
- ## API Reference
456
-
457
- ### Components
197
+ ---
458
198
 
459
- #### `<InsforgeProvider>`
199
+ ## Local Development
460
200
 
461
- Wraps your application and provides auth context. Automatically fetches OAuth provider configuration from your backend.
201
+ Backend typically runs on different port during local dev:
462
202
 
463
- ```tsx
464
- <InsforgeProvider
465
- baseUrl="https://your-backend.insforge.app"
466
- onAuthChange={(user) => console.log('Auth changed:', user)}
467
- >
468
- {children}
469
- </InsforgeProvider>
203
+ ```bash
204
+ # .env.local
205
+ NEXT_PUBLIC_INSFORGE_BASE_URL=http://localhost:7130 # Backend API
206
+ NEXT_PUBLIC_INSFORGE_FRONTEND_URL=http://localhost:7131 # Backend Frontend (auth pages)
207
+ INSFORGE_BASE_URL=http://localhost:7130 # For middleware
470
208
  ```
471
209
 
472
- **Props:**
473
- - `baseUrl` (required): Your Insforge backend URL
474
- - `onAuthChange`: Optional callback when auth state changes
475
-
476
- **Note:** The provider automatically queries your backend's OAuth configuration, so components like `<SignIn>` and `<SignUp>` will display the correct OAuth buttons without manual configuration.
210
+ Provider automatically uses `NEXT_PUBLIC_INSFORGE_FRONTEND_URL` for auth redirects if set.
477
211
 
478
212
  ---
479
213
 
480
- #### `<SignIn>`
214
+ ## Advanced: Custom Components
481
215
 
482
- Pre-built sign-in form with email/password and OAuth.
216
+ Need custom branding? Disable built-in auth:
483
217
 
484
218
  ```tsx
485
- <SignIn
486
- afterSignInUrl="/dashboard"
487
- appearance={{
488
- container: { background: '#f5f5f5' },
489
- button: { background: 'blue' }
490
- }}
491
- onSuccess={(user) => console.log('Signed in:', user)}
492
- onError={(error) => console.error('Error:', error)}
493
- />
494
- ```
495
-
496
- **Props:**
497
- - `afterSignInUrl`: Redirect URL after successful sign-in (default: `/`)
498
- - `appearance`: Custom styles for container, form, and button
499
- - `onSuccess`: Callback on successful sign-in
500
- - `onError`: Callback on error
501
-
502
- **Note:** OAuth providers are automatically detected from your backend configuration via `InsforgeProvider`. No need to manually specify them!
503
-
504
- ---
219
+ // app/layout.tsx
220
+ import { InsforgeProvider } from '@insforge/nextjs';
221
+ import '@insforge/nextjs/styles.css'; // Required for custom components
505
222
 
506
- #### `<SignUp>`
223
+ export default function RootLayout({ children }) {
224
+ return (
225
+ <InsforgeProvider
226
+ baseUrl={process.env.NEXT_PUBLIC_INSFORGE_BASE_URL!}
227
+ useBuiltInAuth={false}
228
+ >
229
+ {children}
230
+ </InsforgeProvider>
231
+ );
232
+ }
233
+ ```
507
234
 
508
- Pre-built sign-up form with email/password and OAuth.
235
+ ### Pre-built Components
509
236
 
510
237
  ```tsx
511
- <SignUp
512
- afterSignUpUrl="/onboarding"
513
- appearance={{
514
- container: { background: '#f5f5f5' }
515
- }}
516
- />
517
- ```
518
-
519
- **Props:**
520
- - `afterSignUpUrl`: Redirect URL after successful sign-up (default: `/`)
521
- - `appearance`: Custom styles for container, form, and button
522
- - `onSuccess`: Callback on successful sign-up
523
- - `onError`: Callback on error
238
+ // app/sign-in/page.tsx
239
+ import { SignIn } from '@insforge/nextjs';
524
240
 
525
- **Note:** OAuth providers are automatically detected from your backend configuration via `InsforgeProvider`. No need to manually specify them!
241
+ export default function SignInPage() {
242
+ return <SignIn afterSignInUrl="/dashboard" />;
243
+ }
244
+ ```
526
245
 
527
- ---
246
+ OAuth providers are auto-detected from your backend config.
528
247
 
529
- ### Auth Component Primitives (v0.5.0+)
248
+ ### Build from Primitives
530
249
 
531
- For advanced customization, use the low-level Auth primitives to build your own authentication UI:
250
+ For complete control:
532
251
 
533
252
  ```tsx
534
253
  import {
535
254
  AuthContainer,
536
255
  AuthHeader,
537
- AuthErrorBanner,
538
256
  AuthFormField,
539
257
  AuthPasswordField,
540
258
  AuthSubmitButton,
541
- AuthDivider,
542
- AuthLink,
543
259
  AuthOAuthProviders,
544
- AuthBranding,
545
260
  } from '@insforge/nextjs';
546
- import '@insforge/nextjs/styles.css';
547
261
 
548
262
  function CustomSignIn() {
549
- const [email, setEmail] = useState('');
550
- const [password, setPassword] = useState('');
551
- const [error, setError] = useState('');
552
- const [loading, setLoading] = useState(false);
553
-
554
- async function handleSubmit(e) {
555
- e.preventDefault();
556
- // Your custom auth logic
557
- }
558
-
559
- return (
560
- <AuthContainer>
561
- <AuthHeader title="Welcome Back" subtitle="Sign in to continue" />
562
-
563
- <AuthErrorBanner error={error} />
564
-
565
- <form onSubmit={handleSubmit} className="insforge-form">
566
- <AuthFormField
567
- id="email"
568
- type="email"
569
- label="Email"
570
- placeholder="you@example.com"
571
- value={email}
572
- onChange={(e) => setEmail(e.target.value)}
573
- required
574
- />
575
-
576
- <AuthPasswordField
577
- id="password"
578
- label="Password"
579
- value={password}
580
- onChange={(e) => setPassword(e.target.value)}
581
- required
582
- forgotPasswordLink={{
583
- href: '/forgot-password',
584
- text: 'Forgot Password?'
585
- }}
586
- />
587
-
588
- <AuthSubmitButton isLoading={loading}>
589
- Sign In
590
- </AuthSubmitButton>
591
- </form>
592
-
593
- <AuthLink
594
- text="Don't have an account?"
595
- linkText="Sign up"
596
- href="/sign-up"
597
- />
598
-
599
- <AuthDivider text="or" />
600
-
601
- <AuthOAuthProviders
602
- providers={['google', 'github', 'discord']}
603
- onClick={(provider) => handleOAuth(provider)}
604
- loading={null}
605
- />
606
-
607
- <AuthBranding />
608
- </AuthContainer>
609
- );
263
+ // Build your own auth UI with full control
610
264
  }
611
265
  ```
612
266
 
613
- #### Available Primitives
614
-
615
- | Component | Description |
616
- |-----------|-------------|
617
- | `<AuthContainer>` | Main wrapper with card styling. Accepts `style` prop for customization |
618
- | `<AuthHeader>` | Displays `title` and optional `subtitle` |
619
- | `<AuthErrorBanner>` | Shows error messages. Pass `error` string prop |
620
- | `<AuthFormField>` | Standard input field. Supports all HTML input props + `label` |
621
- | `<AuthPasswordField>` | Password input with visibility toggle, optional strength indicator, and forgot password link |
622
- | `<AuthSubmitButton>` | Submit button with loading state. Pass `isLoading` and optional `loadingText` |
623
- | `<AuthDivider>` | Visual separator with customizable `text` (default: "or") |
624
- | `<AuthLink>` | Call-to-action link. Props: `text`, `linkText`, `href` |
625
- | `<AuthOAuthProviders>` | Smart grid of OAuth buttons. Props: `providers`, `onClick`, `loading` |
626
- | `<AuthBranding>` | "Powered by InsForge" branding. Optional `text` and `href` props |
627
-
628
- **`AuthPasswordField` Props:**
629
- - All standard input props (`value`, `onChange`, `required`, etc.)
630
- - `label`: Field label text
631
- - `id`: Input element ID
632
- - `showStrengthIndicator`: Show password strength indicator (default: `false`)
633
- - `forgotPasswordLink`: Object with `{ href: string, text?: string }`
634
-
635
- **`AuthOAuthProviders` Smart Layout:**
636
- - 1 provider: Full-width button with "Continue with Google"
637
- - 2 providers: Two columns with short text "Google", "GitHub"
638
- - 3+ providers: Three columns with icons only
639
- - Auto-centers incomplete last rows (e.g., 5, 8, 11 providers)
640
-
641
- **When to use primitives vs pre-built components:**
642
- - Use `<SignIn>` / `<SignUp>` for quick setup with minimal customization
643
- - Use Auth primitives when you need:
644
- - Custom form layouts
645
- - Additional fields (username, terms checkbox, etc.)
646
- - Integration with your own state management
647
- - Unique branding and styling
648
-
649
- ---
650
-
651
- #### `<UserButton>`
652
-
653
- User profile dropdown with sign-out functionality.
654
-
655
- ```tsx
656
- <UserButton
657
- afterSignOutUrl="/"
658
- showEmail={true}
659
- appearance={{
660
- button: { borderRadius: '50%' },
661
- dropdown: { minWidth: '250px' }
662
- }}
663
- />
664
- ```
665
-
666
- **Props:**
667
- - `afterSignOutUrl`: Redirect URL after sign-out (default: `/`)
668
- - `showEmail`: Show user email in dropdown (default: `true`)
669
- - `appearance`: Custom styles for button and dropdown
670
-
671
- ---
672
-
673
- #### `<SignedIn>`
674
-
675
- Renders children only when user is authenticated.
676
-
677
- ```tsx
678
- <SignedIn>
679
- <Dashboard />
680
- </SignedIn>
681
- ```
682
-
683
- ---
684
-
685
- #### `<SignedOut>`
267
+ **Available primitives**: `AuthContainer`, `AuthHeader`, `AuthErrorBanner`, `AuthFormField`, `AuthPasswordField`, `AuthSubmitButton`, `AuthDivider`, `AuthLink`, `AuthOAuthProviders`, `AuthBranding`.
686
268
 
687
- Renders children only when user is NOT authenticated.
269
+ ### Update Middleware
688
270
 
689
- ```tsx
690
- <SignedOut>
691
- <LandingPage />
692
- </SignedOut>
271
+ ```ts
272
+ // middleware.ts
273
+ export default InsforgeMiddleware({
274
+ baseUrl: process.env.INSFORGE_BASE_URL!,
275
+ publicRoutes: ['/sign-in', '/sign-up', '/auth/callback', '/'],
276
+ signInUrl: '/sign-in',
277
+ useBuiltInAuth: false, // Important!
278
+ });
693
279
  ```
694
280
 
695
281
  ---
696
282
 
697
- #### `<Protect>`
698
-
699
- Protects content and optionally redirects unauthenticated users.
700
-
701
- ```tsx
702
- <Protect
703
- fallback={<Loading />}
704
- redirectTo="/sign-in"
705
- condition={(user) => user.role === 'admin'}
706
- >
707
- <AdminPanel />
708
- </Protect>
709
- ```
710
-
711
- **Props:**
712
- - `fallback`: Component to show while loading or if unauthorized
713
- - `redirectTo`: URL to redirect to if not authorized (default: `/sign-in`)
714
- - `condition`: Optional function for custom authorization logic (e.g., role-based access)
715
-
716
- ---
283
+ ## API Reference
717
284
 
718
285
  ### Hooks
719
286
 
720
287
  #### `useAuth()`
721
288
 
722
- Access complete auth state and methods.
723
-
724
289
  ```tsx
725
290
  import { useAuth } from '@insforge/nextjs';
726
291
 
727
292
  function Component() {
728
- const {
729
- user,
730
- session,
731
- isLoaded,
732
- isSignedIn,
733
- signIn,
734
- signUp,
735
- signOut,
736
- updateUser
737
- } = useAuth();
738
-
739
- if (!isLoaded) return <div>Loading...</div>;
740
- if (!isSignedIn) return <div>Please sign in</div>;
741
-
742
- return <div>Hello {user.email}</div>;
293
+ const { user, isSignedIn, signOut } = useAuth();
294
+ return <div>{user?.email}</div>;
743
295
  }
744
296
  ```
745
297
 
746
- **Returns:**
747
- - `user`: Current user object or `null`
748
- - `session`: Current session object or `null`
749
- - `isLoaded`: Whether auth state has loaded
750
- - `isSignedIn`: Whether user is authenticated
751
- - `signIn(email, password)`: Sign in method
752
- - `signUp(email, password)`: Sign up method
753
- - `signOut()`: Sign out method
754
- - `updateUser(data)`: Update user data
755
-
756
- ---
298
+ **Returns**: `user`, `session`, `isLoaded`, `isSignedIn`, `signIn()`, `signUp()`, `signOut()`, `updateUser()`
757
299
 
758
300
  #### `useUser()`
759
301
 
760
- Access current user data.
761
-
762
302
  ```tsx
763
- import { useUser } from '@insforge/nextjs';
764
-
765
- function Component() {
766
- const { user, isLoaded } = useUser();
767
-
768
- if (!isLoaded) return <div>Loading...</div>;
769
- if (!user) return <div>Not signed in</div>;
770
-
771
- return <div>{user.email}</div>;
772
- }
303
+ const { user, isLoaded } = useUser();
773
304
  ```
774
305
 
775
- ---
776
-
777
306
  #### `useSession()`
778
307
 
779
- Access current session data.
780
-
781
308
  ```tsx
782
- import { useSession } from '@insforge/nextjs';
783
-
784
- function Component() {
785
- const { session, isLoaded, isSignedIn } = useSession();
786
-
787
- return <div>Signed in: {isSignedIn ? 'Yes' : 'No'}</div>;
788
- }
309
+ const { session, isSignedIn } = useSession();
789
310
  ```
790
311
 
791
- ---
792
-
793
- ### API Route Handlers
312
+ ### Components
794
313
 
795
- #### `createAuthRouteHandlers(config)`
314
+ #### `<SignIn>` / `<SignUp>`
796
315
 
797
- Creates Next.js App Router API handlers for authentication with HTTP-only cookie management.
316
+ Pre-built auth forms (only with `useBuiltInAuth={false}`):
798
317
 
799
318
  ```tsx
800
- // app/api/auth/route.ts
801
- import { createAuthRouteHandlers } from '@insforge/nextjs/api';
802
-
803
- const handlers = createAuthRouteHandlers({
804
- baseUrl: process.env.INSFORGE_BASE_URL!,
805
- cookieName: 'insforge_token',
806
- cookieMaxAge: 7 * 24 * 60 * 60, // 7 days
807
- });
808
-
809
- export const POST = handlers.POST;
810
- export const GET = handlers.GET;
811
- export const DELETE = handlers.DELETE;
812
- ```
813
-
814
- **Config:**
815
- - `baseUrl` (required): Your Insforge backend URL
816
- - `cookieName`: Cookie name for auth token (default: `insforge_token`)
817
- - `cookieMaxAge`: Cookie expiration in seconds (default: 7 days)
818
- - `secure`: Use secure cookies (auto-detected, `true` in production)
819
-
820
- **Supported Actions (POST):**
821
- - `sign-in`: Email/password sign-in with cookie creation
822
- - `sign-up`: Email/password sign-up with cookie creation
823
- - `sync-token`: Sync token from localStorage to cookie (for OAuth)
824
-
825
- **Example: Sync token after OAuth**
826
- ```typescript
827
- // In your OAuth callback page
828
- const response = await fetch('/api/auth', {
829
- method: 'POST',
830
- headers: { 'Content-Type': 'application/json' },
831
- body: JSON.stringify({
832
- action: 'sync-token',
833
- token: accessToken,
834
- }),
835
- });
319
+ <SignIn afterSignInUrl="/dashboard" />
320
+ <SignUp afterSignUpUrl="/onboarding" />
836
321
  ```
837
322
 
838
- **Endpoints:**
839
- - `POST /api/auth` - Sign in, sign up, or sync token
840
- - `GET /api/auth` - Check current session
841
- - `DELETE /api/auth` - Sign out and clear cookies
842
-
843
- ---
844
-
845
- ### Middleware
846
-
847
- #### `InsforgeMiddleware(config)`
323
+ **Props**: `afterSignInUrl`/`afterSignUpUrl`, `appearance`, `onSuccess`, `onError`
848
324
 
849
- Create Next.js middleware for route protection.
850
-
851
- ```ts
852
- import { InsforgeMiddleware } from '@insforge/nextjs/middleware';
325
+ #### `<UserButton>`
853
326
 
854
- export default InsforgeMiddleware({
855
- baseUrl: process.env.INSFORGE_BASE_URL!,
856
- publicRoutes: ['/sign-in', '/sign-up', '/', '/about'],
857
- signInUrl: '/sign-in',
858
- cookieName: 'insforge_token',
859
- });
327
+ ```tsx
328
+ <UserButton afterSignOutUrl="/" showEmail={true} />
860
329
  ```
861
330
 
862
- **Config:**
863
- - `baseUrl` (required): Your Insforge backend URL
864
- - `publicRoutes`: Array of routes that don't require auth (supports wildcards: `/blog/*`)
865
- - `signInUrl`: URL to redirect to when not authenticated (default: `/sign-in`)
866
- - `cookieName`: Cookie name for auth token (default: `insforge_token`)
867
-
868
- **Alternative exports:**
869
- - `withInsforgeAuth` - Same as `InsforgeMiddleware` with Insforge branding
870
- - `withAuth` - Deprecated alias for backward compatibility
331
+ #### `<SignedIn>` / `<SignedOut>` / `<Protect>`
871
332
 
872
- ---
873
-
874
- #### `getAuthUserId(headers)` and `getAuthToken(headers)`
333
+ ```tsx
334
+ <SignedIn><Dashboard /></SignedIn>
335
+ <SignedOut><LandingPage /></SignedOut>
336
+ <Protect condition={(user) => user.role === 'admin'}><AdminPanel /></Protect>
337
+ ```
875
338
 
876
- Get authenticated user ID or token in server components.
339
+ ### Server-Side Helpers
877
340
 
878
341
  ```tsx
342
+ // app/dashboard/page.tsx (Server Component)
879
343
  import { headers } from 'next/headers';
880
344
  import { getAuthUserId, getAuthToken } from '@insforge/nextjs/middleware';
881
345
 
882
- export default async function ServerComponent() {
346
+ export default async function Dashboard() {
883
347
  const userId = getAuthUserId(headers());
884
348
  const token = getAuthToken(headers());
885
-
886
- // Fetch user-specific data
887
- const data = await fetchUserData(userId, token);
888
-
349
+
350
+ // Fetch user data server-side
889
351
  return <div>User ID: {userId}</div>;
890
352
  }
891
- ```
892
353
 
893
- **Alternative exports:**
894
- - `getAuthUser` - Deprecated alias for `getAuthUserId` (backward compatibility)
354
+ ```
895
355
 
896
356
  ---
897
357
 
898
- ## Styling
899
-
900
- This package uses **standalone CSS** for styling - no Tailwind CSS dependency required! All components come pre-styled and ready to use.
358
+ ## Customization
901
359
 
902
- ### Setup
360
+ ### Styling (Custom Components Only)
903
361
 
904
- Simply import the CSS file once in your root layout:
362
+ When using `useBuiltInAuth={false}`, import styles:
905
363
 
906
364
  ```tsx
907
- // app/layout.tsx
908
365
  import '@insforge/nextjs/styles.css';
909
366
  ```
910
367
 
911
- That's it! No configuration needed.
912
-
913
- ### Typography
914
-
915
- The package includes the **Manrope variable font** for a modern, professional look. The font is automatically loaded and available as a CSS variable:
916
-
917
- ```css
918
- /* Use the font in your own CSS */
919
- .my-component {
920
- font-family: var(--font-manrope);
921
- }
922
- ```
923
-
924
- The font family includes fallbacks to system fonts for optimal loading:
925
- - `--font-manrope`: 'Manrope', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif
926
-
927
- ### Customization
928
-
929
- **Option 1: Text Customization** (Easiest)
930
-
931
- All text elements in the components can be customized via props:
932
-
933
- ```tsx
934
- <SignIn
935
- baseUrl="..."
936
- // Customize all text
937
- title="Welcome Back to MyApp"
938
- subtitle="We're happy to see you again"
939
- emailLabel="Your Email"
940
- emailPlaceholder="you@company.com"
941
- passwordLabel="Your Password"
942
- forgotPasswordText="Forgot it?"
943
- submitButtonText="Log In"
944
- loadingButtonText="Logging in..."
945
- signUpText="New user?"
946
- signUpLinkText="Create an account"
947
- signUpUrl="/register"
948
- dividerText="or continue with"
949
- />
950
-
951
- <SignUp
952
- baseUrl="..."
953
- // Customize signup text
954
- title="Join MyApp Today"
955
- subtitle="Create your account in seconds"
956
- submitButtonText="Create Account"
957
- signInText="Existing user?"
958
- signInLinkText="Log in here"
959
- />
960
- ```
961
-
962
- **Option 2: Style Customization**
963
-
964
- Override inline styles for specific components:
368
+ Override with `appearance` prop or CSS classes:
965
369
 
966
370
  ```tsx
967
- <SignIn
968
- appearance={{
969
- container: {
970
- background: 'linear-gradient(to right, #4f46e5, #7c3aed)'
971
- },
972
- form: {
973
- padding: '3rem',
974
- borderRadius: '16px'
975
- },
976
- button: {
977
- background: '#4f46e5',
978
- color: 'white'
979
- }
980
- }}
981
- />
371
+ <SignIn appearance={{ container: { background: '#f5f5f5' } }} />
982
372
  ```
983
373
 
984
- **Option 3: Override CSS Classes**
985
-
986
- All components use semantic CSS class names prefixed with `insforge-`:
987
-
988
374
  ```css
989
- /* In your global CSS file */
990
-
991
- /* Customize the primary button */
992
- .insforge-btn-primary {
993
- background: #8b5cf6;
994
- border-radius: 12px;
995
- }
996
-
997
- /* Customize form inputs */
998
- .insforge-input {
999
- border-color: #e0e0e0;
1000
- border-radius: 8px;
1001
- }
1002
-
1003
- /* Customize the auth card */
1004
- .insforge-auth-card {
1005
- box-shadow: 0 20px 50px rgba(0, 0, 0, 0.15);
1006
- }
1007
- ```
1008
-
1009
- **Available CSS Classes:**
1010
- - `.insforge-auth-container` - Main container
1011
- - `.insforge-auth-card` - Form card
1012
- - `.insforge-input` - Input fields
1013
- - `.insforge-btn-primary` - Primary buttons
1014
- - `.insforge-oauth-btn` - OAuth buttons
1015
- - `.insforge-user-button` - User menu button
1016
- - `.insforge-error-banner` - Error messages
1017
-
1018
- ---
1019
-
1020
- ## Advanced Usage
1021
-
1022
- ### Role-Based Access Control
1023
-
1024
- ```tsx
1025
- <Protect condition={(user) => user.role === 'admin'}>
1026
- <AdminDashboard />
1027
- </Protect>
375
+ .insforge-btn-primary { background: #8b5cf6; }
1028
376
  ```
1029
377
 
1030
- ### Custom Auth Callbacks
378
+ ### Auth Callbacks
1031
379
 
1032
380
  ```tsx
1033
- <InsforgeProvider
381
+ <InsforgeProvider
1034
382
  baseUrl={baseUrl}
1035
- onAuthChange={(user) => {
1036
- if (user) {
1037
- analytics.identify(user.id);
1038
- } else {
1039
- analytics.reset();
1040
- }
1041
- }}
1042
- >
1043
- {children}
1044
- </InsforgeProvider>
1045
- ```
1046
-
1047
- ### Server-Side User Data
1048
-
1049
- ```tsx
1050
- // app/dashboard/page.tsx
1051
- import { headers } from 'next/headers';
1052
- import { getAuthUserId } from '@insforge/nextjs/middleware';
1053
- import { createClient } from '@insforge/sdk';
1054
-
1055
- export default async function Dashboard() {
1056
- const userId = getAuthUserId(headers());
1057
-
1058
- const insforge = createClient({
1059
- baseUrl: process.env.INSFORGE_BASE_URL!
1060
- });
1061
-
1062
- const user = await insforge.auth.getUserById(userId);
1063
-
1064
- return <div>Welcome {user.email}</div>;
1065
- }
1066
- ```
1067
-
1068
- ---
1069
-
1070
- ## Environment Variables
1071
-
1072
- Create a `.env.local` file:
1073
-
1074
- ```env
1075
- # Public (accessible in browser)
1076
- NEXT_PUBLIC_INSFORGE_BASE_URL=https://your-backend.insforge.app
1077
-
1078
- # Server-only (middleware)
1079
- INSFORGE_BASE_URL=https://your-backend.insforge.app
383
+ onAuthChange={(user) => user && analytics.identify(user.id)}
384
+ />
1080
385
  ```
1081
386
 
1082
387
  ---
1083
388
 
1084
389
  ## Why @insforge/nextjs?
1085
390
 
1086
- ### For Developers
1087
- - **Faster Development**: Drop-in components save hours of auth implementation
1088
- - **Best Practices**: Built-in security, session management, and error handling
1089
- - **Flexible**: Fully customizable while providing sensible defaults
1090
- - **Explicit Configuration**: OAuth providers passed as props - no hidden API calls or magic
1091
-
1092
- ### For AI Agents
1093
- - **Reduced Token Usage**: Pre-built components mean agents don't rebuild UI each time
1094
- - **Consistent UX**: Standard patterns across projects
1095
- - **Less Context**: Import and use instead of implementing from scratch
1096
- - **Discoverable**: Query `/api/metadata/auth` via MCP to get OAuth config, then generate code with correct `providers` prop
1097
-
1098
- ### AI Agent Workflow with Insforge MCP
1099
-
1100
- When building Next.js apps with authentication:
1101
-
1102
- 1. **Connect to Insforge MCP** - Access the user's Insforge backend
1103
- 2. **Generate code** - Use `<SignIn>` and `<SignUp>` components
1104
- 3. **Automatic configuration** - OAuth providers are detected automatically from backend
1105
-
1106
- **Agent generates:**
1107
- ```tsx
1108
- // Step 1: Wrap app with InsforgeProvider
1109
- <InsforgeProvider baseUrl={process.env.NEXT_PUBLIC_INSFORGE_BASE_URL!}>
1110
- {children}
1111
- </InsforgeProvider>
1112
-
1113
- // Step 2: Add sign-in page (OAuth auto-detected)
1114
- <SignIn afterSignInUrl="/dashboard" />
1115
- ```
391
+ **For Developers**: 5-minute auth setup instead of hours. Production-ready security and session management included.
1116
392
 
1117
- **Benefits:**
1118
- - No need to query OAuth configuration manually
1119
- - Components automatically render correct OAuth buttons
1120
- - Backend configuration is the single source of truth
393
+ **For AI Agents**: Minimal code generation (5 files vs 20+ for custom auth). Reduced token usage. Consistent patterns across projects.
1121
394
 
1122
395
  ---
1123
396
 
1124
397
  ## TypeScript
1125
398
 
1126
- Full TypeScript support with exported types:
1127
-
1128
399
  ```tsx
1129
- import type {
1130
- InsforgeUser,
1131
- InsforgeSession,
1132
- AuthContextValue,
1133
- SignInProps,
1134
- ProtectProps
1135
- } from '@insforge/nextjs';
400
+ import type { InsforgeUser, InsforgeSession, AuthContextValue } from '@insforge/nextjs';
1136
401
  ```
1137
402
 
1138
403
  ---
1139
404
 
1140
- ## Examples
1141
-
1142
- Check out the `/examples` directory for:
1143
- - Basic Next.js App Router setup
1144
- - Role-based access control
1145
- - Custom styling
1146
- - Server-side data fetching
405
+ ## Support
1147
406
 
1148
- ---
407
+ - **Docs**: https://docs.insforge.dev/introduction
408
+ - **Issues**: https://github.com/InsForge/InsForge/issues
409
+ - **Discord**: https://discord.com/invite/DvBtaEc9Jz
1149
410
 
1150
411
  ## License
1151
412
 
1152
413
  MIT
1153
-
1154
- ---
1155
-
1156
- ## Support
1157
-
1158
- - Documentation: https://docs.insforge.dev/introduction
1159
- - Issues: https://github.com/InsForge/InsForge/issues
1160
- - Discord: https://discord.com/invite/DvBtaEc9Jz
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@insforge/nextjs",
3
- "version": "0.6.5",
3
+ "version": "0.6.6",
4
4
  "description": "Pre-built authentication UI components for Next.js with Insforge backend - zero configuration required",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",