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