@insforge/nextjs 0.4.0 → 0.6.5

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
@@ -4,115 +4,249 @@ Pre-built authentication UI components for Next.js applications using Insforge b
4
4
 
5
5
  ## Features
6
6
 
7
+ - **Built-in Authentication (v0.6.0+)**: Zero-config authentication using backend-hosted pages
7
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
8
10
  - **React Hooks**: Easy access to auth state with `useAuth()`, `useUser()`, `useSession()`
9
11
  - **Control Components**: `<SignedIn>`, `<SignedOut>`, `<Protect>` for conditional rendering
10
12
  - **Next.js Middleware**: Server-side route protection
11
- - **OAuth Support**: Built-in Google and GitHub authentication
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
12
15
  - **TypeScript**: Full type safety out of the box
13
- - **Customizable**: Override styles with the `appearance` prop
16
+ - **Customizable**: Override styles with the `appearance` prop or build from primitives
14
17
 
15
18
  ## Installation
16
19
 
17
20
  ```bash
18
- npm install @insforge/nextjs lucide-react
21
+ npm install @insforge/nextjs
19
22
  # or
20
- yarn add @insforge/nextjs lucide-react
23
+ yarn add @insforge/nextjs
21
24
  ```
22
25
 
23
- **Required peer dependencies:**
24
- - `lucide-react` - Icon library (used for icons in components)
25
26
 
26
27
  ## Quick Start
27
28
 
28
- ### 1. Check Your Backend OAuth Configuration
29
+ ### 1. Wrap your app with InsforgeProvider
29
30
 
30
- First, query your Insforge backend to see what OAuth providers are configured:
31
+ ```tsx
32
+ // app/layout.tsx
33
+ import { InsforgeProvider } from '@insforge/nextjs';
34
+ import '@insforge/nextjs/styles.css';
31
35
 
32
- ```bash
33
- # Using curl (requires admin authentication)
34
- curl -H "Authorization: Bearer YOUR_ADMIN_TOKEN" \
35
- https://your-backend.insforge.app/api/metadata/auth
36
- ```
37
-
38
- **Response example:**
39
- ```json
40
- {
41
- "oauths": [
42
- { "provider": "github", "clientId": "...", ... },
43
- { "provider": "google", "clientId": "...", ... }
44
- ]
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
+ );
45
48
  }
46
49
  ```
47
50
 
48
- **For AI Agents:** Use the Insforge MCP tool `get-backend-metadata` to automatically retrieve this configuration.
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
49
72
 
50
- ### 2. Wrap your app with AuthProvider
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
+ ```
51
82
 
52
83
  ```tsx
53
84
  // app/layout.tsx
54
- import { AuthProvider } from '@insforge/nextjs';
85
+ import { InsforgeProvider } from '@insforge/nextjs';
55
86
 
56
87
  export default function RootLayout({ children }: { children: React.ReactNode }) {
57
- const baseUrl = process.env.NEXT_PUBLIC_INSFORGE_BASE_URL!;
88
+ return (
89
+ <html lang="en">
90
+ <body>
91
+ <InsforgeProvider baseUrl={process.env.NEXT_PUBLIC_INSFORGE_BASE_URL!}>
92
+ {children}
93
+ </InsforgeProvider>
94
+ </body>
95
+ </html>
96
+ );
97
+ }
98
+ ```
99
+
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
+ ```
58
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 }) {
59
135
  return (
60
136
  <html lang="en">
61
137
  <body>
62
- <AuthProvider baseUrl={baseUrl}>
138
+ <InsforgeProvider
139
+ baseUrl={process.env.NEXT_PUBLIC_INSFORGE_BASE_URL!}
140
+ useBuiltInAuth={false} // Use custom components instead
141
+ >
63
142
  {children}
64
- </AuthProvider>
143
+ </InsforgeProvider>
65
144
  </body>
66
145
  </html>
67
146
  );
68
147
  }
69
148
  ```
70
149
 
71
- **Note:** Import the component styles:
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.
72
153
 
73
154
  ```tsx
74
- // In your layout.tsx or _app.tsx
75
- import '@insforge/nextjs/styles.css';
155
+ // app/api/auth/route.ts
156
+ import { createAuthRouteHandlers } from '@insforge/nextjs/api';
157
+
158
+ const handlers = createAuthRouteHandlers({
159
+ baseUrl: process.env.INSFORGE_BASE_URL || process.env.NEXT_PUBLIC_INSFORGE_BASE_URL!,
160
+ cookieName: 'insforge_token',
161
+ });
162
+
163
+ export const POST = handlers.POST;
164
+ export const GET = handlers.GET;
165
+ export const DELETE = handlers.DELETE;
76
166
  ```
77
167
 
78
- **That's it!** No Tailwind configuration needed. The package now uses standalone CSS.
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
79
174
 
80
- ### 3. Add OAuth callback page (Required for OAuth)
175
+ ### 3. Add authentication callback page (Required for Next.js SSR)
81
176
 
82
- If you're using OAuth providers, you **must** create a callback page:
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:
83
178
 
84
179
  ```tsx
85
180
  // app/auth/callback/page.tsx
86
181
  'use client';
87
182
 
88
- import { useEffect, Suspense } from 'react';
183
+ import { useEffect, useRef, Suspense } from 'react';
89
184
  import { useRouter, useSearchParams } from 'next/navigation';
90
185
 
91
186
  function CallbackContent() {
92
187
  const router = useRouter();
93
188
  const searchParams = useSearchParams();
189
+ const isProcessingRef = useRef(false);
94
190
 
95
191
  useEffect(() => {
96
- const processOAuthCallback = () => {
192
+ const processOAuthCallback = async () => {
193
+ // Prevent double-processing in React Strict Mode
194
+ if (isProcessingRef.current) return;
195
+ isProcessingRef.current = true;
196
+
97
197
  const accessToken = searchParams.get('access_token');
98
198
  const error = searchParams.get('error');
99
199
 
100
200
  if (error) {
201
+ console.error('OAuth error:', error);
101
202
  router.push('/sign-in?error=' + encodeURIComponent(error));
102
203
  return;
103
204
  }
104
205
 
105
206
  if (accessToken) {
106
- // Store token in localStorage (where SDK expects it)
207
+ // 1. Store token in localStorage (for SDK client-side operations)
107
208
  localStorage.setItem('insforge-auth-token', accessToken);
108
209
 
109
- // Get final destination
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
110
235
  const finalDestination = sessionStorage.getItem('oauth_final_destination') || '/dashboard';
111
236
  sessionStorage.removeItem('oauth_final_destination');
112
237
 
113
- router.push(finalDestination);
238
+ // 4. Clean up URL to remove OAuth params before redirect
239
+ window.history.replaceState({}, '', '/auth/callback');
240
+
241
+ // 5. Small delay to ensure cookie is set before redirect
242
+ setTimeout(() => {
243
+ router.push(finalDestination);
244
+ }, 100);
114
245
  } else {
115
- router.push('/sign-in?error=no_token');
246
+ if (searchParams.toString()) {
247
+ console.error('No token received from OAuth');
248
+ router.push('/sign-in?error=no_token');
249
+ }
116
250
  }
117
251
  };
118
252
 
@@ -131,14 +265,28 @@ function CallbackContent() {
131
265
 
132
266
  export default function OAuthCallbackPage() {
133
267
  return (
134
- <Suspense fallback={<div>Loading...</div>}>
268
+ <Suspense fallback={
269
+ <div className="flex items-center justify-center min-h-screen">
270
+ <div>Loading...</div>
271
+ </div>
272
+ }>
135
273
  <CallbackContent />
136
274
  </Suspense>
137
275
  );
138
276
  }
139
277
  ```
140
278
 
141
- ### 4. Add sign-in page with OAuth providers
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
142
290
 
143
291
  ```tsx
144
292
  // app/sign-in/page.tsx
@@ -147,8 +295,6 @@ import { SignIn } from '@insforge/nextjs';
147
295
  export default function SignInPage() {
148
296
  return (
149
297
  <SignIn
150
- baseUrl={process.env.NEXT_PUBLIC_INSFORGE_BASE_URL!}
151
- providers={['github', 'google']} // Based on your backend config
152
298
  afterSignInUrl="/dashboard"
153
299
  title="Welcome to MyApp"
154
300
  subtitle="Sign in to continue"
@@ -157,7 +303,7 @@ export default function SignInPage() {
157
303
  }
158
304
  ```
159
305
 
160
- **Note:** The `providers` prop should match what you've configured in your Insforge backend. OAuth buttons will only render for providers specified in this array.
306
+ **OAuth Providers:** The package automatically detects which OAuth providers are configured on your backend. You don't need to specify them manually!
161
307
 
162
308
  ### 5. Add sign-up page
163
309
 
@@ -168,8 +314,6 @@ import { SignUp } from '@insforge/nextjs';
168
314
  export default function SignUpPage() {
169
315
  return (
170
316
  <SignUp
171
- baseUrl={process.env.NEXT_PUBLIC_INSFORGE_BASE_URL!}
172
- providers={['github', 'google']} // Based on your backend config
173
317
  afterSignUpUrl="/dashboard"
174
318
  />
175
319
  );
@@ -210,41 +354,127 @@ export default function Home() {
210
354
  ### 7. Protect routes with middleware
211
355
 
212
356
  ```ts
213
- // middleware.ts
214
- import { withAuth } from '@insforge/nextjs/middleware';
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';
215
374
 
216
- export default withAuth({
375
+ export default InsforgeMiddleware({
217
376
  baseUrl: process.env.INSFORGE_BASE_URL!,
218
- publicRoutes: ['/sign-in', '/sign-up', '/'],
377
+ publicRoutes: ['/sign-in', '/sign-up', '/', '/auth/callback'],
219
378
  signInUrl: '/sign-in',
379
+ cookieName: 'insforge_token',
380
+ useBuiltInAuth: false, // Use custom components
220
381
  });
221
382
 
222
383
  export const config = {
223
- matcher: ['/((?!_next|api|.*\\..*).*)'],
384
+ matcher: ['/((?!api|_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)'],
224
385
  };
225
386
  ```
226
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
397
+
398
+ ---
399
+
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:**
415
+
416
+ ```
417
+ ┌─────────────────────────────────────────────────────────────┐
418
+ │ Authentication Flow (Email/Password & OAuth) │
419
+ └─────────────────────────────────────────────────────────────┘
420
+
421
+ 1. User signs in (email/password OR OAuth)
422
+
423
+ 2. Backend redirects to: /auth/callback?access_token=xxx
424
+
425
+ 3. Callback Page (Client-side)
426
+ ├─ Stores token in localStorage (for SDK)
427
+ └─ Calls /api/auth with "sync-token" action
428
+
429
+ 4. API Route (Server-side)
430
+ ├─ Validates token with backend
431
+ └─ Sets HTTP-only cookie
432
+
433
+ 5. Middleware (Server-side)
434
+ └─ Reads cookie to protect routes
435
+ ```
436
+
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 |
450
+
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
+
227
455
  ## API Reference
228
456
 
229
457
  ### Components
230
458
 
231
- #### `<AuthProvider>`
459
+ #### `<InsforgeProvider>`
232
460
 
233
- Wraps your application and provides auth context.
461
+ Wraps your application and provides auth context. Automatically fetches OAuth provider configuration from your backend.
234
462
 
235
463
  ```tsx
236
- <AuthProvider
464
+ <InsforgeProvider
237
465
  baseUrl="https://your-backend.insforge.app"
238
466
  onAuthChange={(user) => console.log('Auth changed:', user)}
239
467
  >
240
468
  {children}
241
- </AuthProvider>
469
+ </InsforgeProvider>
242
470
  ```
243
471
 
244
472
  **Props:**
245
473
  - `baseUrl` (required): Your Insforge backend URL
246
474
  - `onAuthChange`: Optional callback when auth state changes
247
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.
477
+
248
478
  ---
249
479
 
250
480
  #### `<SignIn>`
@@ -253,8 +483,6 @@ Pre-built sign-in form with email/password and OAuth.
253
483
 
254
484
  ```tsx
255
485
  <SignIn
256
- baseUrl="https://your-backend.insforge.app"
257
- providers={['github', 'google']}
258
486
  afterSignInUrl="/dashboard"
259
487
  appearance={{
260
488
  container: { background: '#f5f5f5' },
@@ -266,13 +494,13 @@ Pre-built sign-in form with email/password and OAuth.
266
494
  ```
267
495
 
268
496
  **Props:**
269
- - `baseUrl` (required): Your Insforge backend URL
270
- - `providers`: Array of OAuth providers to display (e.g., `['github', 'google']`). Omit or pass empty array for email/password only
271
497
  - `afterSignInUrl`: Redirect URL after successful sign-in (default: `/`)
272
498
  - `appearance`: Custom styles for container, form, and button
273
499
  - `onSuccess`: Callback on successful sign-in
274
500
  - `onError`: Callback on error
275
501
 
502
+ **Note:** OAuth providers are automatically detected from your backend configuration via `InsforgeProvider`. No need to manually specify them!
503
+
276
504
  ---
277
505
 
278
506
  #### `<SignUp>`
@@ -281,8 +509,6 @@ Pre-built sign-up form with email/password and OAuth.
281
509
 
282
510
  ```tsx
283
511
  <SignUp
284
- baseUrl="https://your-backend.insforge.app"
285
- providers={['github', 'google']}
286
512
  afterSignUpUrl="/onboarding"
287
513
  appearance={{
288
514
  container: { background: '#f5f5f5' }
@@ -291,13 +517,135 @@ Pre-built sign-up form with email/password and OAuth.
291
517
  ```
292
518
 
293
519
  **Props:**
294
- - `baseUrl` (required): Your Insforge backend URL
295
- - `providers`: Array of OAuth providers to display (e.g., `['github', 'google']`). Omit or pass empty array for email/password only
296
520
  - `afterSignUpUrl`: Redirect URL after successful sign-up (default: `/`)
297
521
  - `appearance`: Custom styles for container, form, and button
298
522
  - `onSuccess`: Callback on successful sign-up
299
523
  - `onError`: Callback on error
300
524
 
525
+ **Note:** OAuth providers are automatically detected from your backend configuration via `InsforgeProvider`. No need to manually specify them!
526
+
527
+ ---
528
+
529
+ ### Auth Component Primitives (v0.5.0+)
530
+
531
+ For advanced customization, use the low-level Auth primitives to build your own authentication UI:
532
+
533
+ ```tsx
534
+ import {
535
+ AuthContainer,
536
+ AuthHeader,
537
+ AuthErrorBanner,
538
+ AuthFormField,
539
+ AuthPasswordField,
540
+ AuthSubmitButton,
541
+ AuthDivider,
542
+ AuthLink,
543
+ AuthOAuthProviders,
544
+ AuthBranding,
545
+ } from '@insforge/nextjs';
546
+ import '@insforge/nextjs/styles.css';
547
+
548
+ 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
+ );
610
+ }
611
+ ```
612
+
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
+
301
649
  ---
302
650
 
303
651
  #### `<UserButton>`
@@ -442,16 +790,68 @@ function Component() {
442
790
 
443
791
  ---
444
792
 
793
+ ### API Route Handlers
794
+
795
+ #### `createAuthRouteHandlers(config)`
796
+
797
+ Creates Next.js App Router API handlers for authentication with HTTP-only cookie management.
798
+
799
+ ```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
+ });
836
+ ```
837
+
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
+
445
845
  ### Middleware
446
846
 
447
- #### `withAuth(config)`
847
+ #### `InsforgeMiddleware(config)`
448
848
 
449
849
  Create Next.js middleware for route protection.
450
850
 
451
851
  ```ts
452
- import { withAuth } from '@insforge/nextjs/middleware';
852
+ import { InsforgeMiddleware } from '@insforge/nextjs/middleware';
453
853
 
454
- export default withAuth({
854
+ export default InsforgeMiddleware({
455
855
  baseUrl: process.env.INSFORGE_BASE_URL!,
456
856
  publicRoutes: ['/sign-in', '/sign-up', '/', '/about'],
457
857
  signInUrl: '/sign-in',
@@ -465,18 +865,22 @@ export default withAuth({
465
865
  - `signInUrl`: URL to redirect to when not authenticated (default: `/sign-in`)
466
866
  - `cookieName`: Cookie name for auth token (default: `insforge_token`)
467
867
 
868
+ **Alternative exports:**
869
+ - `withInsforgeAuth` - Same as `InsforgeMiddleware` with Insforge branding
870
+ - `withAuth` - Deprecated alias for backward compatibility
871
+
468
872
  ---
469
873
 
470
- #### `getAuthUser(headers)` and `getAuthToken(headers)`
874
+ #### `getAuthUserId(headers)` and `getAuthToken(headers)`
471
875
 
472
876
  Get authenticated user ID or token in server components.
473
877
 
474
878
  ```tsx
475
879
  import { headers } from 'next/headers';
476
- import { getAuthUser, getAuthToken } from '@insforge/nextjs/middleware';
880
+ import { getAuthUserId, getAuthToken } from '@insforge/nextjs/middleware';
477
881
 
478
882
  export default async function ServerComponent() {
479
- const userId = getAuthUser(headers());
883
+ const userId = getAuthUserId(headers());
480
884
  const token = getAuthToken(headers());
481
885
 
482
886
  // Fetch user-specific data
@@ -486,6 +890,9 @@ export default async function ServerComponent() {
486
890
  }
487
891
  ```
488
892
 
893
+ **Alternative exports:**
894
+ - `getAuthUser` - Deprecated alias for `getAuthUserId` (backward compatibility)
895
+
489
896
  ---
490
897
 
491
898
  ## Styling
@@ -623,7 +1030,7 @@ All components use semantic CSS class names prefixed with `insforge-`:
623
1030
  ### Custom Auth Callbacks
624
1031
 
625
1032
  ```tsx
626
- <AuthProvider
1033
+ <InsforgeProvider
627
1034
  baseUrl={baseUrl}
628
1035
  onAuthChange={(user) => {
629
1036
  if (user) {
@@ -634,7 +1041,7 @@ All components use semantic CSS class names prefixed with `insforge-`:
634
1041
  }}
635
1042
  >
636
1043
  {children}
637
- </AuthProvider>
1044
+ </InsforgeProvider>
638
1045
  ```
639
1046
 
640
1047
  ### Server-Side User Data
@@ -642,11 +1049,11 @@ All components use semantic CSS class names prefixed with `insforge-`:
642
1049
  ```tsx
643
1050
  // app/dashboard/page.tsx
644
1051
  import { headers } from 'next/headers';
645
- import { getAuthUser } from '@insforge/nextjs/middleware';
1052
+ import { getAuthUserId } from '@insforge/nextjs/middleware';
646
1053
  import { createClient } from '@insforge/sdk';
647
1054
 
648
1055
  export default async function Dashboard() {
649
- const userId = getAuthUser(headers());
1056
+ const userId = getAuthUserId(headers());
650
1057
 
651
1058
  const insforge = createClient({
652
1059
  baseUrl: process.env.INSFORGE_BASE_URL!
@@ -693,31 +1100,25 @@ INSFORGE_BASE_URL=https://your-backend.insforge.app
693
1100
  When building Next.js apps with authentication:
694
1101
 
695
1102
  1. **Connect to Insforge MCP** - Access the user's Insforge backend
696
- 2. **Query metadata** - Call `get-backend-metadata` to get OAuth configuration
697
- 3. **Generate code** - Write auth pages with explicit `providers={['github', 'google']}` based on backend config
698
- 4. **User sees OAuth buttons** - Components render buttons for configured providers
699
-
700
- **Example MCP Response:**
701
- ```json
702
- {
703
- "auth": {
704
- "oauths": [
705
- { "provider": "github", "clientId": "..." },
706
- { "provider": "google", "clientId": "..." }
707
- ]
708
- }
709
- }
710
- ```
1103
+ 2. **Generate code** - Use `<SignIn>` and `<SignUp>` components
1104
+ 3. **Automatic configuration** - OAuth providers are detected automatically from backend
711
1105
 
712
1106
  **Agent generates:**
713
1107
  ```tsx
714
- <SignIn
715
- baseUrl={process.env.NEXT_PUBLIC_INSFORGE_BASE_URL!}
716
- providers={['github', 'google']} // From metadata
717
- afterSignInUrl="/dashboard"
718
- />
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" />
719
1115
  ```
720
1116
 
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
1121
+
721
1122
  ---
722
1123
 
723
1124
  ## TypeScript
@@ -754,6 +1155,6 @@ MIT
754
1155
 
755
1156
  ## Support
756
1157
 
757
- - Documentation: https://docs.insforge.app
758
- - Issues: https://github.com/insforge/nextjs/issues
759
- - Discord: https://discord.gg/insforge
1158
+ - Documentation: https://docs.insforge.dev/introduction
1159
+ - Issues: https://github.com/InsForge/InsForge/issues
1160
+ - Discord: https://discord.com/invite/DvBtaEc9Jz