@insforge/nextjs 0.4.0
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 +759 -0
- package/dist/Manrope-VariableFont_wght-OKHRIJEM.ttf +0 -0
- package/dist/api.d.mts +39 -0
- package/dist/api.d.ts +39 -0
- package/dist/api.js +267 -0
- package/dist/api.js.map +1 -0
- package/dist/api.mjs +240 -0
- package/dist/api.mjs.map +1 -0
- package/dist/index.css +468 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.mts +146 -0
- package/dist/index.d.ts +146 -0
- package/dist/index.js +1086 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1037 -0
- package/dist/index.mjs.map +1 -0
- package/dist/middleware.d.mts +14 -0
- package/dist/middleware.d.ts +14 -0
- package/dist/middleware.js +99 -0
- package/dist/middleware.js.map +1 -0
- package/dist/middleware.mjs +70 -0
- package/dist/middleware.mjs.map +1 -0
- package/package.json +65 -0
- package/src/fonts/Manrope-VariableFont_wght.ttf +0 -0
- package/src/styles.css +551 -0
package/README.md
ADDED
|
@@ -0,0 +1,759 @@
|
|
|
1
|
+
# @insforge/nextjs
|
|
2
|
+
|
|
3
|
+
Pre-built authentication UI components for Next.js applications using Insforge backend. This package provides a Clerk-like experience with drop-in components that connect directly to your Insforge backend, reducing token usage for AI agents.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Drop-in UI Components**: Pre-styled SignIn, SignUp, and UserButton components
|
|
8
|
+
- **React Hooks**: Easy access to auth state with `useAuth()`, `useUser()`, `useSession()`
|
|
9
|
+
- **Control Components**: `<SignedIn>`, `<SignedOut>`, `<Protect>` for conditional rendering
|
|
10
|
+
- **Next.js Middleware**: Server-side route protection
|
|
11
|
+
- **OAuth Support**: Built-in Google and GitHub authentication
|
|
12
|
+
- **TypeScript**: Full type safety out of the box
|
|
13
|
+
- **Customizable**: Override styles with the `appearance` prop
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @insforge/nextjs lucide-react
|
|
19
|
+
# or
|
|
20
|
+
yarn add @insforge/nextjs lucide-react
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**Required peer dependencies:**
|
|
24
|
+
- `lucide-react` - Icon library (used for icons in components)
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
### 1. Check Your Backend OAuth Configuration
|
|
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
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
// app/layout.tsx
|
|
54
|
+
import { AuthProvider } from '@insforge/nextjs';
|
|
55
|
+
|
|
56
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
57
|
+
const baseUrl = process.env.NEXT_PUBLIC_INSFORGE_BASE_URL!;
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<html lang="en">
|
|
61
|
+
<body>
|
|
62
|
+
<AuthProvider baseUrl={baseUrl}>
|
|
63
|
+
{children}
|
|
64
|
+
</AuthProvider>
|
|
65
|
+
</body>
|
|
66
|
+
</html>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Note:** Import the component styles:
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
// In your layout.tsx or _app.tsx
|
|
75
|
+
import '@insforge/nextjs/styles.css';
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**That's it!** No Tailwind configuration needed. The package now uses standalone CSS.
|
|
79
|
+
|
|
80
|
+
### 3. Add OAuth callback page (Required for OAuth)
|
|
81
|
+
|
|
82
|
+
If you're using OAuth providers, you **must** create a callback page:
|
|
83
|
+
|
|
84
|
+
```tsx
|
|
85
|
+
// app/auth/callback/page.tsx
|
|
86
|
+
'use client';
|
|
87
|
+
|
|
88
|
+
import { useEffect, Suspense } from 'react';
|
|
89
|
+
import { useRouter, useSearchParams } from 'next/navigation';
|
|
90
|
+
|
|
91
|
+
function CallbackContent() {
|
|
92
|
+
const router = useRouter();
|
|
93
|
+
const searchParams = useSearchParams();
|
|
94
|
+
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
const processOAuthCallback = () => {
|
|
97
|
+
const accessToken = searchParams.get('access_token');
|
|
98
|
+
const error = searchParams.get('error');
|
|
99
|
+
|
|
100
|
+
if (error) {
|
|
101
|
+
router.push('/sign-in?error=' + encodeURIComponent(error));
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (accessToken) {
|
|
106
|
+
// Store token in localStorage (where SDK expects it)
|
|
107
|
+
localStorage.setItem('insforge-auth-token', accessToken);
|
|
108
|
+
|
|
109
|
+
// Get final destination
|
|
110
|
+
const finalDestination = sessionStorage.getItem('oauth_final_destination') || '/dashboard';
|
|
111
|
+
sessionStorage.removeItem('oauth_final_destination');
|
|
112
|
+
|
|
113
|
+
router.push(finalDestination);
|
|
114
|
+
} else {
|
|
115
|
+
router.push('/sign-in?error=no_token');
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
processOAuthCallback();
|
|
120
|
+
}, [searchParams, router]);
|
|
121
|
+
|
|
122
|
+
return (
|
|
123
|
+
<div className="flex items-center justify-center min-h-screen">
|
|
124
|
+
<div className="text-center">
|
|
125
|
+
<h2 className="text-2xl font-semibold mb-4">Completing authentication...</h2>
|
|
126
|
+
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto"></div>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export default function OAuthCallbackPage() {
|
|
133
|
+
return (
|
|
134
|
+
<Suspense fallback={<div>Loading...</div>}>
|
|
135
|
+
<CallbackContent />
|
|
136
|
+
</Suspense>
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### 4. Add sign-in page with OAuth providers
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
// app/sign-in/page.tsx
|
|
145
|
+
import { SignIn } from '@insforge/nextjs';
|
|
146
|
+
|
|
147
|
+
export default function SignInPage() {
|
|
148
|
+
return (
|
|
149
|
+
<SignIn
|
|
150
|
+
baseUrl={process.env.NEXT_PUBLIC_INSFORGE_BASE_URL!}
|
|
151
|
+
providers={['github', 'google']} // Based on your backend config
|
|
152
|
+
afterSignInUrl="/dashboard"
|
|
153
|
+
title="Welcome to MyApp"
|
|
154
|
+
subtitle="Sign in to continue"
|
|
155
|
+
/>
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Note:** The `providers` prop should match what you've configured in your Insforge backend. OAuth buttons will only render for providers specified in this array.
|
|
161
|
+
|
|
162
|
+
### 5. Add sign-up page
|
|
163
|
+
|
|
164
|
+
```tsx
|
|
165
|
+
// app/sign-up/page.tsx
|
|
166
|
+
import { SignUp } from '@insforge/nextjs';
|
|
167
|
+
|
|
168
|
+
export default function SignUpPage() {
|
|
169
|
+
return (
|
|
170
|
+
<SignUp
|
|
171
|
+
baseUrl={process.env.NEXT_PUBLIC_INSFORGE_BASE_URL!}
|
|
172
|
+
providers={['github', 'google']} // Based on your backend config
|
|
173
|
+
afterSignUpUrl="/dashboard"
|
|
174
|
+
/>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### 6. Use conditional components
|
|
180
|
+
|
|
181
|
+
```tsx
|
|
182
|
+
// app/page.tsx
|
|
183
|
+
import { SignedIn, SignedOut, UserButton } from '@insforge/nextjs';
|
|
184
|
+
|
|
185
|
+
export default function Home() {
|
|
186
|
+
return (
|
|
187
|
+
<div>
|
|
188
|
+
<nav>
|
|
189
|
+
<SignedOut>
|
|
190
|
+
<a href="/sign-in">Sign In</a>
|
|
191
|
+
</SignedOut>
|
|
192
|
+
|
|
193
|
+
<SignedIn>
|
|
194
|
+
<UserButton afterSignOutUrl="/" />
|
|
195
|
+
</SignedIn>
|
|
196
|
+
</nav>
|
|
197
|
+
|
|
198
|
+
<SignedIn>
|
|
199
|
+
<h1>Welcome back!</h1>
|
|
200
|
+
</SignedIn>
|
|
201
|
+
|
|
202
|
+
<SignedOut>
|
|
203
|
+
<h1>Please sign in</h1>
|
|
204
|
+
</SignedOut>
|
|
205
|
+
</div>
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### 7. Protect routes with middleware
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
// middleware.ts
|
|
214
|
+
import { withAuth } from '@insforge/nextjs/middleware';
|
|
215
|
+
|
|
216
|
+
export default withAuth({
|
|
217
|
+
baseUrl: process.env.INSFORGE_BASE_URL!,
|
|
218
|
+
publicRoutes: ['/sign-in', '/sign-up', '/'],
|
|
219
|
+
signInUrl: '/sign-in',
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
export const config = {
|
|
223
|
+
matcher: ['/((?!_next|api|.*\\..*).*)'],
|
|
224
|
+
};
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## API Reference
|
|
228
|
+
|
|
229
|
+
### Components
|
|
230
|
+
|
|
231
|
+
#### `<AuthProvider>`
|
|
232
|
+
|
|
233
|
+
Wraps your application and provides auth context.
|
|
234
|
+
|
|
235
|
+
```tsx
|
|
236
|
+
<AuthProvider
|
|
237
|
+
baseUrl="https://your-backend.insforge.app"
|
|
238
|
+
onAuthChange={(user) => console.log('Auth changed:', user)}
|
|
239
|
+
>
|
|
240
|
+
{children}
|
|
241
|
+
</AuthProvider>
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
**Props:**
|
|
245
|
+
- `baseUrl` (required): Your Insforge backend URL
|
|
246
|
+
- `onAuthChange`: Optional callback when auth state changes
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
#### `<SignIn>`
|
|
251
|
+
|
|
252
|
+
Pre-built sign-in form with email/password and OAuth.
|
|
253
|
+
|
|
254
|
+
```tsx
|
|
255
|
+
<SignIn
|
|
256
|
+
baseUrl="https://your-backend.insforge.app"
|
|
257
|
+
providers={['github', 'google']}
|
|
258
|
+
afterSignInUrl="/dashboard"
|
|
259
|
+
appearance={{
|
|
260
|
+
container: { background: '#f5f5f5' },
|
|
261
|
+
button: { background: 'blue' }
|
|
262
|
+
}}
|
|
263
|
+
onSuccess={(user) => console.log('Signed in:', user)}
|
|
264
|
+
onError={(error) => console.error('Error:', error)}
|
|
265
|
+
/>
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
**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
|
+
- `afterSignInUrl`: Redirect URL after successful sign-in (default: `/`)
|
|
272
|
+
- `appearance`: Custom styles for container, form, and button
|
|
273
|
+
- `onSuccess`: Callback on successful sign-in
|
|
274
|
+
- `onError`: Callback on error
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
#### `<SignUp>`
|
|
279
|
+
|
|
280
|
+
Pre-built sign-up form with email/password and OAuth.
|
|
281
|
+
|
|
282
|
+
```tsx
|
|
283
|
+
<SignUp
|
|
284
|
+
baseUrl="https://your-backend.insforge.app"
|
|
285
|
+
providers={['github', 'google']}
|
|
286
|
+
afterSignUpUrl="/onboarding"
|
|
287
|
+
appearance={{
|
|
288
|
+
container: { background: '#f5f5f5' }
|
|
289
|
+
}}
|
|
290
|
+
/>
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
**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
|
+
- `afterSignUpUrl`: Redirect URL after successful sign-up (default: `/`)
|
|
297
|
+
- `appearance`: Custom styles for container, form, and button
|
|
298
|
+
- `onSuccess`: Callback on successful sign-up
|
|
299
|
+
- `onError`: Callback on error
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
#### `<UserButton>`
|
|
304
|
+
|
|
305
|
+
User profile dropdown with sign-out functionality.
|
|
306
|
+
|
|
307
|
+
```tsx
|
|
308
|
+
<UserButton
|
|
309
|
+
afterSignOutUrl="/"
|
|
310
|
+
showEmail={true}
|
|
311
|
+
appearance={{
|
|
312
|
+
button: { borderRadius: '50%' },
|
|
313
|
+
dropdown: { minWidth: '250px' }
|
|
314
|
+
}}
|
|
315
|
+
/>
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
**Props:**
|
|
319
|
+
- `afterSignOutUrl`: Redirect URL after sign-out (default: `/`)
|
|
320
|
+
- `showEmail`: Show user email in dropdown (default: `true`)
|
|
321
|
+
- `appearance`: Custom styles for button and dropdown
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
#### `<SignedIn>`
|
|
326
|
+
|
|
327
|
+
Renders children only when user is authenticated.
|
|
328
|
+
|
|
329
|
+
```tsx
|
|
330
|
+
<SignedIn>
|
|
331
|
+
<Dashboard />
|
|
332
|
+
</SignedIn>
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
#### `<SignedOut>`
|
|
338
|
+
|
|
339
|
+
Renders children only when user is NOT authenticated.
|
|
340
|
+
|
|
341
|
+
```tsx
|
|
342
|
+
<SignedOut>
|
|
343
|
+
<LandingPage />
|
|
344
|
+
</SignedOut>
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
---
|
|
348
|
+
|
|
349
|
+
#### `<Protect>`
|
|
350
|
+
|
|
351
|
+
Protects content and optionally redirects unauthenticated users.
|
|
352
|
+
|
|
353
|
+
```tsx
|
|
354
|
+
<Protect
|
|
355
|
+
fallback={<Loading />}
|
|
356
|
+
redirectTo="/sign-in"
|
|
357
|
+
condition={(user) => user.role === 'admin'}
|
|
358
|
+
>
|
|
359
|
+
<AdminPanel />
|
|
360
|
+
</Protect>
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
**Props:**
|
|
364
|
+
- `fallback`: Component to show while loading or if unauthorized
|
|
365
|
+
- `redirectTo`: URL to redirect to if not authorized (default: `/sign-in`)
|
|
366
|
+
- `condition`: Optional function for custom authorization logic (e.g., role-based access)
|
|
367
|
+
|
|
368
|
+
---
|
|
369
|
+
|
|
370
|
+
### Hooks
|
|
371
|
+
|
|
372
|
+
#### `useAuth()`
|
|
373
|
+
|
|
374
|
+
Access complete auth state and methods.
|
|
375
|
+
|
|
376
|
+
```tsx
|
|
377
|
+
import { useAuth } from '@insforge/nextjs';
|
|
378
|
+
|
|
379
|
+
function Component() {
|
|
380
|
+
const {
|
|
381
|
+
user,
|
|
382
|
+
session,
|
|
383
|
+
isLoaded,
|
|
384
|
+
isSignedIn,
|
|
385
|
+
signIn,
|
|
386
|
+
signUp,
|
|
387
|
+
signOut,
|
|
388
|
+
updateUser
|
|
389
|
+
} = useAuth();
|
|
390
|
+
|
|
391
|
+
if (!isLoaded) return <div>Loading...</div>;
|
|
392
|
+
if (!isSignedIn) return <div>Please sign in</div>;
|
|
393
|
+
|
|
394
|
+
return <div>Hello {user.email}</div>;
|
|
395
|
+
}
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
**Returns:**
|
|
399
|
+
- `user`: Current user object or `null`
|
|
400
|
+
- `session`: Current session object or `null`
|
|
401
|
+
- `isLoaded`: Whether auth state has loaded
|
|
402
|
+
- `isSignedIn`: Whether user is authenticated
|
|
403
|
+
- `signIn(email, password)`: Sign in method
|
|
404
|
+
- `signUp(email, password)`: Sign up method
|
|
405
|
+
- `signOut()`: Sign out method
|
|
406
|
+
- `updateUser(data)`: Update user data
|
|
407
|
+
|
|
408
|
+
---
|
|
409
|
+
|
|
410
|
+
#### `useUser()`
|
|
411
|
+
|
|
412
|
+
Access current user data.
|
|
413
|
+
|
|
414
|
+
```tsx
|
|
415
|
+
import { useUser } from '@insforge/nextjs';
|
|
416
|
+
|
|
417
|
+
function Component() {
|
|
418
|
+
const { user, isLoaded } = useUser();
|
|
419
|
+
|
|
420
|
+
if (!isLoaded) return <div>Loading...</div>;
|
|
421
|
+
if (!user) return <div>Not signed in</div>;
|
|
422
|
+
|
|
423
|
+
return <div>{user.email}</div>;
|
|
424
|
+
}
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
---
|
|
428
|
+
|
|
429
|
+
#### `useSession()`
|
|
430
|
+
|
|
431
|
+
Access current session data.
|
|
432
|
+
|
|
433
|
+
```tsx
|
|
434
|
+
import { useSession } from '@insforge/nextjs';
|
|
435
|
+
|
|
436
|
+
function Component() {
|
|
437
|
+
const { session, isLoaded, isSignedIn } = useSession();
|
|
438
|
+
|
|
439
|
+
return <div>Signed in: {isSignedIn ? 'Yes' : 'No'}</div>;
|
|
440
|
+
}
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
---
|
|
444
|
+
|
|
445
|
+
### Middleware
|
|
446
|
+
|
|
447
|
+
#### `withAuth(config)`
|
|
448
|
+
|
|
449
|
+
Create Next.js middleware for route protection.
|
|
450
|
+
|
|
451
|
+
```ts
|
|
452
|
+
import { withAuth } from '@insforge/nextjs/middleware';
|
|
453
|
+
|
|
454
|
+
export default withAuth({
|
|
455
|
+
baseUrl: process.env.INSFORGE_BASE_URL!,
|
|
456
|
+
publicRoutes: ['/sign-in', '/sign-up', '/', '/about'],
|
|
457
|
+
signInUrl: '/sign-in',
|
|
458
|
+
cookieName: 'insforge_token',
|
|
459
|
+
});
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
**Config:**
|
|
463
|
+
- `baseUrl` (required): Your Insforge backend URL
|
|
464
|
+
- `publicRoutes`: Array of routes that don't require auth (supports wildcards: `/blog/*`)
|
|
465
|
+
- `signInUrl`: URL to redirect to when not authenticated (default: `/sign-in`)
|
|
466
|
+
- `cookieName`: Cookie name for auth token (default: `insforge_token`)
|
|
467
|
+
|
|
468
|
+
---
|
|
469
|
+
|
|
470
|
+
#### `getAuthUser(headers)` and `getAuthToken(headers)`
|
|
471
|
+
|
|
472
|
+
Get authenticated user ID or token in server components.
|
|
473
|
+
|
|
474
|
+
```tsx
|
|
475
|
+
import { headers } from 'next/headers';
|
|
476
|
+
import { getAuthUser, getAuthToken } from '@insforge/nextjs/middleware';
|
|
477
|
+
|
|
478
|
+
export default async function ServerComponent() {
|
|
479
|
+
const userId = getAuthUser(headers());
|
|
480
|
+
const token = getAuthToken(headers());
|
|
481
|
+
|
|
482
|
+
// Fetch user-specific data
|
|
483
|
+
const data = await fetchUserData(userId, token);
|
|
484
|
+
|
|
485
|
+
return <div>User ID: {userId}</div>;
|
|
486
|
+
}
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
---
|
|
490
|
+
|
|
491
|
+
## Styling
|
|
492
|
+
|
|
493
|
+
This package uses **standalone CSS** for styling - no Tailwind CSS dependency required! All components come pre-styled and ready to use.
|
|
494
|
+
|
|
495
|
+
### Setup
|
|
496
|
+
|
|
497
|
+
Simply import the CSS file once in your root layout:
|
|
498
|
+
|
|
499
|
+
```tsx
|
|
500
|
+
// app/layout.tsx
|
|
501
|
+
import '@insforge/nextjs/styles.css';
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
That's it! No configuration needed.
|
|
505
|
+
|
|
506
|
+
### Typography
|
|
507
|
+
|
|
508
|
+
The package includes the **Manrope variable font** for a modern, professional look. The font is automatically loaded and available as a CSS variable:
|
|
509
|
+
|
|
510
|
+
```css
|
|
511
|
+
/* Use the font in your own CSS */
|
|
512
|
+
.my-component {
|
|
513
|
+
font-family: var(--font-manrope);
|
|
514
|
+
}
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
The font family includes fallbacks to system fonts for optimal loading:
|
|
518
|
+
- `--font-manrope`: 'Manrope', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif
|
|
519
|
+
|
|
520
|
+
### Customization
|
|
521
|
+
|
|
522
|
+
**Option 1: Text Customization** (Easiest)
|
|
523
|
+
|
|
524
|
+
All text elements in the components can be customized via props:
|
|
525
|
+
|
|
526
|
+
```tsx
|
|
527
|
+
<SignIn
|
|
528
|
+
baseUrl="..."
|
|
529
|
+
// Customize all text
|
|
530
|
+
title="Welcome Back to MyApp"
|
|
531
|
+
subtitle="We're happy to see you again"
|
|
532
|
+
emailLabel="Your Email"
|
|
533
|
+
emailPlaceholder="you@company.com"
|
|
534
|
+
passwordLabel="Your Password"
|
|
535
|
+
forgotPasswordText="Forgot it?"
|
|
536
|
+
submitButtonText="Log In"
|
|
537
|
+
loadingButtonText="Logging in..."
|
|
538
|
+
signUpText="New user?"
|
|
539
|
+
signUpLinkText="Create an account"
|
|
540
|
+
signUpUrl="/register"
|
|
541
|
+
dividerText="or continue with"
|
|
542
|
+
/>
|
|
543
|
+
|
|
544
|
+
<SignUp
|
|
545
|
+
baseUrl="..."
|
|
546
|
+
// Customize signup text
|
|
547
|
+
title="Join MyApp Today"
|
|
548
|
+
subtitle="Create your account in seconds"
|
|
549
|
+
submitButtonText="Create Account"
|
|
550
|
+
signInText="Existing user?"
|
|
551
|
+
signInLinkText="Log in here"
|
|
552
|
+
/>
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
**Option 2: Style Customization**
|
|
556
|
+
|
|
557
|
+
Override inline styles for specific components:
|
|
558
|
+
|
|
559
|
+
```tsx
|
|
560
|
+
<SignIn
|
|
561
|
+
appearance={{
|
|
562
|
+
container: {
|
|
563
|
+
background: 'linear-gradient(to right, #4f46e5, #7c3aed)'
|
|
564
|
+
},
|
|
565
|
+
form: {
|
|
566
|
+
padding: '3rem',
|
|
567
|
+
borderRadius: '16px'
|
|
568
|
+
},
|
|
569
|
+
button: {
|
|
570
|
+
background: '#4f46e5',
|
|
571
|
+
color: 'white'
|
|
572
|
+
}
|
|
573
|
+
}}
|
|
574
|
+
/>
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
**Option 3: Override CSS Classes**
|
|
578
|
+
|
|
579
|
+
All components use semantic CSS class names prefixed with `insforge-`:
|
|
580
|
+
|
|
581
|
+
```css
|
|
582
|
+
/* In your global CSS file */
|
|
583
|
+
|
|
584
|
+
/* Customize the primary button */
|
|
585
|
+
.insforge-btn-primary {
|
|
586
|
+
background: #8b5cf6;
|
|
587
|
+
border-radius: 12px;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
/* Customize form inputs */
|
|
591
|
+
.insforge-input {
|
|
592
|
+
border-color: #e0e0e0;
|
|
593
|
+
border-radius: 8px;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/* Customize the auth card */
|
|
597
|
+
.insforge-auth-card {
|
|
598
|
+
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.15);
|
|
599
|
+
}
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
**Available CSS Classes:**
|
|
603
|
+
- `.insforge-auth-container` - Main container
|
|
604
|
+
- `.insforge-auth-card` - Form card
|
|
605
|
+
- `.insforge-input` - Input fields
|
|
606
|
+
- `.insforge-btn-primary` - Primary buttons
|
|
607
|
+
- `.insforge-oauth-btn` - OAuth buttons
|
|
608
|
+
- `.insforge-user-button` - User menu button
|
|
609
|
+
- `.insforge-error-banner` - Error messages
|
|
610
|
+
|
|
611
|
+
---
|
|
612
|
+
|
|
613
|
+
## Advanced Usage
|
|
614
|
+
|
|
615
|
+
### Role-Based Access Control
|
|
616
|
+
|
|
617
|
+
```tsx
|
|
618
|
+
<Protect condition={(user) => user.role === 'admin'}>
|
|
619
|
+
<AdminDashboard />
|
|
620
|
+
</Protect>
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
### Custom Auth Callbacks
|
|
624
|
+
|
|
625
|
+
```tsx
|
|
626
|
+
<AuthProvider
|
|
627
|
+
baseUrl={baseUrl}
|
|
628
|
+
onAuthChange={(user) => {
|
|
629
|
+
if (user) {
|
|
630
|
+
analytics.identify(user.id);
|
|
631
|
+
} else {
|
|
632
|
+
analytics.reset();
|
|
633
|
+
}
|
|
634
|
+
}}
|
|
635
|
+
>
|
|
636
|
+
{children}
|
|
637
|
+
</AuthProvider>
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
### Server-Side User Data
|
|
641
|
+
|
|
642
|
+
```tsx
|
|
643
|
+
// app/dashboard/page.tsx
|
|
644
|
+
import { headers } from 'next/headers';
|
|
645
|
+
import { getAuthUser } from '@insforge/nextjs/middleware';
|
|
646
|
+
import { createClient } from '@insforge/sdk';
|
|
647
|
+
|
|
648
|
+
export default async function Dashboard() {
|
|
649
|
+
const userId = getAuthUser(headers());
|
|
650
|
+
|
|
651
|
+
const insforge = createClient({
|
|
652
|
+
baseUrl: process.env.INSFORGE_BASE_URL!
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
const user = await insforge.auth.getUserById(userId);
|
|
656
|
+
|
|
657
|
+
return <div>Welcome {user.email}</div>;
|
|
658
|
+
}
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
---
|
|
662
|
+
|
|
663
|
+
## Environment Variables
|
|
664
|
+
|
|
665
|
+
Create a `.env.local` file:
|
|
666
|
+
|
|
667
|
+
```env
|
|
668
|
+
# Public (accessible in browser)
|
|
669
|
+
NEXT_PUBLIC_INSFORGE_BASE_URL=https://your-backend.insforge.app
|
|
670
|
+
|
|
671
|
+
# Server-only (middleware)
|
|
672
|
+
INSFORGE_BASE_URL=https://your-backend.insforge.app
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
---
|
|
676
|
+
|
|
677
|
+
## Why @insforge/nextjs?
|
|
678
|
+
|
|
679
|
+
### For Developers
|
|
680
|
+
- **Faster Development**: Drop-in components save hours of auth implementation
|
|
681
|
+
- **Best Practices**: Built-in security, session management, and error handling
|
|
682
|
+
- **Flexible**: Fully customizable while providing sensible defaults
|
|
683
|
+
- **Explicit Configuration**: OAuth providers passed as props - no hidden API calls or magic
|
|
684
|
+
|
|
685
|
+
### For AI Agents
|
|
686
|
+
- **Reduced Token Usage**: Pre-built components mean agents don't rebuild UI each time
|
|
687
|
+
- **Consistent UX**: Standard patterns across projects
|
|
688
|
+
- **Less Context**: Import and use instead of implementing from scratch
|
|
689
|
+
- **Discoverable**: Query `/api/metadata/auth` via MCP to get OAuth config, then generate code with correct `providers` prop
|
|
690
|
+
|
|
691
|
+
### AI Agent Workflow with Insforge MCP
|
|
692
|
+
|
|
693
|
+
When building Next.js apps with authentication:
|
|
694
|
+
|
|
695
|
+
1. **Connect to Insforge MCP** - Access the user's Insforge backend
|
|
696
|
+
2. **Query metadata** - Call `get-backend-metadata` to get OAuth configuration
|
|
697
|
+
3. **Generate code** - Write auth pages with explicit `providers={['github', 'google']}` based on backend config
|
|
698
|
+
4. **User sees OAuth buttons** - Components render buttons for configured providers
|
|
699
|
+
|
|
700
|
+
**Example MCP Response:**
|
|
701
|
+
```json
|
|
702
|
+
{
|
|
703
|
+
"auth": {
|
|
704
|
+
"oauths": [
|
|
705
|
+
{ "provider": "github", "clientId": "..." },
|
|
706
|
+
{ "provider": "google", "clientId": "..." }
|
|
707
|
+
]
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
**Agent generates:**
|
|
713
|
+
```tsx
|
|
714
|
+
<SignIn
|
|
715
|
+
baseUrl={process.env.NEXT_PUBLIC_INSFORGE_BASE_URL!}
|
|
716
|
+
providers={['github', 'google']} // From metadata
|
|
717
|
+
afterSignInUrl="/dashboard"
|
|
718
|
+
/>
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
---
|
|
722
|
+
|
|
723
|
+
## TypeScript
|
|
724
|
+
|
|
725
|
+
Full TypeScript support with exported types:
|
|
726
|
+
|
|
727
|
+
```tsx
|
|
728
|
+
import type {
|
|
729
|
+
InsforgeUser,
|
|
730
|
+
InsforgeSession,
|
|
731
|
+
AuthContextValue,
|
|
732
|
+
SignInProps,
|
|
733
|
+
ProtectProps
|
|
734
|
+
} from '@insforge/nextjs';
|
|
735
|
+
```
|
|
736
|
+
|
|
737
|
+
---
|
|
738
|
+
|
|
739
|
+
## Examples
|
|
740
|
+
|
|
741
|
+
Check out the `/examples` directory for:
|
|
742
|
+
- Basic Next.js App Router setup
|
|
743
|
+
- Role-based access control
|
|
744
|
+
- Custom styling
|
|
745
|
+
- Server-side data fetching
|
|
746
|
+
|
|
747
|
+
---
|
|
748
|
+
|
|
749
|
+
## License
|
|
750
|
+
|
|
751
|
+
MIT
|
|
752
|
+
|
|
753
|
+
---
|
|
754
|
+
|
|
755
|
+
## Support
|
|
756
|
+
|
|
757
|
+
- Documentation: https://docs.insforge.app
|
|
758
|
+
- Issues: https://github.com/insforge/nextjs/issues
|
|
759
|
+
- Discord: https://discord.gg/insforge
|