@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.
- package/README.md +151 -898
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,84 +1,35 @@
|
|
|
1
1
|
# @insforge/nextjs
|
|
2
2
|
|
|
3
|
-
|
|
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
|
|
8
|
-
- **
|
|
9
|
-
- **
|
|
10
|
-
- **
|
|
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
|
-
- **
|
|
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.
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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
|
-
|
|
239
|
-
|
|
105
|
+
const destination = sessionStorage.getItem('auth_destination') || '/dashboard';
|
|
106
|
+
sessionStorage.removeItem('auth_destination');
|
|
240
107
|
|
|
241
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
**
|
|
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
|
-
|
|
136
|
+
### 4. Add middleware (protects routes)
|
|
307
137
|
|
|
308
|
-
|
|
138
|
+
```ts
|
|
139
|
+
// middleware.ts
|
|
140
|
+
import { InsforgeMiddleware } from '@insforge/nextjs/middleware';
|
|
309
141
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
142
|
+
export default InsforgeMiddleware({
|
|
143
|
+
baseUrl: process.env.INSFORGE_BASE_URL!,
|
|
144
|
+
publicRoutes: ['/auth/callback', '/'],
|
|
145
|
+
cookieName: 'insforge_token',
|
|
146
|
+
});
|
|
313
147
|
|
|
314
|
-
export
|
|
315
|
-
|
|
316
|
-
|
|
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
|
-
###
|
|
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
|
-
<
|
|
333
|
-
<
|
|
334
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
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.
|
|
184
|
+
2. Redirects to: https://backend.insforge.app/auth/signin?redirect=yourapp.com/auth/callback
|
|
424
185
|
↓
|
|
425
|
-
3.
|
|
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.
|
|
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.
|
|
434
|
-
└─ Reads cookie to protect routes
|
|
190
|
+
5. Callback stores token (localStorage + cookie) → User sees /dashboard
|
|
435
191
|
```
|
|
436
192
|
|
|
437
|
-
**Why
|
|
438
|
-
- **
|
|
439
|
-
- **
|
|
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
|
-
|
|
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
|
-
|
|
199
|
+
## Local Development
|
|
460
200
|
|
|
461
|
-
|
|
201
|
+
Backend typically runs on different port during local dev:
|
|
462
202
|
|
|
463
|
-
```
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
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
|
-
|
|
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
|
-
|
|
214
|
+
## Advanced: Custom Components
|
|
481
215
|
|
|
482
|
-
|
|
216
|
+
Need custom branding? Disable built-in auth:
|
|
483
217
|
|
|
484
218
|
```tsx
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
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
|
-
|
|
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
|
|
235
|
+
### Pre-built Components
|
|
509
236
|
|
|
510
237
|
```tsx
|
|
511
|
-
|
|
512
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
248
|
+
### Build from Primitives
|
|
530
249
|
|
|
531
|
-
For
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
269
|
+
### Update Middleware
|
|
688
270
|
|
|
689
|
-
```
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
####
|
|
314
|
+
#### `<SignIn>` / `<SignUp>`
|
|
796
315
|
|
|
797
|
-
|
|
316
|
+
Pre-built auth forms (only with `useBuiltInAuth={false}`):
|
|
798
317
|
|
|
799
318
|
```tsx
|
|
800
|
-
|
|
801
|
-
|
|
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
|
-
**
|
|
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
|
-
|
|
850
|
-
|
|
851
|
-
```ts
|
|
852
|
-
import { InsforgeMiddleware } from '@insforge/nextjs/middleware';
|
|
325
|
+
#### `<UserButton>`
|
|
853
326
|
|
|
854
|
-
|
|
855
|
-
|
|
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
|
-
|
|
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
|
-
|
|
333
|
+
```tsx
|
|
334
|
+
<SignedIn><Dashboard /></SignedIn>
|
|
335
|
+
<SignedOut><LandingPage /></SignedOut>
|
|
336
|
+
<Protect condition={(user) => user.role === 'admin'}><AdminPanel /></Protect>
|
|
337
|
+
```
|
|
875
338
|
|
|
876
|
-
|
|
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
|
|
346
|
+
export default async function Dashboard() {
|
|
883
347
|
const userId = getAuthUserId(headers());
|
|
884
348
|
const token = getAuthToken(headers());
|
|
885
|
-
|
|
886
|
-
// Fetch user
|
|
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
|
-
|
|
894
|
-
- `getAuthUser` - Deprecated alias for `getAuthUserId` (backward compatibility)
|
|
354
|
+
```
|
|
895
355
|
|
|
896
356
|
---
|
|
897
357
|
|
|
898
|
-
##
|
|
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
|
-
###
|
|
360
|
+
### Styling (Custom Components Only)
|
|
903
361
|
|
|
904
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
378
|
+
### Auth Callbacks
|
|
1031
379
|
|
|
1032
380
|
```tsx
|
|
1033
|
-
<InsforgeProvider
|
|
381
|
+
<InsforgeProvider
|
|
1034
382
|
baseUrl={baseUrl}
|
|
1035
|
-
onAuthChange={(user) =>
|
|
1036
|
-
|
|
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
|
-
|
|
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
|
-
**
|
|
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
|
-
##
|
|
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