@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 +497 -96
- package/dist/api.d.mts +12 -1
- package/dist/api.d.ts +12 -1
- package/dist/api.js +59 -3
- package/dist/api.js.map +1 -1
- package/dist/api.mjs +57 -2
- package/dist/api.mjs.map +1 -1
- package/dist/index.css +170 -26
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +147 -35
- package/dist/index.d.ts +147 -35
- package/dist/index.js +937 -519
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +903 -500
- package/dist/index.mjs.map +1 -1
- package/dist/middleware.d.mts +10 -4
- package/dist/middleware.d.ts +10 -4
- package/dist/middleware.js +55 -12
- package/dist/middleware.js.map +1 -1
- package/dist/middleware.mjs +51 -11
- package/dist/middleware.mjs.map +1 -1
- package/package.json +4 -5
- package/src/styles.css +737 -551
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**:
|
|
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
|
|
21
|
+
npm install @insforge/nextjs
|
|
19
22
|
# or
|
|
20
|
-
yarn add @insforge/nextjs
|
|
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.
|
|
29
|
+
### 1. Wrap your app with InsforgeProvider
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
```tsx
|
|
32
|
+
// app/layout.tsx
|
|
33
|
+
import { InsforgeProvider } from '@insforge/nextjs';
|
|
34
|
+
import '@insforge/nextjs/styles.css';
|
|
31
35
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
**
|
|
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
|
-
|
|
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 {
|
|
85
|
+
import { InsforgeProvider } from '@insforge/nextjs';
|
|
55
86
|
|
|
56
87
|
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
57
|
-
|
|
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
|
-
<
|
|
138
|
+
<InsforgeProvider
|
|
139
|
+
baseUrl={process.env.NEXT_PUBLIC_INSFORGE_BASE_URL!}
|
|
140
|
+
useBuiltInAuth={false} // Use custom components instead
|
|
141
|
+
>
|
|
63
142
|
{children}
|
|
64
|
-
</
|
|
143
|
+
</InsforgeProvider>
|
|
65
144
|
</body>
|
|
66
145
|
</html>
|
|
67
146
|
);
|
|
68
147
|
}
|
|
69
148
|
```
|
|
70
149
|
|
|
71
|
-
|
|
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
|
-
//
|
|
75
|
-
import '@insforge/nextjs/
|
|
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
|
-
**
|
|
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
|
|
175
|
+
### 3. Add authentication callback page (Required for Next.js SSR)
|
|
81
176
|
|
|
82
|
-
|
|
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 (
|
|
207
|
+
// 1. Store token in localStorage (for SDK client-side operations)
|
|
107
208
|
localStorage.setItem('insforge-auth-token', accessToken);
|
|
108
209
|
|
|
109
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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={
|
|
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
|
-
|
|
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
|
-
**
|
|
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 {
|
|
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
|
|
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|
|
|
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
|
-
#### `<
|
|
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
|
-
<
|
|
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
|
-
</
|
|
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
|
-
#### `
|
|
847
|
+
#### `InsforgeMiddleware(config)`
|
|
448
848
|
|
|
449
849
|
Create Next.js middleware for route protection.
|
|
450
850
|
|
|
451
851
|
```ts
|
|
452
|
-
import {
|
|
852
|
+
import { InsforgeMiddleware } from '@insforge/nextjs/middleware';
|
|
453
853
|
|
|
454
|
-
export default
|
|
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
|
-
#### `
|
|
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 {
|
|
880
|
+
import { getAuthUserId, getAuthToken } from '@insforge/nextjs/middleware';
|
|
477
881
|
|
|
478
882
|
export default async function ServerComponent() {
|
|
479
|
-
const userId =
|
|
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
|
-
<
|
|
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
|
-
</
|
|
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 {
|
|
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 =
|
|
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. **
|
|
697
|
-
3. **
|
|
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
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
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.
|
|
758
|
-
- Issues: https://github.com/
|
|
759
|
-
- Discord: https://discord.
|
|
1158
|
+
- Documentation: https://docs.insforge.dev/introduction
|
|
1159
|
+
- Issues: https://github.com/InsForge/InsForge/issues
|
|
1160
|
+
- Discord: https://discord.com/invite/DvBtaEc9Jz
|