@insforge/sdk 1.4.3 → 1.4.5-beta.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 +124 -143
- package/SDK-REFERENCE.md +146 -168
- package/dist/{client-B6eZHolm.d.mts → client-BZyLCjO1.d.mts} +44 -120
- package/dist/{client-DUmOm_3W.d.ts → client-Dh7GOydb.d.ts} +44 -120
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +556 -423
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +559 -423
- package/dist/index.mjs.map +1 -1
- package/dist/ssr/middleware.js +71 -52
- package/dist/ssr/middleware.js.map +1 -1
- package/dist/ssr/middleware.mjs +71 -52
- package/dist/ssr/middleware.mjs.map +1 -1
- package/dist/ssr.d.mts +1 -1
- package/dist/ssr.d.ts +1 -1
- package/dist/ssr.js +651 -539
- package/dist/ssr.js.map +1 -1
- package/dist/ssr.mjs +655 -540
- package/dist/ssr.mjs.map +1 -1
- package/package.json +12 -5
package/README.md
CHANGED
|
@@ -33,11 +33,11 @@ yarn add @insforge/sdk
|
|
|
33
33
|
### Initialize the Client
|
|
34
34
|
|
|
35
35
|
```javascript
|
|
36
|
-
import { createClient } from
|
|
36
|
+
import { createClient } from '@insforge/sdk';
|
|
37
37
|
|
|
38
38
|
const insforge = createClient({
|
|
39
|
-
baseUrl:
|
|
40
|
-
anonKey:
|
|
39
|
+
baseUrl: 'http://localhost:7130', // Your InsForge backend URL
|
|
40
|
+
anonKey: 'your-anon-key', // Optional public anon key
|
|
41
41
|
});
|
|
42
42
|
```
|
|
43
43
|
|
|
@@ -46,10 +46,10 @@ const insforge = createClient({
|
|
|
46
46
|
Use `createAdminClient()` only in trusted server code that needs project-admin privileges:
|
|
47
47
|
|
|
48
48
|
```typescript
|
|
49
|
-
import { createAdminClient } from
|
|
49
|
+
import { createAdminClient } from '@insforge/sdk';
|
|
50
50
|
|
|
51
51
|
const admin = createAdminClient({
|
|
52
|
-
baseUrl:
|
|
52
|
+
baseUrl: 'http://localhost:7130',
|
|
53
53
|
apiKey: process.env.INSFORGE_API_KEY!,
|
|
54
54
|
});
|
|
55
55
|
```
|
|
@@ -62,7 +62,7 @@ In edge functions or other server code that receives a user's JWT, seed the clie
|
|
|
62
62
|
|
|
63
63
|
```javascript
|
|
64
64
|
const insforge = createClient({
|
|
65
|
-
baseUrl:
|
|
65
|
+
baseUrl: 'http://localhost:7130',
|
|
66
66
|
accessToken: userJwt, // e.g. from the request's Authorization header
|
|
67
67
|
});
|
|
68
68
|
```
|
|
@@ -74,22 +74,22 @@ All requests run as that user (RLS applies). The token is used as-is — the SDK
|
|
|
74
74
|
```javascript
|
|
75
75
|
// Sign up a new user
|
|
76
76
|
const { data, error } = await insforge.auth.signUp({
|
|
77
|
-
email:
|
|
78
|
-
password:
|
|
79
|
-
name:
|
|
80
|
-
redirectTo:
|
|
77
|
+
email: 'user@example.com',
|
|
78
|
+
password: 'securePassword123',
|
|
79
|
+
name: 'John Doe', // optional
|
|
80
|
+
redirectTo: 'http://localhost:3000/sign-in', // optional, recommended for link-based verification
|
|
81
81
|
});
|
|
82
82
|
|
|
83
83
|
// Sign in with email/password
|
|
84
84
|
const { data, error } = await insforge.auth.signInWithPassword({
|
|
85
|
-
email:
|
|
86
|
-
password:
|
|
85
|
+
email: 'user@example.com',
|
|
86
|
+
password: 'securePassword123',
|
|
87
87
|
});
|
|
88
88
|
|
|
89
89
|
// OAuth authentication (built-in or custom provider key)
|
|
90
|
-
await insforge.auth.signInWithOAuth(
|
|
91
|
-
redirectTo:
|
|
92
|
-
additionalParams: { prompt:
|
|
90
|
+
await insforge.auth.signInWithOAuth('google', {
|
|
91
|
+
redirectTo: 'http://localhost:3000/dashboard',
|
|
92
|
+
additionalParams: { prompt: 'select_account' },
|
|
93
93
|
});
|
|
94
94
|
// additionalParams is for provider-specific hints only.
|
|
95
95
|
// Do not pass client_id, scope, redirect_uri, code_challenge, state, or response_type;
|
|
@@ -99,13 +99,13 @@ await insforge.auth.signInWithOAuth("google", {
|
|
|
99
99
|
const { data: currentUser } = await insforge.auth.getCurrentUser();
|
|
100
100
|
|
|
101
101
|
// Get any user's profile by ID (public endpoint)
|
|
102
|
-
const { data: profile, error } = await insforge.auth.getProfile(
|
|
102
|
+
const { data: profile, error } = await insforge.auth.getProfile('user-id-here');
|
|
103
103
|
|
|
104
104
|
// Update current user's profile (requires authentication)
|
|
105
105
|
const { data: updatedProfile, error } = await insforge.auth.setProfile({
|
|
106
|
-
displayName:
|
|
107
|
-
bio:
|
|
108
|
-
avatarUrl:
|
|
106
|
+
displayName: 'John Doe',
|
|
107
|
+
bio: 'Software developer',
|
|
108
|
+
avatarUrl: 'https://example.com/avatar.jpg',
|
|
109
109
|
});
|
|
110
110
|
|
|
111
111
|
// Sign out
|
|
@@ -117,31 +117,31 @@ await insforge.auth.signOut();
|
|
|
117
117
|
```javascript
|
|
118
118
|
// Resend a verification email
|
|
119
119
|
await insforge.auth.resendVerificationEmail({
|
|
120
|
-
email:
|
|
121
|
-
redirectTo:
|
|
120
|
+
email: 'user@example.com',
|
|
121
|
+
redirectTo: 'http://localhost:3000/sign-in', // optional, recommended for link-based verification
|
|
122
122
|
});
|
|
123
123
|
|
|
124
124
|
// Verify email with a 6-digit code
|
|
125
125
|
await insforge.auth.verifyEmail({
|
|
126
|
-
email:
|
|
127
|
-
otp:
|
|
126
|
+
email: 'user@example.com',
|
|
127
|
+
otp: '123456',
|
|
128
128
|
});
|
|
129
129
|
|
|
130
130
|
// Send password reset email
|
|
131
131
|
await insforge.auth.sendResetPasswordEmail({
|
|
132
|
-
email:
|
|
133
|
-
redirectTo:
|
|
132
|
+
email: 'user@example.com',
|
|
133
|
+
redirectTo: 'http://localhost:3000/reset-password', // optional, recommended for link-based reset
|
|
134
134
|
});
|
|
135
135
|
|
|
136
136
|
// Code-based reset flow: exchange the code, then reset the password
|
|
137
137
|
const { data: resetToken } = await insforge.auth.exchangeResetPasswordToken({
|
|
138
|
-
email:
|
|
139
|
-
code:
|
|
138
|
+
email: 'user@example.com',
|
|
139
|
+
code: '123456',
|
|
140
140
|
});
|
|
141
141
|
|
|
142
142
|
if (resetToken) {
|
|
143
143
|
await insforge.auth.resetPassword({
|
|
144
|
-
newPassword:
|
|
144
|
+
newPassword: 'newSecurePassword123',
|
|
145
145
|
otp: resetToken.token,
|
|
146
146
|
});
|
|
147
147
|
}
|
|
@@ -163,26 +163,20 @@ Those backend endpoints validate the token first, then redirect the browser to y
|
|
|
163
163
|
```javascript
|
|
164
164
|
// Insert data
|
|
165
165
|
const { data, error } = await insforge.database
|
|
166
|
-
.from(
|
|
167
|
-
.insert([{ title:
|
|
166
|
+
.from('posts')
|
|
167
|
+
.insert([{ title: 'My First Post', content: 'Hello World!' }]);
|
|
168
168
|
|
|
169
169
|
// Query data
|
|
170
|
-
const { data, error } = await insforge.database
|
|
171
|
-
.from("posts")
|
|
172
|
-
.select("*")
|
|
173
|
-
.eq("author_id", userId);
|
|
170
|
+
const { data, error } = await insforge.database.from('posts').select('*').eq('author_id', userId);
|
|
174
171
|
|
|
175
172
|
// Update data
|
|
176
173
|
const { data, error } = await insforge.database
|
|
177
|
-
.from(
|
|
178
|
-
.update({ title:
|
|
179
|
-
.eq(
|
|
174
|
+
.from('posts')
|
|
175
|
+
.update({ title: 'Updated Title' })
|
|
176
|
+
.eq('id', postId);
|
|
180
177
|
|
|
181
178
|
// Delete data
|
|
182
|
-
const { data, error } = await insforge.database
|
|
183
|
-
.from("posts")
|
|
184
|
-
.delete()
|
|
185
|
-
.eq("id", postId);
|
|
179
|
+
const { data, error } = await insforge.database.from('posts').delete().eq('id', postId);
|
|
186
180
|
```
|
|
187
181
|
|
|
188
182
|
#### Selecting a schema
|
|
@@ -190,21 +184,18 @@ const { data, error } = await insforge.database
|
|
|
190
184
|
Queries hit the `public` schema by default. Target a custom schema by chaining `.schema()` — the table name stays bare (it maps to PostgREST's `Accept-Profile`/`Content-Profile` header):
|
|
191
185
|
|
|
192
186
|
```javascript
|
|
193
|
-
const { data } = await insforge.database
|
|
194
|
-
.schema("analytics")
|
|
195
|
-
.from("events")
|
|
196
|
-
.select("*");
|
|
187
|
+
const { data } = await insforge.database.schema('analytics').from('events').select('*');
|
|
197
188
|
|
|
198
|
-
await insforge.database.schema(
|
|
189
|
+
await insforge.database.schema('analytics').rpc('rollup', { day: '2026-01-01' });
|
|
199
190
|
```
|
|
200
191
|
|
|
201
192
|
Or set a default schema for every query when creating the client:
|
|
202
193
|
|
|
203
194
|
```javascript
|
|
204
195
|
const insforge = createClient({
|
|
205
|
-
baseUrl:
|
|
206
|
-
anonKey:
|
|
207
|
-
db: { schema:
|
|
196
|
+
baseUrl: '...',
|
|
197
|
+
anonKey: '...',
|
|
198
|
+
db: { schema: 'analytics' },
|
|
208
199
|
});
|
|
209
200
|
```
|
|
210
201
|
|
|
@@ -215,28 +206,24 @@ The schema must be exposed by the backend, and access is still gated by grants +
|
|
|
215
206
|
```javascript
|
|
216
207
|
// Upload a file
|
|
217
208
|
const file = document.querySelector('input[type="file"]').files[0];
|
|
218
|
-
const { data, error } = await insforge.storage.from(
|
|
209
|
+
const { data, error } = await insforge.storage.from('avatars').upload(file);
|
|
219
210
|
|
|
220
211
|
// Download a file
|
|
221
|
-
const { data, error } = await insforge.storage
|
|
222
|
-
.from("avatars")
|
|
223
|
-
.download("user-avatar.png");
|
|
212
|
+
const { data, error } = await insforge.storage.from('avatars').download('user-avatar.png');
|
|
224
213
|
|
|
225
214
|
// Delete a file
|
|
226
|
-
const { data, error } = await insforge.storage
|
|
227
|
-
.from("avatars")
|
|
228
|
-
.remove(["user-avatar.png"]);
|
|
215
|
+
const { data, error } = await insforge.storage.from('avatars').remove(['user-avatar.png']);
|
|
229
216
|
|
|
230
217
|
// List files
|
|
231
|
-
const { data, error } = await insforge.storage.from(
|
|
218
|
+
const { data, error } = await insforge.storage.from('avatars').list();
|
|
232
219
|
```
|
|
233
220
|
|
|
234
221
|
### Edge Functions
|
|
235
222
|
|
|
236
223
|
```javascript
|
|
237
224
|
// Invoke an edge function
|
|
238
|
-
const { data, error } = await insforge.functions.invoke(
|
|
239
|
-
body: { key:
|
|
225
|
+
const { data, error } = await insforge.functions.invoke('my-function', {
|
|
226
|
+
body: { key: 'value' },
|
|
240
227
|
});
|
|
241
228
|
```
|
|
242
229
|
|
|
@@ -244,60 +231,58 @@ const { data, error } = await insforge.functions.invoke("my-function", {
|
|
|
244
231
|
|
|
245
232
|
```javascript
|
|
246
233
|
// Create and redirect to a Stripe Checkout Session
|
|
247
|
-
const { data, error } = await insforge.payments.stripe.createCheckoutSession(
|
|
248
|
-
|
|
249
|
-
{
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
idempotencyKey: "cart_123",
|
|
255
|
-
},
|
|
256
|
-
);
|
|
234
|
+
const { data, error } = await insforge.payments.stripe.createCheckoutSession('test', {
|
|
235
|
+
mode: 'payment',
|
|
236
|
+
lineItems: [{ priceId: 'price_123', quantity: 1 }],
|
|
237
|
+
successUrl: `${window.location.origin}/success`,
|
|
238
|
+
cancelUrl: `${window.location.origin}/pricing`,
|
|
239
|
+
idempotencyKey: 'cart_123',
|
|
240
|
+
});
|
|
257
241
|
|
|
258
242
|
if (!error && data?.checkoutSession.url) {
|
|
259
243
|
window.location.assign(data.checkoutSession.url);
|
|
260
244
|
}
|
|
261
245
|
|
|
262
246
|
// Create a subscription checkout for an app billing subject
|
|
263
|
-
const { data: subscriptionCheckout } =
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
247
|
+
const { data: subscriptionCheckout } = await insforge.payments.stripe.createCheckoutSession(
|
|
248
|
+
'test',
|
|
249
|
+
{
|
|
250
|
+
mode: 'subscription',
|
|
251
|
+
subject: { type: 'team', id: 'team_123' },
|
|
252
|
+
lineItems: [{ priceId: 'price_monthly_123', quantity: 1 }],
|
|
268
253
|
successUrl: `${window.location.origin}/billing/success`,
|
|
269
254
|
cancelUrl: `${window.location.origin}/billing`,
|
|
270
|
-
}
|
|
255
|
+
}
|
|
256
|
+
);
|
|
271
257
|
|
|
272
258
|
if (subscriptionCheckout?.checkoutSession.url) {
|
|
273
259
|
window.location.assign(subscriptionCheckout.checkoutSession.url);
|
|
274
260
|
}
|
|
275
261
|
|
|
276
262
|
// Let an authenticated customer manage their subscription in Stripe Billing Portal
|
|
277
|
-
const { data: portal } =
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
});
|
|
263
|
+
const { data: portal } = await insforge.payments.stripe.createCustomerPortalSession('test', {
|
|
264
|
+
subject: { type: 'team', id: 'team_123' },
|
|
265
|
+
returnUrl: `${window.location.origin}/billing`,
|
|
266
|
+
});
|
|
282
267
|
|
|
283
268
|
if (portal?.customerPortalSession.url) {
|
|
284
269
|
window.location.assign(portal.customerPortalSession.url);
|
|
285
270
|
}
|
|
286
271
|
|
|
287
272
|
// Create a Razorpay order, then pass checkoutOptions to Razorpay Checkout.js
|
|
288
|
-
const { data: order } = await insforge.payments.razorpay.createOrder(
|
|
273
|
+
const { data: order } = await insforge.payments.razorpay.createOrder('test', {
|
|
289
274
|
amount: 200000,
|
|
290
|
-
currency:
|
|
291
|
-
subject: { type:
|
|
292
|
-
customerEmail:
|
|
293
|
-
notes: { order_id:
|
|
275
|
+
currency: 'INR',
|
|
276
|
+
subject: { type: 'team', id: 'team_123' },
|
|
277
|
+
customerEmail: 'ada@example.com',
|
|
278
|
+
notes: { order_id: 'order_123' },
|
|
294
279
|
});
|
|
295
280
|
|
|
296
281
|
if (order) {
|
|
297
282
|
const checkout = new Razorpay({
|
|
298
283
|
...order.checkoutOptions,
|
|
299
284
|
handler: async (response) => {
|
|
300
|
-
await insforge.payments.razorpay.verifyOrder(
|
|
285
|
+
await insforge.payments.razorpay.verifyOrder('test', {
|
|
301
286
|
orderId: response.razorpay_order_id,
|
|
302
287
|
paymentId: response.razorpay_payment_id,
|
|
303
288
|
signature: response.razorpay_signature,
|
|
@@ -308,13 +293,12 @@ if (order) {
|
|
|
308
293
|
}
|
|
309
294
|
|
|
310
295
|
// Create a Razorpay subscription checkout for an app billing subject
|
|
311
|
-
const { data: subscription } =
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
});
|
|
296
|
+
const { data: subscription } = await insforge.payments.razorpay.createSubscription('test', {
|
|
297
|
+
planId: 'plan_123',
|
|
298
|
+
totalCount: 12,
|
|
299
|
+
subject: { type: 'team', id: 'team_123' },
|
|
300
|
+
notes: { order_id: 'order_123' },
|
|
301
|
+
});
|
|
318
302
|
```
|
|
319
303
|
|
|
320
304
|
### AI Integration
|
|
@@ -324,32 +308,32 @@ AI methods return an OpenAI-like response object directly (not the `{ data, erro
|
|
|
324
308
|
```javascript
|
|
325
309
|
// Chat completion
|
|
326
310
|
const completion = await insforge.ai.chat.completions.create({
|
|
327
|
-
model:
|
|
328
|
-
messages: [{ role:
|
|
311
|
+
model: 'anthropic/claude-3.5-haiku',
|
|
312
|
+
messages: [{ role: 'user', content: 'Write a hello world program' }],
|
|
329
313
|
});
|
|
330
314
|
console.log(completion.choices[0].message.content);
|
|
331
315
|
|
|
332
316
|
// Streaming chat — returns an async iterable of chunks
|
|
333
317
|
const stream = await insforge.ai.chat.completions.create({
|
|
334
|
-
model:
|
|
335
|
-
messages: [{ role:
|
|
318
|
+
model: 'anthropic/claude-3.5-haiku',
|
|
319
|
+
messages: [{ role: 'user', content: 'Tell me a story' }],
|
|
336
320
|
stream: true,
|
|
337
321
|
});
|
|
338
322
|
for await (const chunk of stream) {
|
|
339
|
-
process.stdout.write(chunk.choices[0]?.delta?.content ??
|
|
323
|
+
process.stdout.write(chunk.choices[0]?.delta?.content ?? '');
|
|
340
324
|
}
|
|
341
325
|
|
|
342
326
|
// Generate an image — returns base64-encoded image data
|
|
343
327
|
const image = await insforge.ai.images.generate({
|
|
344
|
-
model:
|
|
345
|
-
prompt:
|
|
328
|
+
model: 'google/gemini-3-pro-image-preview',
|
|
329
|
+
prompt: 'A sunset over mountains',
|
|
346
330
|
});
|
|
347
331
|
const base64Png = image.data[0].b64_json;
|
|
348
332
|
|
|
349
333
|
// Create embeddings
|
|
350
334
|
const embeddings = await insforge.ai.embeddings.create({
|
|
351
|
-
model:
|
|
352
|
-
input:
|
|
335
|
+
model: 'openai/text-embedding-3-small',
|
|
336
|
+
input: 'Hello world',
|
|
353
337
|
});
|
|
354
338
|
console.log(embeddings.data[0].embedding); // number[]
|
|
355
339
|
```
|
|
@@ -367,8 +351,8 @@ The SDK supports the following configuration options:
|
|
|
367
351
|
|
|
368
352
|
```javascript
|
|
369
353
|
const insforge = createClient({
|
|
370
|
-
baseUrl:
|
|
371
|
-
anonKey:
|
|
354
|
+
baseUrl: 'http://localhost:7130', // Your InsForge backend URL
|
|
355
|
+
anonKey: 'your-anon-key', // Optional
|
|
372
356
|
});
|
|
373
357
|
```
|
|
374
358
|
|
|
@@ -386,7 +370,7 @@ By default, the SSR helpers use:
|
|
|
386
370
|
|
|
387
371
|
```typescript
|
|
388
372
|
// app/lib/insforge/client.ts
|
|
389
|
-
import { createBrowserClient } from
|
|
373
|
+
import { createBrowserClient } from '@insforge/sdk/ssr';
|
|
390
374
|
|
|
391
375
|
export const insforge = createBrowserClient();
|
|
392
376
|
```
|
|
@@ -398,8 +382,8 @@ server so the app can write server-owned auth cookies.
|
|
|
398
382
|
|
|
399
383
|
```typescript
|
|
400
384
|
// app/lib/insforge/server.ts
|
|
401
|
-
import { cookies } from
|
|
402
|
-
import { createServerClient } from
|
|
385
|
+
import { cookies } from 'next/headers';
|
|
386
|
+
import { createServerClient } from '@insforge/sdk/ssr';
|
|
403
387
|
|
|
404
388
|
export async function createInsForgeServerClient() {
|
|
405
389
|
return createServerClient({ cookies: await cookies() });
|
|
@@ -408,7 +392,7 @@ export async function createInsForgeServerClient() {
|
|
|
408
392
|
|
|
409
393
|
```typescript
|
|
410
394
|
// app/api/auth/refresh/route.ts
|
|
411
|
-
import { createRefreshAuthRouter } from
|
|
395
|
+
import { createRefreshAuthRouter } from '@insforge/sdk/ssr';
|
|
412
396
|
|
|
413
397
|
export const { POST } = createRefreshAuthRouter();
|
|
414
398
|
```
|
|
@@ -420,17 +404,17 @@ so access and refresh tokens stay server-owned.
|
|
|
420
404
|
|
|
421
405
|
```typescript
|
|
422
406
|
// app/actions.ts
|
|
423
|
-
|
|
407
|
+
'use server';
|
|
424
408
|
|
|
425
|
-
import { cookies } from
|
|
426
|
-
import { createAuthActions } from
|
|
409
|
+
import { cookies } from 'next/headers';
|
|
410
|
+
import { createAuthActions } from '@insforge/sdk/ssr';
|
|
427
411
|
|
|
428
412
|
export async function signIn(formData: FormData) {
|
|
429
413
|
const auth = createAuthActions({ cookies: await cookies() });
|
|
430
414
|
|
|
431
415
|
const { data, error } = await auth.signInWithPassword({
|
|
432
|
-
email: String(formData.get(
|
|
433
|
-
password: String(formData.get(
|
|
416
|
+
email: String(formData.get('email')),
|
|
417
|
+
password: String(formData.get('password')),
|
|
434
418
|
});
|
|
435
419
|
|
|
436
420
|
return { user: data?.user ?? null, error };
|
|
@@ -443,32 +427,29 @@ verifier in an httpOnly app cookie and exchange the callback code with
|
|
|
443
427
|
|
|
444
428
|
```typescript
|
|
445
429
|
// app/actions.ts
|
|
446
|
-
|
|
430
|
+
'use server';
|
|
447
431
|
|
|
448
|
-
import { cookies } from
|
|
449
|
-
import { redirect } from
|
|
450
|
-
import { createAuthActions } from
|
|
432
|
+
import { cookies } from 'next/headers';
|
|
433
|
+
import { redirect } from 'next/navigation';
|
|
434
|
+
import { createAuthActions } from '@insforge/sdk/ssr';
|
|
451
435
|
|
|
452
436
|
export async function signInWithGoogle() {
|
|
453
437
|
const cookieStore = await cookies();
|
|
454
438
|
const auth = createAuthActions({ cookies: cookieStore });
|
|
455
|
-
const { data, error } = await auth.signInWithOAuth(
|
|
456
|
-
redirectTo: new URL(
|
|
457
|
-
"/api/auth/callback",
|
|
458
|
-
process.env.NEXT_PUBLIC_APP_URL
|
|
459
|
-
).toString(),
|
|
439
|
+
const { data, error } = await auth.signInWithOAuth('google', {
|
|
440
|
+
redirectTo: new URL('/api/auth/callback', process.env.NEXT_PUBLIC_APP_URL).toString(),
|
|
460
441
|
skipBrowserRedirect: true,
|
|
461
442
|
});
|
|
462
443
|
|
|
463
444
|
if (error || !data.url || !data.codeVerifier) {
|
|
464
|
-
throw new Error(error?.message ??
|
|
445
|
+
throw new Error(error?.message ?? 'OAuth init failed');
|
|
465
446
|
}
|
|
466
447
|
|
|
467
|
-
cookieStore.set(
|
|
448
|
+
cookieStore.set('insforge_code_verifier', data.codeVerifier, {
|
|
468
449
|
httpOnly: true,
|
|
469
|
-
secure: process.env.NODE_ENV ===
|
|
470
|
-
sameSite:
|
|
471
|
-
path:
|
|
450
|
+
secure: process.env.NODE_ENV === 'production',
|
|
451
|
+
sameSite: 'lax',
|
|
452
|
+
path: '/',
|
|
472
453
|
maxAge: 600,
|
|
473
454
|
});
|
|
474
455
|
|
|
@@ -478,28 +459,28 @@ export async function signInWithGoogle() {
|
|
|
478
459
|
|
|
479
460
|
```typescript
|
|
480
461
|
// app/api/auth/callback/route.ts
|
|
481
|
-
import { cookies } from
|
|
482
|
-
import { NextResponse, type NextRequest } from
|
|
483
|
-
import { createAuthActions } from
|
|
462
|
+
import { cookies } from 'next/headers';
|
|
463
|
+
import { NextResponse, type NextRequest } from 'next/server';
|
|
464
|
+
import { createAuthActions } from '@insforge/sdk/ssr';
|
|
484
465
|
|
|
485
466
|
export async function GET(request: NextRequest) {
|
|
486
|
-
const code = request.nextUrl.searchParams.get(
|
|
487
|
-
const verifier = (await cookies()).get(
|
|
467
|
+
const code = request.nextUrl.searchParams.get('insforge_code');
|
|
468
|
+
const verifier = (await cookies()).get('insforge_code_verifier')?.value;
|
|
488
469
|
if (!code || !verifier) {
|
|
489
|
-
return NextResponse.redirect(new URL(
|
|
470
|
+
return NextResponse.redirect(new URL('/login?error=oauth', request.url));
|
|
490
471
|
}
|
|
491
472
|
|
|
492
|
-
const response = NextResponse.redirect(new URL(
|
|
473
|
+
const response = NextResponse.redirect(new URL('/dashboard', request.url));
|
|
493
474
|
const auth = createAuthActions({
|
|
494
475
|
requestCookies: request.cookies,
|
|
495
476
|
responseCookies: response.cookies,
|
|
496
477
|
});
|
|
497
478
|
const { error } = await auth.exchangeOAuthCode(code, verifier);
|
|
498
479
|
if (error) {
|
|
499
|
-
return NextResponse.redirect(new URL(
|
|
480
|
+
return NextResponse.redirect(new URL('/login?error=oauth', request.url));
|
|
500
481
|
}
|
|
501
482
|
|
|
502
|
-
response.cookies.delete(
|
|
483
|
+
response.cookies.delete('insforge_code_verifier');
|
|
503
484
|
return response;
|
|
504
485
|
}
|
|
505
486
|
```
|
|
@@ -513,8 +494,8 @@ response cookies for writing the next session:
|
|
|
513
494
|
|
|
514
495
|
```typescript
|
|
515
496
|
// app/api/auth/sign-out/route.ts
|
|
516
|
-
import { NextResponse, type NextRequest } from
|
|
517
|
-
import { createAuthActions } from
|
|
497
|
+
import { NextResponse, type NextRequest } from 'next/server';
|
|
498
|
+
import { createAuthActions } from '@insforge/sdk/ssr';
|
|
518
499
|
|
|
519
500
|
export async function POST(request: NextRequest) {
|
|
520
501
|
const response = NextResponse.json({ ok: true });
|
|
@@ -538,7 +519,7 @@ export async function POST(request: NextRequest) {
|
|
|
538
519
|
If your refresh route needs custom side effects:
|
|
539
520
|
|
|
540
521
|
```typescript
|
|
541
|
-
import { refreshAuth } from
|
|
522
|
+
import { refreshAuth } from '@insforge/sdk/ssr';
|
|
542
523
|
|
|
543
524
|
export async function POST(request: Request) {
|
|
544
525
|
const result = await refreshAuth({ request });
|
|
@@ -551,8 +532,8 @@ For Next.js Proxy/Middleware, refresh before Server Components render:
|
|
|
551
532
|
|
|
552
533
|
```typescript
|
|
553
534
|
// proxy.ts on Next.js 16+, middleware.ts on Next.js 15 and earlier
|
|
554
|
-
import { NextResponse, type NextRequest } from
|
|
555
|
-
import { updateSession } from
|
|
535
|
+
import { NextResponse, type NextRequest } from 'next/server';
|
|
536
|
+
import { updateSession } from '@insforge/sdk/ssr/middleware';
|
|
556
537
|
|
|
557
538
|
export async function proxy(request: NextRequest) {
|
|
558
539
|
const response = NextResponse.next({ request });
|
|
@@ -574,10 +555,10 @@ the session refresh helpers and avoids bundling the full SDK client.
|
|
|
574
555
|
The SDK is written in TypeScript and provides full type definitions:
|
|
575
556
|
|
|
576
557
|
```typescript
|
|
577
|
-
import { createClient, InsForgeClient } from
|
|
558
|
+
import { createClient, InsForgeClient } from '@insforge/sdk';
|
|
578
559
|
|
|
579
560
|
const insforge: InsForgeClient = createClient({
|
|
580
|
-
baseUrl:
|
|
561
|
+
baseUrl: 'http://localhost:7130',
|
|
581
562
|
});
|
|
582
563
|
|
|
583
564
|
// Type-safe API calls
|