@insforge/nextjs 0.4.0 → 0.5.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 +387 -100
- package/dist/api.js +1 -1
- package/dist/api.js.map +1 -1
- package/dist/api.mjs +1 -1
- 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 +143 -35
- package/dist/index.d.ts +143 -35
- package/dist/index.js +895 -517
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +861 -498
- package/dist/index.mjs.map +1 -1
- package/dist/middleware.d.mts +4 -4
- package/dist/middleware.d.ts +4 -4
- package/dist/middleware.js +7 -7
- package/dist/middleware.js.map +1 -1
- package/dist/middleware.mjs +5 -5
- package/dist/middleware.mjs.map +1 -1
- package/package.json +1 -2
- package/src/styles.css +737 -551
package/README.md
CHANGED
|
@@ -5,53 +5,32 @@ Pre-built authentication UI components for Next.js applications using Insforge b
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- **Drop-in UI Components**: Pre-styled SignIn, SignUp, and UserButton components
|
|
8
|
+
- **Auth Primitives (v0.5.0+)**: 10 low-level components for building custom auth UIs
|
|
8
9
|
- **React Hooks**: Easy access to auth state with `useAuth()`, `useUser()`, `useSession()`
|
|
9
10
|
- **Control Components**: `<SignedIn>`, `<SignedOut>`, `<Protect>` for conditional rendering
|
|
10
11
|
- **Next.js Middleware**: Server-side route protection
|
|
11
|
-
- **OAuth Support**:
|
|
12
|
+
- **OAuth Support**: 11 providers (Google, GitHub, Discord, Facebook, LinkedIn, Microsoft, Apple, X, Instagram, TikTok, Spotify)
|
|
13
|
+
- **Smart OAuth Layout**: Adaptive grid (1/2/3 columns) based on provider count
|
|
12
14
|
- **TypeScript**: Full type safety out of the box
|
|
13
|
-
- **Customizable**: Override styles with the `appearance` prop
|
|
15
|
+
- **Customizable**: Override styles with the `appearance` prop or build from primitives
|
|
14
16
|
|
|
15
17
|
## Installation
|
|
16
18
|
|
|
17
19
|
```bash
|
|
18
|
-
npm install @insforge/nextjs
|
|
20
|
+
npm install @insforge/nextjs
|
|
19
21
|
# or
|
|
20
|
-
yarn add @insforge/nextjs
|
|
22
|
+
yarn add @insforge/nextjs
|
|
21
23
|
```
|
|
22
24
|
|
|
23
|
-
**Required peer dependencies:**
|
|
24
|
-
- `lucide-react` - Icon library (used for icons in components)
|
|
25
25
|
|
|
26
26
|
## Quick Start
|
|
27
27
|
|
|
28
|
-
### 1.
|
|
29
|
-
|
|
30
|
-
First, query your Insforge backend to see what OAuth providers are configured:
|
|
31
|
-
|
|
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
|
-
]
|
|
45
|
-
}
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
**For AI Agents:** Use the Insforge MCP tool `get-backend-metadata` to automatically retrieve this configuration.
|
|
49
|
-
|
|
50
|
-
### 2. Wrap your app with AuthProvider
|
|
28
|
+
### 1. Wrap your app with InsforgeProvider
|
|
51
29
|
|
|
52
30
|
```tsx
|
|
53
31
|
// app/layout.tsx
|
|
54
|
-
import {
|
|
32
|
+
import { InsforgeProvider } from '@insforge/nextjs';
|
|
33
|
+
import '@insforge/nextjs/styles.css';
|
|
55
34
|
|
|
56
35
|
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
57
36
|
const baseUrl = process.env.NEXT_PUBLIC_INSFORGE_BASE_URL!;
|
|
@@ -59,60 +38,117 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
|
|
59
38
|
return (
|
|
60
39
|
<html lang="en">
|
|
61
40
|
<body>
|
|
62
|
-
<
|
|
41
|
+
<InsforgeProvider baseUrl={baseUrl}>
|
|
63
42
|
{children}
|
|
64
|
-
</
|
|
43
|
+
</InsforgeProvider>
|
|
65
44
|
</body>
|
|
66
45
|
</html>
|
|
67
46
|
);
|
|
68
47
|
}
|
|
69
48
|
```
|
|
70
49
|
|
|
71
|
-
**
|
|
50
|
+
**That's it!** No Tailwind configuration needed. The package uses standalone CSS.
|
|
51
|
+
|
|
52
|
+
### 2. Set up API route for authentication (Required for SSR)
|
|
53
|
+
|
|
54
|
+
Create an API route to handle authentication cookies. This is **essential** for Next.js server-side rendering and middleware to work properly.
|
|
72
55
|
|
|
73
56
|
```tsx
|
|
74
|
-
//
|
|
75
|
-
import '@insforge/nextjs/
|
|
57
|
+
// app/api/auth/route.ts
|
|
58
|
+
import { createAuthRouteHandlers } from '@insforge/nextjs/api';
|
|
59
|
+
|
|
60
|
+
const handlers = createAuthRouteHandlers({
|
|
61
|
+
baseUrl: process.env.INSFORGE_BASE_URL || process.env.NEXT_PUBLIC_INSFORGE_BASE_URL!,
|
|
62
|
+
cookieName: 'insforge_token',
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
export const POST = handlers.POST;
|
|
66
|
+
export const GET = handlers.GET;
|
|
67
|
+
export const DELETE = handlers.DELETE;
|
|
76
68
|
```
|
|
77
69
|
|
|
78
|
-
**
|
|
70
|
+
**Why is this needed?**
|
|
71
|
+
|
|
72
|
+
Next.js middleware runs on the server and cannot access `localStorage` (which is browser-only). This API route:
|
|
73
|
+
- Syncs tokens from `localStorage` to HTTP-only cookies
|
|
74
|
+
- Enables server-side authentication in middleware
|
|
75
|
+
- Provides server-side sign-in/sign-up endpoints
|
|
79
76
|
|
|
80
|
-
### 3. Add
|
|
77
|
+
### 3. Add authentication callback page (Required for Next.js SSR)
|
|
81
78
|
|
|
82
|
-
|
|
79
|
+
Create a callback page to handle post-authentication token synchronization. This is **required for all authentication methods** (email/password and OAuth) when using Next.js middleware:
|
|
83
80
|
|
|
84
81
|
```tsx
|
|
85
82
|
// app/auth/callback/page.tsx
|
|
86
83
|
'use client';
|
|
87
84
|
|
|
88
|
-
import { useEffect, Suspense } from 'react';
|
|
85
|
+
import { useEffect, useRef, Suspense } from 'react';
|
|
89
86
|
import { useRouter, useSearchParams } from 'next/navigation';
|
|
90
87
|
|
|
91
88
|
function CallbackContent() {
|
|
92
89
|
const router = useRouter();
|
|
93
90
|
const searchParams = useSearchParams();
|
|
91
|
+
const isProcessingRef = useRef(false);
|
|
94
92
|
|
|
95
93
|
useEffect(() => {
|
|
96
|
-
const processOAuthCallback = () => {
|
|
94
|
+
const processOAuthCallback = async () => {
|
|
95
|
+
// Prevent double-processing in React Strict Mode
|
|
96
|
+
if (isProcessingRef.current) return;
|
|
97
|
+
isProcessingRef.current = true;
|
|
98
|
+
|
|
97
99
|
const accessToken = searchParams.get('access_token');
|
|
98
100
|
const error = searchParams.get('error');
|
|
99
101
|
|
|
100
102
|
if (error) {
|
|
103
|
+
console.error('OAuth error:', error);
|
|
101
104
|
router.push('/sign-in?error=' + encodeURIComponent(error));
|
|
102
105
|
return;
|
|
103
106
|
}
|
|
104
107
|
|
|
105
108
|
if (accessToken) {
|
|
106
|
-
// Store token in localStorage (
|
|
109
|
+
// 1. Store token in localStorage (for SDK client-side operations)
|
|
107
110
|
localStorage.setItem('insforge-auth-token', accessToken);
|
|
108
111
|
|
|
109
|
-
|
|
112
|
+
console.log('✅ OAuth token stored in localStorage');
|
|
113
|
+
|
|
114
|
+
// 2. Sync token to HTTP-only cookie (for server-side middleware)
|
|
115
|
+
try {
|
|
116
|
+
const syncResponse = await fetch('/api/auth', {
|
|
117
|
+
method: 'POST',
|
|
118
|
+
headers: {
|
|
119
|
+
'Content-Type': 'application/json',
|
|
120
|
+
},
|
|
121
|
+
body: JSON.stringify({
|
|
122
|
+
action: 'sync-token',
|
|
123
|
+
token: accessToken,
|
|
124
|
+
}),
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
if (syncResponse.ok) {
|
|
128
|
+
console.log('✅ OAuth token synced to cookie');
|
|
129
|
+
} else {
|
|
130
|
+
console.warn('⚠️ Failed to sync token to cookie, but localStorage has it');
|
|
131
|
+
}
|
|
132
|
+
} catch (syncError) {
|
|
133
|
+
console.warn('⚠️ Cookie sync failed:', syncError);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// 3. Get the final destination from sessionStorage
|
|
110
137
|
const finalDestination = sessionStorage.getItem('oauth_final_destination') || '/dashboard';
|
|
111
138
|
sessionStorage.removeItem('oauth_final_destination');
|
|
112
139
|
|
|
113
|
-
|
|
140
|
+
// 4. Clean up URL to remove OAuth params before redirect
|
|
141
|
+
window.history.replaceState({}, '', '/auth/callback');
|
|
142
|
+
|
|
143
|
+
// 5. Small delay to ensure cookie is set before redirect
|
|
144
|
+
setTimeout(() => {
|
|
145
|
+
router.push(finalDestination);
|
|
146
|
+
}, 100);
|
|
114
147
|
} else {
|
|
115
|
-
|
|
148
|
+
if (searchParams.toString()) {
|
|
149
|
+
console.error('No token received from OAuth');
|
|
150
|
+
router.push('/sign-in?error=no_token');
|
|
151
|
+
}
|
|
116
152
|
}
|
|
117
153
|
};
|
|
118
154
|
|
|
@@ -131,14 +167,28 @@ function CallbackContent() {
|
|
|
131
167
|
|
|
132
168
|
export default function OAuthCallbackPage() {
|
|
133
169
|
return (
|
|
134
|
-
<Suspense fallback={
|
|
170
|
+
<Suspense fallback={
|
|
171
|
+
<div className="flex items-center justify-center min-h-screen">
|
|
172
|
+
<div>Loading...</div>
|
|
173
|
+
</div>
|
|
174
|
+
}>
|
|
135
175
|
<CallbackContent />
|
|
136
176
|
</Suspense>
|
|
137
177
|
);
|
|
138
178
|
}
|
|
139
179
|
```
|
|
140
180
|
|
|
141
|
-
|
|
181
|
+
**What this does:**
|
|
182
|
+
1. Receives authentication callback with `access_token` from backend (works for both OAuth and email/password)
|
|
183
|
+
2. Stores token in `localStorage` for SDK client-side operations
|
|
184
|
+
3. Syncs token to HTTP-only cookie via `/api/auth` for server-side middleware
|
|
185
|
+
4. Redirects user to their intended destination
|
|
186
|
+
|
|
187
|
+
**Note:** This callback page handles returns from:
|
|
188
|
+
- OAuth providers (Google, GitHub, etc.)
|
|
189
|
+
- Email/password sign-in/sign-up when using the pre-built components
|
|
190
|
+
|
|
191
|
+
### 4. Add sign-in page
|
|
142
192
|
|
|
143
193
|
```tsx
|
|
144
194
|
// app/sign-in/page.tsx
|
|
@@ -147,8 +197,6 @@ import { SignIn } from '@insforge/nextjs';
|
|
|
147
197
|
export default function SignInPage() {
|
|
148
198
|
return (
|
|
149
199
|
<SignIn
|
|
150
|
-
baseUrl={process.env.NEXT_PUBLIC_INSFORGE_BASE_URL!}
|
|
151
|
-
providers={['github', 'google']} // Based on your backend config
|
|
152
200
|
afterSignInUrl="/dashboard"
|
|
153
201
|
title="Welcome to MyApp"
|
|
154
202
|
subtitle="Sign in to continue"
|
|
@@ -157,7 +205,7 @@ export default function SignInPage() {
|
|
|
157
205
|
}
|
|
158
206
|
```
|
|
159
207
|
|
|
160
|
-
**
|
|
208
|
+
**OAuth Providers:** The package automatically detects which OAuth providers are configured on your backend. You don't need to specify them manually!
|
|
161
209
|
|
|
162
210
|
### 5. Add sign-up page
|
|
163
211
|
|
|
@@ -168,8 +216,6 @@ import { SignUp } from '@insforge/nextjs';
|
|
|
168
216
|
export default function SignUpPage() {
|
|
169
217
|
return (
|
|
170
218
|
<SignUp
|
|
171
|
-
baseUrl={process.env.NEXT_PUBLIC_INSFORGE_BASE_URL!}
|
|
172
|
-
providers={['github', 'google']} // Based on your backend config
|
|
173
219
|
afterSignUpUrl="/dashboard"
|
|
174
220
|
/>
|
|
175
221
|
);
|
|
@@ -211,40 +257,110 @@ export default function Home() {
|
|
|
211
257
|
|
|
212
258
|
```ts
|
|
213
259
|
// middleware.ts
|
|
214
|
-
import {
|
|
260
|
+
import { InsforgeMiddleware } from '@insforge/nextjs/middleware';
|
|
215
261
|
|
|
216
|
-
export default
|
|
262
|
+
export default InsforgeMiddleware({
|
|
217
263
|
baseUrl: process.env.INSFORGE_BASE_URL!,
|
|
218
|
-
publicRoutes: ['/sign-in', '/sign-up', '/'],
|
|
264
|
+
publicRoutes: ['/sign-in', '/sign-up', '/', '/auth/callback'], // Include callback!
|
|
219
265
|
signInUrl: '/sign-in',
|
|
266
|
+
cookieName: 'insforge_token', // Must match API route cookie name
|
|
220
267
|
});
|
|
221
268
|
|
|
222
269
|
export const config = {
|
|
223
|
-
matcher: ['/((?!_next|
|
|
270
|
+
matcher: ['/((?!api|_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)'],
|
|
224
271
|
};
|
|
225
272
|
```
|
|
226
273
|
|
|
274
|
+
**Important:**
|
|
275
|
+
- **Must include** `/auth/callback` in `publicRoutes` to allow authentication returns
|
|
276
|
+
- Use the same `cookieName` in both middleware and API route
|
|
277
|
+
- The `cookieName` must match between middleware and API route configuration
|
|
278
|
+
|
|
279
|
+
**Alternative imports:**
|
|
280
|
+
- `InsforgeMiddleware` - Recommended, most descriptive
|
|
281
|
+
- `withInsforgeAuth` - Alternative with Insforge branding
|
|
282
|
+
- `withAuth` - Deprecated but still supported for backward compatibility
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## Architecture: Why Callback + API Route?
|
|
287
|
+
|
|
288
|
+
This package is specifically designed for **Next.js App Router with Server-Side Rendering (SSR)**. Here's why both the callback page and API route are essential:
|
|
289
|
+
|
|
290
|
+
### The Challenge
|
|
291
|
+
|
|
292
|
+
Next.js has two execution environments:
|
|
293
|
+
- **Client-side**: Browser, can access `localStorage`
|
|
294
|
+
- **Server-side**: Middleware, Server Components, cannot access `localStorage`
|
|
295
|
+
|
|
296
|
+
The Insforge SDK stores authentication tokens in `localStorage` (client-side only), but Next.js middleware needs to verify authentication on the server.
|
|
297
|
+
|
|
298
|
+
### The Solution
|
|
299
|
+
|
|
300
|
+
**Two-Storage Architecture:**
|
|
301
|
+
|
|
302
|
+
```
|
|
303
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
304
|
+
│ Authentication Flow (Email/Password & OAuth) │
|
|
305
|
+
└─────────────────────────────────────────────────────────────┘
|
|
306
|
+
|
|
307
|
+
1. User signs in (email/password OR OAuth)
|
|
308
|
+
↓
|
|
309
|
+
2. Backend redirects to: /auth/callback?access_token=xxx
|
|
310
|
+
↓
|
|
311
|
+
3. Callback Page (Client-side)
|
|
312
|
+
├─ Stores token in localStorage (for SDK)
|
|
313
|
+
└─ Calls /api/auth with "sync-token" action
|
|
314
|
+
↓
|
|
315
|
+
4. API Route (Server-side)
|
|
316
|
+
├─ Validates token with backend
|
|
317
|
+
└─ Sets HTTP-only cookie
|
|
318
|
+
↓
|
|
319
|
+
5. Middleware (Server-side)
|
|
320
|
+
└─ Reads cookie to protect routes
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
**Why this matters:**
|
|
324
|
+
- **Client-side operations** (SDK calls) use `localStorage`
|
|
325
|
+
- **Server-side operations** (middleware, SSR) use HTTP-only cookies
|
|
326
|
+
- Both storages stay synchronized through the `/api/auth` endpoint
|
|
327
|
+
|
|
328
|
+
### What Each Component Does
|
|
329
|
+
|
|
330
|
+
| Component | Purpose | Runs On |
|
|
331
|
+
|-----------|---------|---------|
|
|
332
|
+
| **Callback Page** | Receives auth token (any method), stores in localStorage, syncs to cookie | Client |
|
|
333
|
+
| **API Route** | Validates tokens, manages cookies, provides server-side auth | Server |
|
|
334
|
+
| **Middleware** | Protects routes, reads cookies, validates sessions | Server |
|
|
335
|
+
| **SDK** | Makes API calls, reads from localStorage | Client |
|
|
336
|
+
|
|
337
|
+
This architecture enables full-stack authentication in Next.js while maintaining security with HTTP-only cookies.
|
|
338
|
+
|
|
339
|
+
**Note:** All authentication methods (email/password, OAuth) use this same flow for consistency and SSR support.
|
|
340
|
+
|
|
227
341
|
## API Reference
|
|
228
342
|
|
|
229
343
|
### Components
|
|
230
344
|
|
|
231
|
-
#### `<
|
|
345
|
+
#### `<InsforgeProvider>`
|
|
232
346
|
|
|
233
|
-
Wraps your application and provides auth context.
|
|
347
|
+
Wraps your application and provides auth context. Automatically fetches OAuth provider configuration from your backend.
|
|
234
348
|
|
|
235
349
|
```tsx
|
|
236
|
-
<
|
|
350
|
+
<InsforgeProvider
|
|
237
351
|
baseUrl="https://your-backend.insforge.app"
|
|
238
352
|
onAuthChange={(user) => console.log('Auth changed:', user)}
|
|
239
353
|
>
|
|
240
354
|
{children}
|
|
241
|
-
</
|
|
355
|
+
</InsforgeProvider>
|
|
242
356
|
```
|
|
243
357
|
|
|
244
358
|
**Props:**
|
|
245
359
|
- `baseUrl` (required): Your Insforge backend URL
|
|
246
360
|
- `onAuthChange`: Optional callback when auth state changes
|
|
247
361
|
|
|
362
|
+
**Note:** The provider automatically queries your backend's OAuth configuration, so components like `<SignIn>` and `<SignUp>` will display the correct OAuth buttons without manual configuration.
|
|
363
|
+
|
|
248
364
|
---
|
|
249
365
|
|
|
250
366
|
#### `<SignIn>`
|
|
@@ -253,8 +369,6 @@ Pre-built sign-in form with email/password and OAuth.
|
|
|
253
369
|
|
|
254
370
|
```tsx
|
|
255
371
|
<SignIn
|
|
256
|
-
baseUrl="https://your-backend.insforge.app"
|
|
257
|
-
providers={['github', 'google']}
|
|
258
372
|
afterSignInUrl="/dashboard"
|
|
259
373
|
appearance={{
|
|
260
374
|
container: { background: '#f5f5f5' },
|
|
@@ -266,13 +380,13 @@ Pre-built sign-in form with email/password and OAuth.
|
|
|
266
380
|
```
|
|
267
381
|
|
|
268
382
|
**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
383
|
- `afterSignInUrl`: Redirect URL after successful sign-in (default: `/`)
|
|
272
384
|
- `appearance`: Custom styles for container, form, and button
|
|
273
385
|
- `onSuccess`: Callback on successful sign-in
|
|
274
386
|
- `onError`: Callback on error
|
|
275
387
|
|
|
388
|
+
**Note:** OAuth providers are automatically detected from your backend configuration via `InsforgeProvider`. No need to manually specify them!
|
|
389
|
+
|
|
276
390
|
---
|
|
277
391
|
|
|
278
392
|
#### `<SignUp>`
|
|
@@ -281,8 +395,6 @@ Pre-built sign-up form with email/password and OAuth.
|
|
|
281
395
|
|
|
282
396
|
```tsx
|
|
283
397
|
<SignUp
|
|
284
|
-
baseUrl="https://your-backend.insforge.app"
|
|
285
|
-
providers={['github', 'google']}
|
|
286
398
|
afterSignUpUrl="/onboarding"
|
|
287
399
|
appearance={{
|
|
288
400
|
container: { background: '#f5f5f5' }
|
|
@@ -291,13 +403,135 @@ Pre-built sign-up form with email/password and OAuth.
|
|
|
291
403
|
```
|
|
292
404
|
|
|
293
405
|
**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
406
|
- `afterSignUpUrl`: Redirect URL after successful sign-up (default: `/`)
|
|
297
407
|
- `appearance`: Custom styles for container, form, and button
|
|
298
408
|
- `onSuccess`: Callback on successful sign-up
|
|
299
409
|
- `onError`: Callback on error
|
|
300
410
|
|
|
411
|
+
**Note:** OAuth providers are automatically detected from your backend configuration via `InsforgeProvider`. No need to manually specify them!
|
|
412
|
+
|
|
413
|
+
---
|
|
414
|
+
|
|
415
|
+
### Auth Component Primitives (v0.5.0+)
|
|
416
|
+
|
|
417
|
+
For advanced customization, use the low-level Auth primitives to build your own authentication UI:
|
|
418
|
+
|
|
419
|
+
```tsx
|
|
420
|
+
import {
|
|
421
|
+
AuthContainer,
|
|
422
|
+
AuthHeader,
|
|
423
|
+
AuthErrorBanner,
|
|
424
|
+
AuthFormField,
|
|
425
|
+
AuthPasswordField,
|
|
426
|
+
AuthSubmitButton,
|
|
427
|
+
AuthDivider,
|
|
428
|
+
AuthLink,
|
|
429
|
+
AuthOAuthProviders,
|
|
430
|
+
AuthBranding,
|
|
431
|
+
} from '@insforge/nextjs';
|
|
432
|
+
import '@insforge/nextjs/styles.css';
|
|
433
|
+
|
|
434
|
+
function CustomSignIn() {
|
|
435
|
+
const [email, setEmail] = useState('');
|
|
436
|
+
const [password, setPassword] = useState('');
|
|
437
|
+
const [error, setError] = useState('');
|
|
438
|
+
const [loading, setLoading] = useState(false);
|
|
439
|
+
|
|
440
|
+
async function handleSubmit(e) {
|
|
441
|
+
e.preventDefault();
|
|
442
|
+
// Your custom auth logic
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
return (
|
|
446
|
+
<AuthContainer>
|
|
447
|
+
<AuthHeader title="Welcome Back" subtitle="Sign in to continue" />
|
|
448
|
+
|
|
449
|
+
<AuthErrorBanner error={error} />
|
|
450
|
+
|
|
451
|
+
<form onSubmit={handleSubmit} className="insforge-form">
|
|
452
|
+
<AuthFormField
|
|
453
|
+
id="email"
|
|
454
|
+
type="email"
|
|
455
|
+
label="Email"
|
|
456
|
+
placeholder="you@example.com"
|
|
457
|
+
value={email}
|
|
458
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
459
|
+
required
|
|
460
|
+
/>
|
|
461
|
+
|
|
462
|
+
<AuthPasswordField
|
|
463
|
+
id="password"
|
|
464
|
+
label="Password"
|
|
465
|
+
value={password}
|
|
466
|
+
onChange={(e) => setPassword(e.target.value)}
|
|
467
|
+
required
|
|
468
|
+
forgotPasswordLink={{
|
|
469
|
+
href: '/forgot-password',
|
|
470
|
+
text: 'Forgot Password?'
|
|
471
|
+
}}
|
|
472
|
+
/>
|
|
473
|
+
|
|
474
|
+
<AuthSubmitButton isLoading={loading}>
|
|
475
|
+
Sign In
|
|
476
|
+
</AuthSubmitButton>
|
|
477
|
+
</form>
|
|
478
|
+
|
|
479
|
+
<AuthLink
|
|
480
|
+
text="Don't have an account?"
|
|
481
|
+
linkText="Sign up"
|
|
482
|
+
href="/sign-up"
|
|
483
|
+
/>
|
|
484
|
+
|
|
485
|
+
<AuthDivider text="or" />
|
|
486
|
+
|
|
487
|
+
<AuthOAuthProviders
|
|
488
|
+
providers={['google', 'github', 'discord']}
|
|
489
|
+
onClick={(provider) => handleOAuth(provider)}
|
|
490
|
+
loading={null}
|
|
491
|
+
/>
|
|
492
|
+
|
|
493
|
+
<AuthBranding />
|
|
494
|
+
</AuthContainer>
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
#### Available Primitives
|
|
500
|
+
|
|
501
|
+
| Component | Description |
|
|
502
|
+
|-----------|-------------|
|
|
503
|
+
| `<AuthContainer>` | Main wrapper with card styling. Accepts `style` prop for customization |
|
|
504
|
+
| `<AuthHeader>` | Displays `title` and optional `subtitle` |
|
|
505
|
+
| `<AuthErrorBanner>` | Shows error messages. Pass `error` string prop |
|
|
506
|
+
| `<AuthFormField>` | Standard input field. Supports all HTML input props + `label` |
|
|
507
|
+
| `<AuthPasswordField>` | Password input with visibility toggle, optional strength indicator, and forgot password link |
|
|
508
|
+
| `<AuthSubmitButton>` | Submit button with loading state. Pass `isLoading` and optional `loadingText` |
|
|
509
|
+
| `<AuthDivider>` | Visual separator with customizable `text` (default: "or") |
|
|
510
|
+
| `<AuthLink>` | Call-to-action link. Props: `text`, `linkText`, `href` |
|
|
511
|
+
| `<AuthOAuthProviders>` | Smart grid of OAuth buttons. Props: `providers`, `onClick`, `loading` |
|
|
512
|
+
| `<AuthBranding>` | "Powered by InsForge" branding. Optional `text` and `href` props |
|
|
513
|
+
|
|
514
|
+
**`AuthPasswordField` Props:**
|
|
515
|
+
- All standard input props (`value`, `onChange`, `required`, etc.)
|
|
516
|
+
- `label`: Field label text
|
|
517
|
+
- `id`: Input element ID
|
|
518
|
+
- `showStrengthIndicator`: Show password strength indicator (default: `false`)
|
|
519
|
+
- `forgotPasswordLink`: Object with `{ href: string, text?: string }`
|
|
520
|
+
|
|
521
|
+
**`AuthOAuthProviders` Smart Layout:**
|
|
522
|
+
- 1 provider: Full-width button with "Continue with Google"
|
|
523
|
+
- 2 providers: Two columns with short text "Google", "GitHub"
|
|
524
|
+
- 3+ providers: Three columns with icons only
|
|
525
|
+
- Auto-centers incomplete last rows (e.g., 5, 8, 11 providers)
|
|
526
|
+
|
|
527
|
+
**When to use primitives vs pre-built components:**
|
|
528
|
+
- Use `<SignIn>` / `<SignUp>` for quick setup with minimal customization
|
|
529
|
+
- Use Auth primitives when you need:
|
|
530
|
+
- Custom form layouts
|
|
531
|
+
- Additional fields (username, terms checkbox, etc.)
|
|
532
|
+
- Integration with your own state management
|
|
533
|
+
- Unique branding and styling
|
|
534
|
+
|
|
301
535
|
---
|
|
302
536
|
|
|
303
537
|
#### `<UserButton>`
|
|
@@ -442,16 +676,68 @@ function Component() {
|
|
|
442
676
|
|
|
443
677
|
---
|
|
444
678
|
|
|
679
|
+
### API Route Handlers
|
|
680
|
+
|
|
681
|
+
#### `createAuthRouteHandlers(config)`
|
|
682
|
+
|
|
683
|
+
Creates Next.js App Router API handlers for authentication with HTTP-only cookie management.
|
|
684
|
+
|
|
685
|
+
```tsx
|
|
686
|
+
// app/api/auth/route.ts
|
|
687
|
+
import { createAuthRouteHandlers } from '@insforge/nextjs/api';
|
|
688
|
+
|
|
689
|
+
const handlers = createAuthRouteHandlers({
|
|
690
|
+
baseUrl: process.env.INSFORGE_BASE_URL!,
|
|
691
|
+
cookieName: 'insforge_token',
|
|
692
|
+
cookieMaxAge: 7 * 24 * 60 * 60, // 7 days
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
export const POST = handlers.POST;
|
|
696
|
+
export const GET = handlers.GET;
|
|
697
|
+
export const DELETE = handlers.DELETE;
|
|
698
|
+
```
|
|
699
|
+
|
|
700
|
+
**Config:**
|
|
701
|
+
- `baseUrl` (required): Your Insforge backend URL
|
|
702
|
+
- `cookieName`: Cookie name for auth token (default: `insforge_token`)
|
|
703
|
+
- `cookieMaxAge`: Cookie expiration in seconds (default: 7 days)
|
|
704
|
+
- `secure`: Use secure cookies (auto-detected, `true` in production)
|
|
705
|
+
|
|
706
|
+
**Supported Actions (POST):**
|
|
707
|
+
- `sign-in`: Email/password sign-in with cookie creation
|
|
708
|
+
- `sign-up`: Email/password sign-up with cookie creation
|
|
709
|
+
- `sync-token`: Sync token from localStorage to cookie (for OAuth)
|
|
710
|
+
|
|
711
|
+
**Example: Sync token after OAuth**
|
|
712
|
+
```typescript
|
|
713
|
+
// In your OAuth callback page
|
|
714
|
+
const response = await fetch('/api/auth', {
|
|
715
|
+
method: 'POST',
|
|
716
|
+
headers: { 'Content-Type': 'application/json' },
|
|
717
|
+
body: JSON.stringify({
|
|
718
|
+
action: 'sync-token',
|
|
719
|
+
token: accessToken,
|
|
720
|
+
}),
|
|
721
|
+
});
|
|
722
|
+
```
|
|
723
|
+
|
|
724
|
+
**Endpoints:**
|
|
725
|
+
- `POST /api/auth` - Sign in, sign up, or sync token
|
|
726
|
+
- `GET /api/auth` - Check current session
|
|
727
|
+
- `DELETE /api/auth` - Sign out and clear cookies
|
|
728
|
+
|
|
729
|
+
---
|
|
730
|
+
|
|
445
731
|
### Middleware
|
|
446
732
|
|
|
447
|
-
#### `
|
|
733
|
+
#### `InsforgeMiddleware(config)`
|
|
448
734
|
|
|
449
735
|
Create Next.js middleware for route protection.
|
|
450
736
|
|
|
451
737
|
```ts
|
|
452
|
-
import {
|
|
738
|
+
import { InsforgeMiddleware } from '@insforge/nextjs/middleware';
|
|
453
739
|
|
|
454
|
-
export default
|
|
740
|
+
export default InsforgeMiddleware({
|
|
455
741
|
baseUrl: process.env.INSFORGE_BASE_URL!,
|
|
456
742
|
publicRoutes: ['/sign-in', '/sign-up', '/', '/about'],
|
|
457
743
|
signInUrl: '/sign-in',
|
|
@@ -465,18 +751,22 @@ export default withAuth({
|
|
|
465
751
|
- `signInUrl`: URL to redirect to when not authenticated (default: `/sign-in`)
|
|
466
752
|
- `cookieName`: Cookie name for auth token (default: `insforge_token`)
|
|
467
753
|
|
|
754
|
+
**Alternative exports:**
|
|
755
|
+
- `withInsforgeAuth` - Same as `InsforgeMiddleware` with Insforge branding
|
|
756
|
+
- `withAuth` - Deprecated alias for backward compatibility
|
|
757
|
+
|
|
468
758
|
---
|
|
469
759
|
|
|
470
|
-
#### `
|
|
760
|
+
#### `getAuthUserId(headers)` and `getAuthToken(headers)`
|
|
471
761
|
|
|
472
762
|
Get authenticated user ID or token in server components.
|
|
473
763
|
|
|
474
764
|
```tsx
|
|
475
765
|
import { headers } from 'next/headers';
|
|
476
|
-
import {
|
|
766
|
+
import { getAuthUserId, getAuthToken } from '@insforge/nextjs/middleware';
|
|
477
767
|
|
|
478
768
|
export default async function ServerComponent() {
|
|
479
|
-
const userId =
|
|
769
|
+
const userId = getAuthUserId(headers());
|
|
480
770
|
const token = getAuthToken(headers());
|
|
481
771
|
|
|
482
772
|
// Fetch user-specific data
|
|
@@ -486,6 +776,9 @@ export default async function ServerComponent() {
|
|
|
486
776
|
}
|
|
487
777
|
```
|
|
488
778
|
|
|
779
|
+
**Alternative exports:**
|
|
780
|
+
- `getAuthUser` - Deprecated alias for `getAuthUserId` (backward compatibility)
|
|
781
|
+
|
|
489
782
|
---
|
|
490
783
|
|
|
491
784
|
## Styling
|
|
@@ -623,7 +916,7 @@ All components use semantic CSS class names prefixed with `insforge-`:
|
|
|
623
916
|
### Custom Auth Callbacks
|
|
624
917
|
|
|
625
918
|
```tsx
|
|
626
|
-
<
|
|
919
|
+
<InsforgeProvider
|
|
627
920
|
baseUrl={baseUrl}
|
|
628
921
|
onAuthChange={(user) => {
|
|
629
922
|
if (user) {
|
|
@@ -634,7 +927,7 @@ All components use semantic CSS class names prefixed with `insforge-`:
|
|
|
634
927
|
}}
|
|
635
928
|
>
|
|
636
929
|
{children}
|
|
637
|
-
</
|
|
930
|
+
</InsforgeProvider>
|
|
638
931
|
```
|
|
639
932
|
|
|
640
933
|
### Server-Side User Data
|
|
@@ -642,11 +935,11 @@ All components use semantic CSS class names prefixed with `insforge-`:
|
|
|
642
935
|
```tsx
|
|
643
936
|
// app/dashboard/page.tsx
|
|
644
937
|
import { headers } from 'next/headers';
|
|
645
|
-
import {
|
|
938
|
+
import { getAuthUserId } from '@insforge/nextjs/middleware';
|
|
646
939
|
import { createClient } from '@insforge/sdk';
|
|
647
940
|
|
|
648
941
|
export default async function Dashboard() {
|
|
649
|
-
const userId =
|
|
942
|
+
const userId = getAuthUserId(headers());
|
|
650
943
|
|
|
651
944
|
const insforge = createClient({
|
|
652
945
|
baseUrl: process.env.INSFORGE_BASE_URL!
|
|
@@ -693,31 +986,25 @@ INSFORGE_BASE_URL=https://your-backend.insforge.app
|
|
|
693
986
|
When building Next.js apps with authentication:
|
|
694
987
|
|
|
695
988
|
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
|
-
```
|
|
989
|
+
2. **Generate code** - Use `<SignIn>` and `<SignUp>` components
|
|
990
|
+
3. **Automatic configuration** - OAuth providers are detected automatically from backend
|
|
711
991
|
|
|
712
992
|
**Agent generates:**
|
|
713
993
|
```tsx
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
994
|
+
// Step 1: Wrap app with InsforgeProvider
|
|
995
|
+
<InsforgeProvider baseUrl={process.env.NEXT_PUBLIC_INSFORGE_BASE_URL!}>
|
|
996
|
+
{children}
|
|
997
|
+
</InsforgeProvider>
|
|
998
|
+
|
|
999
|
+
// Step 2: Add sign-in page (OAuth auto-detected)
|
|
1000
|
+
<SignIn afterSignInUrl="/dashboard" />
|
|
719
1001
|
```
|
|
720
1002
|
|
|
1003
|
+
**Benefits:**
|
|
1004
|
+
- No need to query OAuth configuration manually
|
|
1005
|
+
- Components automatically render correct OAuth buttons
|
|
1006
|
+
- Backend configuration is the single source of truth
|
|
1007
|
+
|
|
721
1008
|
---
|
|
722
1009
|
|
|
723
1010
|
## TypeScript
|
|
@@ -754,6 +1041,6 @@ MIT
|
|
|
754
1041
|
|
|
755
1042
|
## Support
|
|
756
1043
|
|
|
757
|
-
- Documentation: https://docs.insforge.
|
|
758
|
-
- Issues: https://github.com/
|
|
759
|
-
- Discord: https://discord.
|
|
1044
|
+
- Documentation: https://docs.insforge.dev/introduction
|
|
1045
|
+
- Issues: https://github.com/InsForge/InsForge/issues
|
|
1046
|
+
- Discord: https://discord.com/invite/DvBtaEc9Jz
|