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