@insforge/nextjs 0.7.5 → 0.7.7
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 +394 -202
- package/dist/index.d.mts +69 -59
- package/dist/index.d.ts +69 -59
- package/dist/index.js +165 -155
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +164 -154
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,25 +1,16 @@
|
|
|
1
1
|
# @insforge/nextjs
|
|
2
2
|
|
|
3
|
-
**Zero-configuration authentication for Next.js** using Insforge backend.
|
|
3
|
+
**Zero-configuration authentication for Next.js** using Insforge backend. Get production-ready auth in 5 minutes.
|
|
4
4
|
|
|
5
|
-
## Why
|
|
6
|
-
|
|
7
|
-
✅ **Zero UI Code** - No SignIn/SignUp pages to create
|
|
8
|
-
✅ **5-Minute Setup** - Provider + API route + callback page = done
|
|
9
|
-
✅ **Production Ready** - Battle-tested auth UI maintained by Insforge
|
|
10
|
-
✅ **Auto OAuth** - 11 providers configured automatically from backend
|
|
11
|
-
✅ **AI-Friendly** - Minimal code generation = fewer tokens
|
|
12
|
-
|
|
13
|
-
**Use custom components only if you need highly customized branding or custom fields.**
|
|
5
|
+
## Why @insforge/nextjs?
|
|
14
6
|
|
|
15
|
-
|
|
7
|
+
✅ **Built-in Auth Pages** - Backend-hosted UI, no React code needed
|
|
8
|
+
✅ **5-Minute Setup** - Provider + API route + callback + middleware = done
|
|
9
|
+
✅ **Full SSR Support** - Works with Next.js App Router and Server Components
|
|
10
|
+
✅ **Auto OAuth** - 11 providers configured from backend automatically
|
|
11
|
+
✅ **TypeScript First** - Complete type safety out of the box
|
|
16
12
|
|
|
17
|
-
|
|
18
|
-
- **OAuth Support**: Google, GitHub, Discord, Facebook, LinkedIn, Microsoft, Apple, X, Instagram, TikTok, Spotify
|
|
19
|
-
- **React Hooks**: `useAuth()`, `useUser()`, `useSession()`
|
|
20
|
-
- **Control Components**: `<SignedIn>`, `<SignedOut>`, `<Protect>`
|
|
21
|
-
- **Next.js Middleware**: Server-side route protection
|
|
22
|
-
- **TypeScript**: Full type safety
|
|
13
|
+
**Need custom UI?** Scroll to [Advanced Usage](#advanced-usage) for custom components and styling.
|
|
23
14
|
|
|
24
15
|
## Installation
|
|
25
16
|
|
|
@@ -27,13 +18,15 @@
|
|
|
27
18
|
npm install @insforge/nextjs @insforge/sdk
|
|
28
19
|
```
|
|
29
20
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
21
|
+
> **Note**: `@insforge/sdk` is required for API calls, token management, and OAuth handling.
|
|
22
|
+
|
|
23
|
+
---
|
|
33
24
|
|
|
34
25
|
## Quick Start
|
|
35
26
|
|
|
36
|
-
### 1.
|
|
27
|
+
### 1. Setup Provider
|
|
28
|
+
|
|
29
|
+
Wrap your app with `InsforgeProvider` in the root layout:
|
|
37
30
|
|
|
38
31
|
```tsx
|
|
39
32
|
// app/layout.tsx
|
|
@@ -52,9 +45,15 @@ export default function RootLayout({ children }: { children: React.ReactNode })
|
|
|
52
45
|
}
|
|
53
46
|
```
|
|
54
47
|
|
|
55
|
-
|
|
48
|
+
**Props:**
|
|
49
|
+
- `baseUrl` (required): Your Insforge backend URL
|
|
50
|
+
- `onAuthChange` (optional): Callback when auth state changes
|
|
51
|
+
|
|
52
|
+
> **Auto-styled**: Component styles are automatically injected. No CSS imports needed!
|
|
53
|
+
|
|
54
|
+
### 2. Create API Route
|
|
56
55
|
|
|
57
|
-
|
|
56
|
+
Create an API route to sync tokens to HTTP-only cookies (enables SSR):
|
|
58
57
|
|
|
59
58
|
```tsx
|
|
60
59
|
// app/api/auth/route.ts
|
|
@@ -70,98 +69,67 @@ export const GET = handlers.GET;
|
|
|
70
69
|
export const DELETE = handlers.DELETE;
|
|
71
70
|
```
|
|
72
71
|
|
|
73
|
-
|
|
72
|
+
**What it does:**
|
|
73
|
+
- `POST /api/auth` - Syncs localStorage token to HTTP-only cookie
|
|
74
|
+
- `GET /api/auth` - Retrieves user data server-side
|
|
75
|
+
- `DELETE /api/auth` - Clears auth cookie on sign out
|
|
74
76
|
|
|
75
|
-
### 3. Create
|
|
77
|
+
### 3. Create Callback Page
|
|
76
78
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
79
|
+
Create a callback page to complete the authentication flow. Use the `<InsforgeCallback>` component that handles everything automatically:
|
|
80
|
+
|
|
81
|
+
- Detects authentication errors from backend
|
|
82
|
+
- Validates authentication tokens
|
|
83
|
+
- Syncs tokens to HTTP-only cookies for SSR
|
|
84
|
+
- Redirects to the destination page
|
|
80
85
|
|
|
81
86
|
```tsx
|
|
82
87
|
// app/auth/callback/page.tsx
|
|
83
88
|
'use client';
|
|
84
89
|
|
|
85
|
-
import {
|
|
86
|
-
import { useRouter, useSearchParams } from 'next/navigation';
|
|
87
|
-
import { createClient } from '@insforge/sdk';
|
|
88
|
-
|
|
89
|
-
function CallbackContent() {
|
|
90
|
-
const router = useRouter();
|
|
91
|
-
const searchParams = useSearchParams();
|
|
92
|
-
const isProcessingRef = useRef(false);
|
|
93
|
-
|
|
94
|
-
useEffect(() => {
|
|
95
|
-
const processCallback = async () => {
|
|
96
|
-
if (isProcessingRef.current) return;
|
|
97
|
-
isProcessingRef.current = true;
|
|
98
|
-
|
|
99
|
-
const error = searchParams.get('error');
|
|
100
|
-
if (error) {
|
|
101
|
-
router.push('/?error=' + encodeURIComponent(error));
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Create SDK client - it automatically detects and stores URL parameters
|
|
106
|
-
// SDK's detectOAuthCallback() handles: access_token, user_id, email, name
|
|
107
|
-
// This automatically stores token in 'insforge-auth-token' and basic user info
|
|
108
|
-
const insforge = createClient({
|
|
109
|
-
baseUrl: process.env.NEXT_PUBLIC_INSFORGE_BASE_URL!,
|
|
110
|
-
});
|
|
90
|
+
import { InsforgeCallback } from '@insforge/nextjs';
|
|
111
91
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
router.push('/?error=authentication_failed');
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Store complete user profile for InsforgeProvider (enables instant UI updates)
|
|
121
|
-
localStorage.setItem('insforge-user-profile', JSON.stringify(userData));
|
|
122
|
-
|
|
123
|
-
// Sync token to HTTP-only cookie (for server-side middleware)
|
|
124
|
-
const token = localStorage.getItem('insforge-auth-token');
|
|
125
|
-
if (token) {
|
|
126
|
-
await fetch('/api/auth', {
|
|
127
|
-
method: 'POST',
|
|
128
|
-
headers: { 'Content-Type': 'application/json' },
|
|
129
|
-
body: JSON.stringify({ action: 'sync-token', token }),
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Clean up URL and redirect
|
|
134
|
-
window.history.replaceState({}, '', '/auth/callback');
|
|
135
|
-
const destination = sessionStorage.getItem('auth_destination') || '/';
|
|
136
|
-
sessionStorage.removeItem('auth_destination');
|
|
137
|
-
setTimeout(() => router.push(destination), 100);
|
|
138
|
-
};
|
|
92
|
+
export default function CallbackPage() {
|
|
93
|
+
return <InsforgeCallback />;
|
|
94
|
+
}
|
|
95
|
+
```
|
|
139
96
|
|
|
140
|
-
|
|
141
|
-
}, [searchParams, router]);
|
|
97
|
+
**Optional: Custom loading UI**
|
|
142
98
|
|
|
99
|
+
```tsx
|
|
100
|
+
export default function CallbackPage() {
|
|
143
101
|
return (
|
|
144
|
-
<
|
|
145
|
-
|
|
146
|
-
<
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
102
|
+
<InsforgeCallback
|
|
103
|
+
loadingComponent={
|
|
104
|
+
<div className="flex items-center justify-center min-h-screen">
|
|
105
|
+
<div className="text-center">
|
|
106
|
+
<h2 className="text-2xl font-semibold mb-4">Signing you in...</h2>
|
|
107
|
+
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto"></div>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
}
|
|
111
|
+
/>
|
|
150
112
|
);
|
|
151
113
|
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**Optional: Custom redirect and callbacks**
|
|
152
117
|
|
|
118
|
+
```tsx
|
|
153
119
|
export default function CallbackPage() {
|
|
154
120
|
return (
|
|
155
|
-
<
|
|
156
|
-
|
|
157
|
-
|
|
121
|
+
<InsforgeCallback
|
|
122
|
+
redirectTo="/dashboard"
|
|
123
|
+
onSuccess={() => console.log('Authentication successful!')}
|
|
124
|
+
onError={(error) => console.error('Authentication failed:', error)}
|
|
125
|
+
/>
|
|
158
126
|
);
|
|
159
127
|
}
|
|
160
128
|
```
|
|
161
129
|
|
|
162
|
-
|
|
130
|
+
### 4. Setup Middleware
|
|
163
131
|
|
|
164
|
-
|
|
132
|
+
Protect routes with middleware:
|
|
165
133
|
|
|
166
134
|
```ts
|
|
167
135
|
// middleware.ts
|
|
@@ -178,7 +146,12 @@ export const config = {
|
|
|
178
146
|
};
|
|
179
147
|
```
|
|
180
148
|
|
|
181
|
-
|
|
149
|
+
**What it does:**
|
|
150
|
+
- Redirects unauthenticated users to backend auth pages
|
|
151
|
+
- Verifies tokens server-side
|
|
152
|
+
- Allows public routes without auth
|
|
153
|
+
|
|
154
|
+
### 5. Use Hooks & Components
|
|
182
155
|
|
|
183
156
|
```tsx
|
|
184
157
|
// app/page.tsx
|
|
@@ -188,6 +161,7 @@ export default function Home() {
|
|
|
188
161
|
return (
|
|
189
162
|
<div>
|
|
190
163
|
<SignedOut>
|
|
164
|
+
{/* Clicking will redirect to backend auth page */}
|
|
191
165
|
<a href="/sign-in">Sign In</a>
|
|
192
166
|
</SignedOut>
|
|
193
167
|
|
|
@@ -200,152 +174,256 @@ export default function Home() {
|
|
|
200
174
|
}
|
|
201
175
|
```
|
|
202
176
|
|
|
203
|
-
**
|
|
177
|
+
**Available Components:**
|
|
178
|
+
- `<SignedIn>` - Shows children only when authenticated
|
|
179
|
+
- `<SignedOut>` - Shows children only when not authenticated
|
|
180
|
+
- `<UserButton>` - User profile button with dropdown
|
|
181
|
+
- `<Protect>` - Conditional rendering with custom logic
|
|
182
|
+
|
|
183
|
+
**Available Hooks:**
|
|
184
|
+
|
|
185
|
+
```tsx
|
|
186
|
+
import { useAuth, useUser } from '@insforge/nextjs';
|
|
187
|
+
|
|
188
|
+
function Component() {
|
|
189
|
+
const { signIn, signUp, signOut, isSignedIn, isLoaded } = useAuth();
|
|
190
|
+
const { user, updateUser } = useUser();
|
|
191
|
+
|
|
192
|
+
return <div>Email: {user?.email}</div>;
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**That's it!** 🎉 Your app now has production-ready authentication.
|
|
204
197
|
|
|
205
198
|
---
|
|
206
199
|
|
|
207
200
|
## How It Works
|
|
208
201
|
|
|
209
202
|
```
|
|
210
|
-
1. User clicks "Sign In" → Middleware
|
|
203
|
+
1. User clicks "Sign In" → Middleware redirects to backend
|
|
211
204
|
↓
|
|
212
|
-
2.
|
|
205
|
+
2. User signs in on backend-hosted page (https://backend.insforge.app/auth/signin)
|
|
213
206
|
↓
|
|
214
|
-
3.
|
|
207
|
+
3. Backend redirects: yourapp.com/auth/callback?access_token=xxx&user_id=xxx...
|
|
215
208
|
↓
|
|
216
|
-
4.
|
|
209
|
+
4. <InsforgeCallback> component:
|
|
210
|
+
- SDK auto-detects and stores token
|
|
211
|
+
- Reloads auth state in provider
|
|
212
|
+
- Syncs token to HTTP-only cookie
|
|
213
|
+
- Redirects to destination
|
|
217
214
|
↓
|
|
218
|
-
5.
|
|
215
|
+
5. User sees dashboard with authenticated state
|
|
219
216
|
```
|
|
220
217
|
|
|
221
|
-
**
|
|
222
|
-
- **
|
|
223
|
-
- **
|
|
218
|
+
**Two-Storage Architecture:**
|
|
219
|
+
- **localStorage**: Client-side token access (hooks, components, SDK)
|
|
220
|
+
- **HTTP-only cookie**: Server-side token access (middleware, SSR)
|
|
224
221
|
|
|
225
222
|
---
|
|
226
223
|
|
|
227
224
|
## Local Development
|
|
228
225
|
|
|
229
|
-
|
|
226
|
+
During local development, backend typically runs on a different port:
|
|
230
227
|
|
|
231
228
|
```bash
|
|
232
229
|
# .env.local
|
|
233
|
-
NEXT_PUBLIC_INSFORGE_BASE_URL=http://localhost:7130
|
|
234
|
-
|
|
235
|
-
INSFORGE_BASE_URL=http://localhost:7130 # For middleware
|
|
230
|
+
NEXT_PUBLIC_INSFORGE_BASE_URL=http://localhost:7130 # Backend API URL
|
|
231
|
+
INSFORGE_BASE_URL=http://localhost:7130 # Backend API URL (for server-side)
|
|
236
232
|
```
|
|
237
233
|
|
|
238
|
-
|
|
234
|
+
Both variables should point to your Insforge backend URL.
|
|
239
235
|
|
|
240
236
|
---
|
|
241
237
|
|
|
242
|
-
|
|
238
|
+
## Advanced Usage
|
|
243
239
|
|
|
244
|
-
|
|
240
|
+
### Custom Auth Components
|
|
245
241
|
|
|
246
|
-
|
|
242
|
+
Want custom branding or additional fields? Create custom auth pages:
|
|
247
243
|
|
|
248
|
-
|
|
244
|
+
```tsx
|
|
245
|
+
// app/sign-in/page.tsx
|
|
246
|
+
'use client';
|
|
249
247
|
|
|
250
|
-
|
|
248
|
+
import { SignIn } from '@insforge/nextjs';
|
|
249
|
+
import { useRouter } from 'next/navigation';
|
|
251
250
|
|
|
252
|
-
|
|
253
|
-
|
|
251
|
+
export default function SignInPage() {
|
|
252
|
+
const router = useRouter();
|
|
254
253
|
|
|
255
|
-
function Component() {
|
|
256
|
-
const { signIn, signUp, signOut, isSignedIn, isLoaded } = useAuth();
|
|
257
|
-
|
|
258
|
-
async function handleSignIn(email: string, password: string) {
|
|
259
|
-
try {
|
|
260
|
-
await signIn(email, password); // Uses SDK internally
|
|
261
|
-
} catch (error) {
|
|
262
|
-
console.error('Sign in failed:', error);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
254
|
return (
|
|
267
|
-
<div>
|
|
268
|
-
|
|
255
|
+
<div className="min-h-screen flex items-center justify-center bg-gray-50">
|
|
256
|
+
<SignIn
|
|
257
|
+
afterSignInUrl="/dashboard"
|
|
258
|
+
onSuccess={(user) => {
|
|
259
|
+
console.log('Signed in:', user);
|
|
260
|
+
router.push('/dashboard');
|
|
261
|
+
}}
|
|
262
|
+
onError={(error) => {
|
|
263
|
+
console.error('Sign in error:', error);
|
|
264
|
+
}}
|
|
265
|
+
/>
|
|
269
266
|
</div>
|
|
270
267
|
);
|
|
271
268
|
}
|
|
272
269
|
```
|
|
273
270
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
271
|
+
```tsx
|
|
272
|
+
// app/sign-up/page.tsx
|
|
273
|
+
'use client';
|
|
277
274
|
|
|
278
|
-
|
|
275
|
+
import { SignUp } from '@insforge/nextjs';
|
|
279
276
|
|
|
280
|
-
|
|
281
|
-
import { useUser } from '@insforge/nextjs';
|
|
282
|
-
|
|
283
|
-
function UserProfile() {
|
|
284
|
-
const { user, isLoaded, updateUser } = useUser();
|
|
285
|
-
|
|
286
|
-
if (!isLoaded) return <div>Loading...</div>;
|
|
287
|
-
if (!user) return <div>Not signed in</div>;
|
|
288
|
-
|
|
277
|
+
export default function SignUpPage() {
|
|
289
278
|
return (
|
|
290
|
-
<div>
|
|
291
|
-
<
|
|
292
|
-
{user.nickname && <p>Nickname: {user.nickname}</p>}
|
|
279
|
+
<div className="min-h-screen flex items-center justify-center bg-gray-50">
|
|
280
|
+
<SignUp afterSignUpUrl="/onboarding" />
|
|
293
281
|
</div>
|
|
294
282
|
);
|
|
295
283
|
}
|
|
296
284
|
```
|
|
297
285
|
|
|
298
|
-
**
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
286
|
+
**SignIn Props:**
|
|
287
|
+
- `afterSignInUrl` - Redirect URL after successful sign in
|
|
288
|
+
- `title` / `subtitle` - Custom heading text
|
|
289
|
+
- `emailLabel` / `passwordLabel` - Custom field labels
|
|
290
|
+
- `submitButtonText` / `loadingButtonText` - Custom button text
|
|
291
|
+
- `appearance` - Tailwind CSS classes for styling
|
|
292
|
+
- `onSuccess` / `onError` - Callback functions
|
|
303
293
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
function SessionInfo() {
|
|
308
|
-
const { session, isLoaded } = useSession();
|
|
309
|
-
|
|
310
|
-
if (!isLoaded) return <div>Loading...</div>;
|
|
311
|
-
if (!session) return <div>No active session</div>;
|
|
312
|
-
|
|
313
|
-
return <div>User ID: {session.userId}</div>;
|
|
314
|
-
}
|
|
315
|
-
```
|
|
294
|
+
**SignUp Props:** Same as SignIn, plus:
|
|
295
|
+
- `afterSignUpUrl` - Redirect URL after successful sign up
|
|
296
|
+
- Additional text customization options
|
|
316
297
|
|
|
317
|
-
|
|
298
|
+
### Styling Components
|
|
318
299
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
#### `<SignIn>` / `<SignUp>`
|
|
322
|
-
|
|
323
|
-
Pre-built auth forms (only with `useBuiltInAuth={false}`):
|
|
300
|
+
All components support an `appearance` prop for Tailwind CSS customization:
|
|
324
301
|
|
|
325
302
|
```tsx
|
|
326
|
-
<SignIn
|
|
327
|
-
|
|
303
|
+
<SignIn
|
|
304
|
+
appearance={{
|
|
305
|
+
containerClassName: "shadow-2xl",
|
|
306
|
+
buttonClassName: "bg-blue-600 hover:bg-blue-700"
|
|
307
|
+
}}
|
|
308
|
+
/>
|
|
309
|
+
|
|
310
|
+
<UserButton
|
|
311
|
+
mode="detailed"
|
|
312
|
+
appearance={{
|
|
313
|
+
containerClassName: "ml-auto",
|
|
314
|
+
buttonClassName: "hover:bg-gray-100",
|
|
315
|
+
nameClassName: "text-white",
|
|
316
|
+
emailClassName: "text-gray-300"
|
|
317
|
+
}}
|
|
318
|
+
/>
|
|
328
319
|
```
|
|
329
320
|
|
|
330
|
-
**
|
|
321
|
+
**UserButton Modes:**
|
|
322
|
+
- `detailed` (default): Shows avatar + name + email
|
|
323
|
+
- `simple`: Shows avatar only
|
|
324
|
+
|
|
325
|
+
### Building Custom Auth Forms
|
|
331
326
|
|
|
332
|
-
|
|
327
|
+
Use primitive components for complete customization:
|
|
333
328
|
|
|
334
329
|
```tsx
|
|
335
|
-
|
|
330
|
+
import {
|
|
331
|
+
AuthContainer,
|
|
332
|
+
AuthHeader,
|
|
333
|
+
AuthFormField,
|
|
334
|
+
AuthPasswordField,
|
|
335
|
+
AuthSubmitButton,
|
|
336
|
+
AuthDivider,
|
|
337
|
+
AuthOAuthProviders,
|
|
338
|
+
AuthLink,
|
|
339
|
+
cn,
|
|
340
|
+
} from '@insforge/nextjs';
|
|
341
|
+
|
|
342
|
+
function CustomSignIn() {
|
|
343
|
+
return (
|
|
344
|
+
<AuthContainer
|
|
345
|
+
appearance={{
|
|
346
|
+
containerClassName: "max-w-md",
|
|
347
|
+
cardClassName: "bg-white"
|
|
348
|
+
}}
|
|
349
|
+
>
|
|
350
|
+
<AuthHeader
|
|
351
|
+
title="Welcome Back"
|
|
352
|
+
subtitle="Sign in to continue"
|
|
353
|
+
appearance={{
|
|
354
|
+
titleClassName: "text-3xl text-blue-900"
|
|
355
|
+
}}
|
|
356
|
+
/>
|
|
357
|
+
|
|
358
|
+
<form onSubmit={handleSubmit}>
|
|
359
|
+
<AuthFormField
|
|
360
|
+
id="email"
|
|
361
|
+
type="email"
|
|
362
|
+
label="Email Address"
|
|
363
|
+
placeholder="you@example.com"
|
|
364
|
+
appearance={{
|
|
365
|
+
inputClassName: "border-blue-500 focus:ring-blue-500"
|
|
366
|
+
}}
|
|
367
|
+
/>
|
|
368
|
+
|
|
369
|
+
<AuthPasswordField
|
|
370
|
+
id="password"
|
|
371
|
+
label="Password"
|
|
372
|
+
emailAuthConfig={config}
|
|
373
|
+
appearance={{
|
|
374
|
+
inputClassName: "border-blue-500"
|
|
375
|
+
}}
|
|
376
|
+
/>
|
|
377
|
+
|
|
378
|
+
<AuthSubmitButton
|
|
379
|
+
isLoading={loading}
|
|
380
|
+
className="bg-blue-600 hover:bg-blue-700"
|
|
381
|
+
>
|
|
382
|
+
Sign In
|
|
383
|
+
</AuthSubmitButton>
|
|
384
|
+
</form>
|
|
385
|
+
|
|
386
|
+
<AuthDivider text="or" />
|
|
387
|
+
|
|
388
|
+
<AuthOAuthProviders
|
|
389
|
+
providers={['google', 'github']}
|
|
390
|
+
onClick={handleOAuth}
|
|
391
|
+
loading={oauthLoading}
|
|
392
|
+
appearance={{
|
|
393
|
+
buttonClassName: "hover:bg-gray-50"
|
|
394
|
+
}}
|
|
395
|
+
/>
|
|
396
|
+
|
|
397
|
+
<AuthLink
|
|
398
|
+
text="Don't have an account?"
|
|
399
|
+
linkText="Sign up"
|
|
400
|
+
href="/sign-up"
|
|
401
|
+
/>
|
|
402
|
+
</AuthContainer>
|
|
403
|
+
);
|
|
404
|
+
}
|
|
336
405
|
```
|
|
337
406
|
|
|
338
|
-
**
|
|
407
|
+
**Available Primitive Components:**
|
|
408
|
+
- `AuthContainer` - Main form container
|
|
409
|
+
- `AuthHeader` - Title and subtitle
|
|
410
|
+
- `AuthFormField` - Standard input field
|
|
411
|
+
- `AuthPasswordField` - Password field with visibility toggle
|
|
412
|
+
- `AuthPasswordStrengthIndicator` - Password requirement checklist
|
|
413
|
+
- `AuthSubmitButton` - Submit button with loading states
|
|
414
|
+
- `AuthErrorBanner` - Error message display
|
|
415
|
+
- `AuthDivider` - Visual separator
|
|
416
|
+
- `AuthOAuthProviders` - OAuth provider buttons grid
|
|
417
|
+
- `AuthOAuthButton` - Individual OAuth button
|
|
418
|
+
- `AuthVerificationCodeInput` - OTP/2FA code input
|
|
419
|
+
- `AuthLink` - Navigation link
|
|
420
|
+
- `AuthBranding` - Insforge branding footer
|
|
339
421
|
|
|
340
|
-
|
|
422
|
+
All components support `appearance` prop with specific `className` options for each element.
|
|
341
423
|
|
|
342
|
-
|
|
343
|
-
<SignedIn><Dashboard /></SignedIn>
|
|
344
|
-
<SignedOut><LandingPage /></SignedOut>
|
|
345
|
-
<Protect condition={(user) => user.role === 'admin'}><AdminPanel /></Protect>
|
|
346
|
-
```
|
|
424
|
+
### Server-Side Usage
|
|
347
425
|
|
|
348
|
-
|
|
426
|
+
Access auth data in Server Components:
|
|
349
427
|
|
|
350
428
|
```tsx
|
|
351
429
|
// app/dashboard/page.tsx (Server Component)
|
|
@@ -356,66 +434,180 @@ import { createClient } from '@insforge/sdk';
|
|
|
356
434
|
export default async function Dashboard() {
|
|
357
435
|
const userId = getAuthUserId(headers());
|
|
358
436
|
const token = getAuthToken(headers());
|
|
359
|
-
|
|
437
|
+
|
|
360
438
|
if (!userId) {
|
|
361
439
|
return <div>Not authenticated</div>;
|
|
362
440
|
}
|
|
363
|
-
|
|
364
|
-
// Use SDK
|
|
441
|
+
|
|
442
|
+
// Use SDK for server-side data fetching
|
|
365
443
|
const insforge = createClient({
|
|
366
444
|
baseUrl: process.env.INSFORGE_BASE_URL!,
|
|
367
445
|
edgeFunctionToken: token || undefined,
|
|
368
446
|
});
|
|
369
|
-
|
|
447
|
+
|
|
370
448
|
const { data: userData } = await insforge.auth.getCurrentUser();
|
|
371
|
-
|
|
449
|
+
|
|
372
450
|
return <div>Welcome, {userData?.user.email}</div>;
|
|
373
451
|
}
|
|
374
452
|
```
|
|
375
453
|
|
|
376
|
-
|
|
454
|
+
### Conditional Rendering
|
|
455
|
+
|
|
456
|
+
```tsx
|
|
457
|
+
import { Protect } from '@insforge/nextjs';
|
|
458
|
+
|
|
459
|
+
// Role-based access
|
|
460
|
+
<Protect condition={(user) => user.role === 'admin'}>
|
|
461
|
+
<AdminPanel />
|
|
462
|
+
</Protect>
|
|
377
463
|
|
|
378
|
-
|
|
464
|
+
// Custom logic
|
|
465
|
+
<Protect condition={(user) => user.emailVerified}>
|
|
466
|
+
<VerifiedUserFeature />
|
|
467
|
+
</Protect>
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
### Auth Change Callback
|
|
471
|
+
|
|
472
|
+
Track authentication events:
|
|
379
473
|
|
|
380
474
|
```tsx
|
|
381
|
-
<InsforgeProvider
|
|
475
|
+
<InsforgeProvider
|
|
382
476
|
baseUrl={baseUrl}
|
|
383
|
-
onAuthChange={(user) =>
|
|
477
|
+
onAuthChange={(user) => {
|
|
478
|
+
if (user) {
|
|
479
|
+
// User signed in
|
|
480
|
+
analytics.identify(user.id);
|
|
481
|
+
} else {
|
|
482
|
+
// User signed out
|
|
483
|
+
analytics.reset();
|
|
484
|
+
}
|
|
485
|
+
}}
|
|
384
486
|
/>
|
|
385
487
|
```
|
|
386
488
|
|
|
387
489
|
---
|
|
388
490
|
|
|
389
|
-
##
|
|
491
|
+
## API Reference
|
|
390
492
|
|
|
391
|
-
|
|
493
|
+
### InsforgeProvider Props
|
|
392
494
|
|
|
393
|
-
|
|
394
|
-
|
|
495
|
+
```tsx
|
|
496
|
+
interface InsforgeProviderProps {
|
|
497
|
+
baseUrl: string; // Insforge backend URL
|
|
498
|
+
onAuthChange?: (user: InsforgeUser | null) => void; // Auth state change callback
|
|
499
|
+
}
|
|
500
|
+
```
|
|
395
501
|
|
|
396
|
-
|
|
502
|
+
### InsforgeCallback Props
|
|
397
503
|
|
|
398
|
-
|
|
504
|
+
```tsx
|
|
505
|
+
interface InsforgeCallbackProps {
|
|
506
|
+
redirectTo?: string; // Custom redirect destination after auth
|
|
507
|
+
onSuccess?: () => void; // Callback fired on successful authentication
|
|
508
|
+
onError?: (error: string) => void; // Callback fired on authentication error
|
|
509
|
+
loadingComponent?: ReactNode; // Custom loading UI during authentication
|
|
510
|
+
}
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
**Default behavior:**
|
|
514
|
+
- Redirects to `sessionStorage['auth_destination']` or `sessionStorage['oauth_final_destination']` or `'/'`
|
|
515
|
+
- Displays a built-in loading spinner during authentication
|
|
516
|
+
- Handles errors by redirecting to `/?error=<error_message>`
|
|
517
|
+
|
|
518
|
+
### useAuth()
|
|
519
|
+
|
|
520
|
+
```tsx
|
|
521
|
+
const {
|
|
522
|
+
signIn, // (email: string, password: string) => Promise<void>
|
|
523
|
+
signUp, // (email: string, password: string) => Promise<void>
|
|
524
|
+
signOut, // () => Promise<void>
|
|
525
|
+
isSignedIn, // boolean
|
|
526
|
+
isLoaded, // boolean
|
|
527
|
+
} = useAuth();
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
### useUser()
|
|
531
|
+
|
|
532
|
+
```tsx
|
|
533
|
+
const {
|
|
534
|
+
user, // InsforgeUser | null
|
|
535
|
+
isLoaded, // boolean
|
|
536
|
+
updateUser, // (data: Partial<InsforgeUser>) => Promise<void>
|
|
537
|
+
} = useUser();
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
**InsforgeUser Type:**
|
|
541
|
+
```tsx
|
|
542
|
+
interface InsforgeUser {
|
|
543
|
+
id: string;
|
|
544
|
+
email: string;
|
|
545
|
+
nickname?: string;
|
|
546
|
+
avatarUrl?: string;
|
|
547
|
+
role?: string;
|
|
548
|
+
emailVerified?: boolean;
|
|
549
|
+
}
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
### Middleware Helpers
|
|
399
553
|
|
|
400
|
-
|
|
554
|
+
```tsx
|
|
555
|
+
import { getAuthUserId, getAuthToken } from '@insforge/nextjs/middleware';
|
|
556
|
+
|
|
557
|
+
const userId = getAuthUserId(headers()); // string | null
|
|
558
|
+
const token = getAuthToken(headers()); // string | null
|
|
559
|
+
```
|
|
401
560
|
|
|
402
|
-
|
|
561
|
+
### Utility Function
|
|
562
|
+
|
|
563
|
+
```tsx
|
|
564
|
+
import { cn } from '@insforge/nextjs';
|
|
565
|
+
|
|
566
|
+
// Merge Tailwind classes (uses clsx + tailwind-merge)
|
|
567
|
+
const className = cn("px-4 py-2", "bg-blue-500", conditionalClass);
|
|
568
|
+
```
|
|
403
569
|
|
|
404
570
|
---
|
|
405
571
|
|
|
406
572
|
## TypeScript
|
|
407
573
|
|
|
574
|
+
Full TypeScript support with exported types:
|
|
575
|
+
|
|
408
576
|
```tsx
|
|
409
|
-
import type {
|
|
577
|
+
import type {
|
|
578
|
+
InsforgeUser,
|
|
579
|
+
InsforgeCallbackProps,
|
|
580
|
+
SignInProps,
|
|
581
|
+
SignUpProps,
|
|
582
|
+
UserButtonProps,
|
|
583
|
+
ProtectProps,
|
|
584
|
+
OAuthProviderConfig,
|
|
585
|
+
} from '@insforge/nextjs';
|
|
410
586
|
```
|
|
411
587
|
|
|
412
588
|
---
|
|
413
589
|
|
|
590
|
+
## Why @insforge/nextjs?
|
|
591
|
+
|
|
592
|
+
**For Developers:**
|
|
593
|
+
- ⚡️ 5-minute setup vs hours of custom auth code
|
|
594
|
+
- 🔒 Production-ready security and session management
|
|
595
|
+
- 🎨 Customizable components when needed
|
|
596
|
+
- 🚀 Built-in SSR support for Next.js App Router
|
|
597
|
+
|
|
598
|
+
**For AI Agents:**
|
|
599
|
+
- 📝 Minimal code generation (5 files vs 20+ for custom auth)
|
|
600
|
+
- 🎯 Consistent patterns across projects
|
|
601
|
+
- 🤖 SDK-first approach reduces errors
|
|
602
|
+
- 💰 Less tokens used per setup
|
|
603
|
+
|
|
604
|
+
---
|
|
605
|
+
|
|
414
606
|
## Support
|
|
415
607
|
|
|
416
|
-
- **
|
|
417
|
-
- **Issues**: https://github.com/InsForge/InsForge/issues
|
|
418
|
-
- **Discord**: https://discord.com/invite/DvBtaEc9Jz
|
|
608
|
+
- **Documentation**: https://docs.insforge.dev
|
|
609
|
+
- **GitHub Issues**: https://github.com/InsForge/InsForge/issues
|
|
610
|
+
- **Discord Community**: https://discord.com/invite/DvBtaEc9Jz
|
|
419
611
|
|
|
420
612
|
## License
|
|
421
613
|
|