@insforge/nextjs 0.8.8 → 0.8.10

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 CHANGED
@@ -34,7 +34,10 @@ export default function RootLayout({ children }: { children: React.ReactNode })
34
34
  return (
35
35
  <html lang="en">
36
36
  <body>
37
- <InsforgeProvider baseUrl={process.env.NEXT_PUBLIC_INSFORGE_BASE_URL!}>
37
+ <InsforgeProvider
38
+ baseUrl={process.env.NEXT_PUBLIC_INSFORGE_BASE_URL!}
39
+ afterSignInUrl="/dashboard"
40
+ >
38
41
  {children}
39
42
  </InsforgeProvider>
40
43
  </body>
@@ -68,22 +71,7 @@ export const DELETE = handlers.DELETE;
68
71
  - `GET /api/auth` - Retrieves user data server-side
69
72
  - `DELETE /api/auth` - Clears auth cookie on sign out
70
73
 
71
- ### 3. Create Callback Page
72
-
73
- Handle OAuth redirects with the `<InsforgeCallback>` component:
74
-
75
- ```tsx
76
- // app/auth/callback/page.tsx
77
- 'use client';
78
-
79
- import { InsforgeCallback } from '@insforge/nextjs';
80
-
81
- export default function CallbackPage() {
82
- return <InsforgeCallback />;
83
- }
84
- ```
85
-
86
- ### 4. Setup Middleware
74
+ ### 3. Setup Middleware
87
75
 
88
76
  Protect routes with middleware:
89
77
 
@@ -107,7 +95,7 @@ export const config = {
107
95
  - Verifies tokens server-side
108
96
  - Allows public routes without auth
109
97
 
110
- ### 5. Use Hooks & Components
98
+ ### 4. Use Hooks & Components
111
99
 
112
100
  ```tsx
113
101
  // app/page.tsx
@@ -153,13 +141,12 @@ function Component() {
153
141
 
154
142
  2. User signs in on backend (https://your-project.insforge.app/auth/sign-in)
155
143
 
156
- 3. Backend redirects: yourapp.com/auth/callback?access_token=xxx&user_id=xxx...
144
+ 3. Backend redirects: yourapp.com?access_token=xxx&user_id=xxx...
157
145
 
158
- 4. <InsforgeCallback> component:
159
- - SDK auto-detects and stores token
160
- - Reloads auth state in provider
161
- - Syncs token to HTTP-only cookie
162
- - Redirects to destination
146
+ 4. SDK auto-detects URL parameters:
147
+ - Stores token in localStorage
148
+ - Provider automatically reloads auth state
149
+ - Token syncs to HTTP-only cookie (via API route)
163
150
 
164
151
  5. User sees dashboard with authenticated state
165
152
  ```
@@ -216,149 +203,6 @@ import { ForgotPassword, ResetPassword, VerifyEmail } from '@insforge/nextjs';
216
203
  <VerifyEmail token={token} />
217
204
  ```
218
205
 
219
- ### Hierarchical Appearance System
220
-
221
- Style any nested component through a hierarchical structure:
222
-
223
- ```tsx
224
- <SignIn
225
- appearance={{
226
- card: "bg-gradient-to-br from-blue-50 to-white shadow-2xl",
227
- header: {
228
- title: "text-3xl font-bold text-purple-900",
229
- subtitle: "text-purple-600"
230
- },
231
- form: {
232
- emailField: {
233
- label: "text-gray-800 font-semibold",
234
- input: "border-purple-300 focus:border-purple-500 rounded-lg"
235
- },
236
- passwordField: {
237
- input: "border-purple-300 focus:border-purple-500 rounded-lg",
238
- forgotPasswordLink: "text-purple-600 hover:text-purple-800"
239
- }
240
- },
241
- button: "bg-purple-600 hover:bg-purple-700 rounded-lg h-12",
242
- link: {
243
- text: "text-gray-600",
244
- link: "text-purple-600 hover:text-purple-800 font-semibold"
245
- },
246
- oauth: {
247
- button: "border-2 hover:bg-gray-50 rounded-xl"
248
- }
249
- }}
250
- />
251
- ```
252
-
253
- ### UserButton Styling
254
-
255
- ```tsx
256
- <UserButton
257
- mode="detailed" // or "simple"
258
- appearance={{
259
- buttonClassName: "hover:bg-gray-100 rounded-full",
260
- nameClassName: "text-gray-900 font-semibold",
261
- emailClassName: "text-gray-500",
262
- dropdownClassName: "shadow-xl"
263
- }}
264
- />
265
- ```
266
-
267
- ### Text Customization
268
-
269
- ```tsx
270
- <SignIn
271
- title="Welcome Back!"
272
- subtitle="Sign in to continue"
273
- emailLabel="Your Email Address"
274
- passwordLabel="Your Password"
275
- submitButtonText="Login Now"
276
- signUpText="New to our platform?"
277
- signUpLinkText="Create an account"
278
- />
279
- ```
280
-
281
- ---
282
-
283
- ## Advanced Usage
284
-
285
- ### Building Custom Auth Forms
286
-
287
- Use atomic components for complete customization:
288
-
289
- ```tsx
290
- import {
291
- AuthContainer,
292
- AuthHeader,
293
- AuthFormField,
294
- AuthPasswordField,
295
- AuthSubmitButton,
296
- AuthDivider,
297
- AuthOAuthProviders,
298
- AuthLink,
299
- } from '@insforge/nextjs';
300
-
301
- function CustomSignIn() {
302
- return (
303
- <AuthContainer
304
- appearance={{
305
- containerClassName: "max-w-md",
306
- cardClassName: "bg-white shadow-2xl"
307
- }}
308
- >
309
- <AuthHeader
310
- title="Welcome Back"
311
- subtitle="Sign in to continue"
312
- appearance={{
313
- titleClassName: "text-3xl text-blue-900"
314
- }}
315
- />
316
-
317
- <form onSubmit={handleSubmit}>
318
- <AuthFormField
319
- id="email"
320
- type="email"
321
- label="Email Address"
322
- placeholder="you@example.com"
323
- appearance={{
324
- inputClassName: "border-blue-500 focus:ring-blue-500"
325
- }}
326
- />
327
-
328
- <AuthPasswordField
329
- id="password"
330
- label="Password"
331
- emailAuthConfig={config}
332
- appearance={{
333
- inputClassName: "border-blue-500"
334
- }}
335
- />
336
-
337
- <AuthSubmitButton
338
- isLoading={loading}
339
- className="bg-blue-600 hover:bg-blue-700"
340
- >
341
- Sign In
342
- </AuthSubmitButton>
343
- </form>
344
-
345
- <AuthDivider text="or" />
346
-
347
- <AuthOAuthProviders
348
- providers={['google', 'github']}
349
- onClick={handleOAuth}
350
- loading={oauthLoading}
351
- />
352
-
353
- <AuthLink
354
- text="Don't have an account?"
355
- linkText="Sign up"
356
- href="/sign-up"
357
- />
358
- </AuthContainer>
359
- );
360
- }
361
- ```
362
206
 
363
207
  **Available Atomic Components:**
364
208
  - `AuthContainer` - Main form container
@@ -421,24 +265,6 @@ import { Protect } from '@insforge/nextjs';
421
265
  </Protect>
422
266
  ```
423
267
 
424
- ### InsforgeCallback Options
425
-
426
- ```tsx
427
- <InsforgeCallback
428
- redirectTo="/dashboard"
429
- onSuccess={() => console.log('Authentication successful!')}
430
- onError={(error) => console.error('Authentication failed:', error)}
431
- loadingComponent={
432
- <div className="flex items-center justify-center min-h-screen">
433
- <div className="text-center">
434
- <h2 className="text-2xl font-semibold mb-4">Signing you in...</h2>
435
- <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto"></div>
436
- </div>
437
- </div>
438
- }
439
- />
440
- ```
441
-
442
268
  ### Auth Change Callback
443
269
 
444
270
  Track authentication events:
package/dist/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  export * from '@insforge/react/types';
2
2
  import * as react_jsx_runtime from 'react/jsx-runtime';
3
3
  import { ReactNode } from 'react';
4
- import { useInsforge as useInsforge$1 } from '@insforge/react';
4
+ import { InsforgeUser, useInsforge as useInsforge$1 } from '@insforge/react';
5
5
  import { ClassValue } from 'clsx';
6
6
  export * from '@insforge/react/hooks';
7
7
  export * from '@insforge/react/components';
@@ -9,9 +9,10 @@ export * from '@insforge/react/components';
9
9
  interface InsforgeProviderProps {
10
10
  children: ReactNode;
11
11
  baseUrl: string;
12
- onAuthChange?: (user: any) => void;
12
+ afterSignInUrl?: string;
13
+ onAuthChange?: (user: InsforgeUser | null) => void;
13
14
  }
14
- declare function InsforgeProvider({ children, baseUrl, onAuthChange, }: InsforgeProviderProps): react_jsx_runtime.JSX.Element;
15
+ declare function InsforgeProvider({ children, baseUrl, afterSignInUrl, onAuthChange, }: InsforgeProviderProps): react_jsx_runtime.JSX.Element;
15
16
  declare const useInsforge: typeof useInsforge$1;
16
17
 
17
18
  declare function cn(...inputs: ClassValue[]): string;
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export * from '@insforge/react/types';
2
2
  import * as react_jsx_runtime from 'react/jsx-runtime';
3
3
  import { ReactNode } from 'react';
4
- import { useInsforge as useInsforge$1 } from '@insforge/react';
4
+ import { InsforgeUser, useInsforge as useInsforge$1 } from '@insforge/react';
5
5
  import { ClassValue } from 'clsx';
6
6
  export * from '@insforge/react/hooks';
7
7
  export * from '@insforge/react/components';
@@ -9,9 +9,10 @@ export * from '@insforge/react/components';
9
9
  interface InsforgeProviderProps {
10
10
  children: ReactNode;
11
11
  baseUrl: string;
12
- onAuthChange?: (user: any) => void;
12
+ afterSignInUrl?: string;
13
+ onAuthChange?: (user: InsforgeUser | null) => void;
13
14
  }
14
- declare function InsforgeProvider({ children, baseUrl, onAuthChange, }: InsforgeProviderProps): react_jsx_runtime.JSX.Element;
15
+ declare function InsforgeProvider({ children, baseUrl, afterSignInUrl, onAuthChange, }: InsforgeProviderProps): react_jsx_runtime.JSX.Element;
15
16
  declare const useInsforge: typeof useInsforge$1;
16
17
 
17
18
  declare function cn(...inputs: ClassValue[]): string;
package/dist/index.js CHANGED
@@ -32,9 +32,9 @@ __reExport(src_exports, require("@insforge/react/types"), module.exports);
32
32
  // src/provider/InsforgeProvider.tsx
33
33
  var import_react = require("@insforge/react");
34
34
  var import_jsx_runtime = require("react/jsx-runtime");
35
- async function syncTokenToCookie(token) {
35
+ async function handleSignIn(token) {
36
36
  try {
37
- const response = await fetch("/api/auth", {
37
+ await fetch("/api/auth", {
38
38
  method: "POST",
39
39
  headers: {
40
40
  "Content-Type": "application/json"
@@ -44,15 +44,11 @@ async function syncTokenToCookie(token) {
44
44
  token
45
45
  })
46
46
  });
47
- if (!response.ok) {
48
- return false;
49
- }
50
- return true;
51
47
  } catch (error) {
52
- return false;
48
+ console.error("[InsforgeProvider] Failed to sync token to cookie:", error);
53
49
  }
54
50
  }
55
- async function clearCookie() {
51
+ async function handleSignOut() {
56
52
  try {
57
53
  await fetch("/api/auth", { method: "DELETE" });
58
54
  } catch (error) {
@@ -61,18 +57,18 @@ async function clearCookie() {
61
57
  function InsforgeProvider({
62
58
  children,
63
59
  baseUrl,
60
+ afterSignInUrl = "/",
64
61
  onAuthChange
65
62
  }) {
66
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
67
- import_react.InsforgeProvider,
68
- {
69
- baseUrl,
70
- onAuthChange,
71
- syncTokenToCookie,
72
- clearCookie,
73
- children
74
- }
75
- );
63
+ const providerProps = {
64
+ children,
65
+ baseUrl,
66
+ afterSignInUrl,
67
+ onAuthChange,
68
+ onSignIn: handleSignIn,
69
+ onSignOut: handleSignOut
70
+ };
71
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.InsforgeProvider, { ...providerProps });
76
72
  }
77
73
  var useInsforge = import_react.useInsforge;
78
74
 
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/provider/InsforgeProvider.tsx","../src/lib/utils.ts"],"sourcesContent":["export * from '@insforge/react/types';\n\n// Provider\nexport { InsforgeProvider, useInsforge } from './provider/InsforgeProvider';\nexport { cn } from './lib/utils';\n\n// Hooks\nexport * from '@insforge/react/hooks';\n\n// Components\nexport * from '@insforge/react/components';\n","'use client';\n\nimport type { ReactNode } from 'react';\nimport { InsforgeProvider as ReactInsforgeProvider, useInsforge as useReactInsforge } from '@insforge/react';\n\nexport interface InsforgeProviderProps {\n children: ReactNode;\n baseUrl: string;\n onAuthChange?: (user: any) => void;\n}\n\n// Sync token from localStorage to server-side cookie via API\nasync function syncTokenToCookie(token: string): Promise<boolean> {\n try {\n const response = await fetch('/api/auth', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n action: 'sync-token',\n token,\n }),\n });\n\n if (!response.ok) {\n return false;\n }\n\n return true;\n } catch (error) {\n return false;\n }\n}\n\n// Clear HTTP-only cookie via API\nasync function clearCookie(): Promise<void> {\n try {\n await fetch('/api/auth', { method: 'DELETE' });\n } catch (error) {\n // API route doesn't exist - ignore\n }\n}\n\n/**\n * Insforge Provider for Next.js - wraps @insforge/react provider with Next.js-specific cookie sync\n * \n * Manages user authentication state and provides all necessary context to child components.\n * Automatically syncs authentication tokens to HTTP-only cookies for server-side middleware support.\n * \n * @example\n * ```tsx\n * import { InsforgeProvider } from '@insforge/nextjs';\n * \n * export default function RootLayout({ children }) {\n * return (\n * <InsforgeProvider baseUrl={process.env.NEXT_PUBLIC_INSFORGE_BASE_URL!}>\n * {children}\n * </InsforgeProvider>\n * );\n * }\n * ```\n */\nexport function InsforgeProvider({ \n children, \n baseUrl,\n onAuthChange,\n}: InsforgeProviderProps) {\n return (\n <ReactInsforgeProvider\n baseUrl={baseUrl}\n onAuthChange={onAuthChange}\n syncTokenToCookie={syncTokenToCookie}\n clearCookie={clearCookie}\n >\n {children}\n </ReactInsforgeProvider>\n );\n}\n\n/**\n * Hook to access Insforge context\n * \n * Re-exports the hook from @insforge/react for convenience.\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { user, isSignedIn, signOut } = useInsforge();\n * \n * if (!isSignedIn) return <SignIn />;\n * \n * return (\n * <div>\n * <p>Welcome {user.email}</p>\n * <button onClick={signOut}>Sign Out</button>\n * </div>\n * );\n * }\n * ```\n */\nexport const useInsforge = useReactInsforge;\n","import { type ClassValue, clsx } from 'clsx';\r\nimport { twMerge } from 'tailwind-merge';\r\n\r\n/**\r\n * Utility function to merge Tailwind CSS classes with clsx\r\n * Allows users to inject custom className overrides\r\n * \r\n * @example\r\n * ```tsx\r\n * cn(\"text-base\", isDark && \"text-white\", customClassName)\r\n * ```\r\n */\r\nexport function cn(...inputs: ClassValue[]) {\r\n return twMerge(clsx(inputs));\r\n}\r\n\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAAc,kCAAd;;;ACGA,mBAA2F;AAkEvF;AAzDJ,eAAe,kBAAkB,OAAiC;AAChE,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,aAAa;AAAA,MACxC,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,MAClB;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB,QAAQ;AAAA,QACR;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT,SAAS,OAAO;AACd,WAAO;AAAA,EACT;AACF;AAGA,eAAe,cAA6B;AAC1C,MAAI;AACF,UAAM,MAAM,aAAa,EAAE,QAAQ,SAAS,CAAC;AAAA,EAC/C,SAAS,OAAO;AAAA,EAEhB;AACF;AAqBO,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AACF,GAA0B;AACxB,SACE;AAAA,IAAC,aAAAA;AAAA,IAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;AAuBO,IAAM,cAAc,aAAAC;;;ACrG3B,kBAAsC;AACtC,4BAAwB;AAWjB,SAAS,MAAM,QAAsB;AAC1C,aAAO,mCAAQ,kBAAK,MAAM,CAAC;AAC7B;;;AFPA,wBAAc,kCAPd;AAUA,wBAAc,uCAVd;","names":["ReactInsforgeProvider","useReactInsforge"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/provider/InsforgeProvider.tsx","../src/lib/utils.ts"],"sourcesContent":["export * from '@insforge/react/types';\n\n// Provider\nexport { InsforgeProvider, useInsforge } from './provider/InsforgeProvider';\nexport { cn } from './lib/utils';\n\n// Hooks\nexport * from '@insforge/react/hooks';\n\n// Components\nexport * from '@insforge/react/components';\n","'use client';\n\nimport type { ReactNode } from 'react';\nimport { \n InsforgeProvider as ReactInsforgeProvider, \n useInsforge as useReactInsforge,\n type InsforgeUser\n} from '@insforge/react';\n\nexport interface InsforgeProviderProps {\n children: ReactNode;\n baseUrl: string;\n /**\n * URL to redirect to after successful sign in (when token is detected in URL)\n * Note: Middleware handles token storage, but Provider handles the redirect\n * @default '/'\n */\n afterSignInUrl?: string;\n onAuthChange?: (user: InsforgeUser | null) => void;\n}\n\n// Extended interface for React provider that includes internal handlers\ninterface ExtendedProviderProps extends InsforgeProviderProps {\n afterSignInUrl?: string;\n onSignIn?: (authToken: string) => Promise<void>;\n onSignOut?: () => Promise<void>;\n}\n\n// Sync token to server-side cookie on sign in\nasync function handleSignIn(token: string): Promise<void> {\n try {\n await fetch('/api/auth', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n action: 'sync-token',\n token,\n }),\n });\n } catch (error) {\n console.error('[InsforgeProvider] Failed to sync token to cookie:', error);\n }\n}\n\n// Clear HTTP-only cookie on sign out\nasync function handleSignOut(): Promise<void> {\n try {\n await fetch('/api/auth', { method: 'DELETE' });\n } catch (error) {\n // API route doesn't exist - ignore\n }\n}\n\n/**\n * Insforge Provider for Next.js - wraps @insforge/react provider with Next.js-specific cookie sync\n * \n * Manages user authentication state and provides all necessary context to child components.\n * Automatically syncs authentication tokens to HTTP-only cookies for server-side middleware support.\n * \n * @example\n * ```tsx\n * import { InsforgeProvider } from '@insforge/nextjs';\n * \n * export default function RootLayout({ children }) {\n * return (\n * <InsforgeProvider \n * baseUrl={process.env.NEXT_PUBLIC_INSFORGE_BASE_URL!}\n * afterSignInUrl=\"/dashboard\"\n * >\n * {children}\n * </InsforgeProvider>\n * );\n * }\n * ```\n */\nexport function InsforgeProvider({ \n children, \n baseUrl,\n afterSignInUrl = '/',\n onAuthChange,\n}: InsforgeProviderProps) {\n const providerProps: ExtendedProviderProps = {\n children,\n baseUrl,\n afterSignInUrl,\n onAuthChange,\n onSignIn: handleSignIn,\n onSignOut: handleSignOut,\n };\n\n return <ReactInsforgeProvider {...providerProps} />;\n}\n\n/**\n * Hook to access Insforge context\n * \n * Re-exports the hook from @insforge/react for convenience.\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { user, isSignedIn, signOut } = useInsforge();\n * \n * if (!isSignedIn) return <SignIn />;\n * \n * return (\n * <div>\n * <p>Welcome {user.email}</p>\n * <button onClick={signOut}>Sign Out</button>\n * </div>\n * );\n * }\n * ```\n */\nexport const useInsforge = useReactInsforge;\n","import { type ClassValue, clsx } from 'clsx';\r\nimport { twMerge } from 'tailwind-merge';\r\n\r\n/**\r\n * Utility function to merge Tailwind CSS classes with clsx\r\n * Allows users to inject custom className overrides\r\n * \r\n * @example\r\n * ```tsx\r\n * cn(\"text-base\", isDark && \"text-white\", customClassName)\r\n * ```\r\n */\r\nexport function cn(...inputs: ClassValue[]) {\r\n return twMerge(clsx(inputs));\r\n}\r\n\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAAc,kCAAd;;;ACGA,mBAIO;AAqFE;AA/DT,eAAe,aAAa,OAA8B;AACxD,MAAI;AACF,UAAM,MAAM,aAAa;AAAA,MACvB,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,MAClB;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB,QAAQ;AAAA,QACR;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH,SAAS,OAAO;AACd,YAAQ,MAAM,sDAAsD,KAAK;AAAA,EAC3E;AACF;AAGA,eAAe,gBAA+B;AAC5C,MAAI;AACF,UAAM,MAAM,aAAa,EAAE,QAAQ,SAAS,CAAC;AAAA,EAC/C,SAAS,OAAO;AAAA,EAEhB;AACF;AAwBO,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA,iBAAiB;AAAA,EACjB;AACF,GAA0B;AACxB,QAAM,gBAAuC;AAAA,IAC3C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV,WAAW;AAAA,EACb;AAEA,SAAO,4CAAC,aAAAA,kBAAA,EAAuB,GAAG,eAAe;AACnD;AAuBO,IAAM,cAAc,aAAAC;;;ACpH3B,kBAAsC;AACtC,4BAAwB;AAWjB,SAAS,MAAM,QAAsB;AAC1C,aAAO,mCAAQ,kBAAK,MAAM,CAAC;AAC7B;;;AFPA,wBAAc,kCAPd;AAUA,wBAAc,uCAVd;","names":["ReactInsforgeProvider","useReactInsforge"]}
package/dist/index.mjs CHANGED
@@ -4,11 +4,14 @@
4
4
  export * from "@insforge/react/types";
5
5
 
6
6
  // src/provider/InsforgeProvider.tsx
7
- import { InsforgeProvider as ReactInsforgeProvider, useInsforge as useReactInsforge } from "@insforge/react";
7
+ import {
8
+ InsforgeProvider as ReactInsforgeProvider,
9
+ useInsforge as useReactInsforge
10
+ } from "@insforge/react";
8
11
  import { jsx } from "react/jsx-runtime";
9
- async function syncTokenToCookie(token) {
12
+ async function handleSignIn(token) {
10
13
  try {
11
- const response = await fetch("/api/auth", {
14
+ await fetch("/api/auth", {
12
15
  method: "POST",
13
16
  headers: {
14
17
  "Content-Type": "application/json"
@@ -18,15 +21,11 @@ async function syncTokenToCookie(token) {
18
21
  token
19
22
  })
20
23
  });
21
- if (!response.ok) {
22
- return false;
23
- }
24
- return true;
25
24
  } catch (error) {
26
- return false;
25
+ console.error("[InsforgeProvider] Failed to sync token to cookie:", error);
27
26
  }
28
27
  }
29
- async function clearCookie() {
28
+ async function handleSignOut() {
30
29
  try {
31
30
  await fetch("/api/auth", { method: "DELETE" });
32
31
  } catch (error) {
@@ -35,18 +34,18 @@ async function clearCookie() {
35
34
  function InsforgeProvider({
36
35
  children,
37
36
  baseUrl,
37
+ afterSignInUrl = "/",
38
38
  onAuthChange
39
39
  }) {
40
- return /* @__PURE__ */ jsx(
41
- ReactInsforgeProvider,
42
- {
43
- baseUrl,
44
- onAuthChange,
45
- syncTokenToCookie,
46
- clearCookie,
47
- children
48
- }
49
- );
40
+ const providerProps = {
41
+ children,
42
+ baseUrl,
43
+ afterSignInUrl,
44
+ onAuthChange,
45
+ onSignIn: handleSignIn,
46
+ onSignOut: handleSignOut
47
+ };
48
+ return /* @__PURE__ */ jsx(ReactInsforgeProvider, { ...providerProps });
50
49
  }
51
50
  var useInsforge = useReactInsforge;
52
51
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/provider/InsforgeProvider.tsx","../src/lib/utils.ts"],"sourcesContent":["export * from '@insforge/react/types';\n\n// Provider\nexport { InsforgeProvider, useInsforge } from './provider/InsforgeProvider';\nexport { cn } from './lib/utils';\n\n// Hooks\nexport * from '@insforge/react/hooks';\n\n// Components\nexport * from '@insforge/react/components';\n","'use client';\n\nimport type { ReactNode } from 'react';\nimport { InsforgeProvider as ReactInsforgeProvider, useInsforge as useReactInsforge } from '@insforge/react';\n\nexport interface InsforgeProviderProps {\n children: ReactNode;\n baseUrl: string;\n onAuthChange?: (user: any) => void;\n}\n\n// Sync token from localStorage to server-side cookie via API\nasync function syncTokenToCookie(token: string): Promise<boolean> {\n try {\n const response = await fetch('/api/auth', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n action: 'sync-token',\n token,\n }),\n });\n\n if (!response.ok) {\n return false;\n }\n\n return true;\n } catch (error) {\n return false;\n }\n}\n\n// Clear HTTP-only cookie via API\nasync function clearCookie(): Promise<void> {\n try {\n await fetch('/api/auth', { method: 'DELETE' });\n } catch (error) {\n // API route doesn't exist - ignore\n }\n}\n\n/**\n * Insforge Provider for Next.js - wraps @insforge/react provider with Next.js-specific cookie sync\n * \n * Manages user authentication state and provides all necessary context to child components.\n * Automatically syncs authentication tokens to HTTP-only cookies for server-side middleware support.\n * \n * @example\n * ```tsx\n * import { InsforgeProvider } from '@insforge/nextjs';\n * \n * export default function RootLayout({ children }) {\n * return (\n * <InsforgeProvider baseUrl={process.env.NEXT_PUBLIC_INSFORGE_BASE_URL!}>\n * {children}\n * </InsforgeProvider>\n * );\n * }\n * ```\n */\nexport function InsforgeProvider({ \n children, \n baseUrl,\n onAuthChange,\n}: InsforgeProviderProps) {\n return (\n <ReactInsforgeProvider\n baseUrl={baseUrl}\n onAuthChange={onAuthChange}\n syncTokenToCookie={syncTokenToCookie}\n clearCookie={clearCookie}\n >\n {children}\n </ReactInsforgeProvider>\n );\n}\n\n/**\n * Hook to access Insforge context\n * \n * Re-exports the hook from @insforge/react for convenience.\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { user, isSignedIn, signOut } = useInsforge();\n * \n * if (!isSignedIn) return <SignIn />;\n * \n * return (\n * <div>\n * <p>Welcome {user.email}</p>\n * <button onClick={signOut}>Sign Out</button>\n * </div>\n * );\n * }\n * ```\n */\nexport const useInsforge = useReactInsforge;\n","import { type ClassValue, clsx } from 'clsx';\r\nimport { twMerge } from 'tailwind-merge';\r\n\r\n/**\r\n * Utility function to merge Tailwind CSS classes with clsx\r\n * Allows users to inject custom className overrides\r\n * \r\n * @example\r\n * ```tsx\r\n * cn(\"text-base\", isDark && \"text-white\", customClassName)\r\n * ```\r\n */\r\nexport function cn(...inputs: ClassValue[]) {\r\n return twMerge(clsx(inputs));\r\n}\r\n\r\n"],"mappings":";;;AAAA,cAAc;;;ACGd,SAAS,oBAAoB,uBAAuB,eAAe,wBAAwB;AAkEvF;AAzDJ,eAAe,kBAAkB,OAAiC;AAChE,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,aAAa;AAAA,MACxC,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,MAClB;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB,QAAQ;AAAA,QACR;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT,SAAS,OAAO;AACd,WAAO;AAAA,EACT;AACF;AAGA,eAAe,cAA6B;AAC1C,MAAI;AACF,UAAM,MAAM,aAAa,EAAE,QAAQ,SAAS,CAAC;AAAA,EAC/C,SAAS,OAAO;AAAA,EAEhB;AACF;AAqBO,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AACF,GAA0B;AACxB,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;AAuBO,IAAM,cAAc;;;ACrG3B,SAA0B,YAAY;AACtC,SAAS,eAAe;AAWjB,SAAS,MAAM,QAAsB;AAC1C,SAAO,QAAQ,KAAK,MAAM,CAAC;AAC7B;;;AFPA,cAAc;AAGd,cAAc;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/provider/InsforgeProvider.tsx","../src/lib/utils.ts"],"sourcesContent":["export * from '@insforge/react/types';\n\n// Provider\nexport { InsforgeProvider, useInsforge } from './provider/InsforgeProvider';\nexport { cn } from './lib/utils';\n\n// Hooks\nexport * from '@insforge/react/hooks';\n\n// Components\nexport * from '@insforge/react/components';\n","'use client';\n\nimport type { ReactNode } from 'react';\nimport { \n InsforgeProvider as ReactInsforgeProvider, \n useInsforge as useReactInsforge,\n type InsforgeUser\n} from '@insforge/react';\n\nexport interface InsforgeProviderProps {\n children: ReactNode;\n baseUrl: string;\n /**\n * URL to redirect to after successful sign in (when token is detected in URL)\n * Note: Middleware handles token storage, but Provider handles the redirect\n * @default '/'\n */\n afterSignInUrl?: string;\n onAuthChange?: (user: InsforgeUser | null) => void;\n}\n\n// Extended interface for React provider that includes internal handlers\ninterface ExtendedProviderProps extends InsforgeProviderProps {\n afterSignInUrl?: string;\n onSignIn?: (authToken: string) => Promise<void>;\n onSignOut?: () => Promise<void>;\n}\n\n// Sync token to server-side cookie on sign in\nasync function handleSignIn(token: string): Promise<void> {\n try {\n await fetch('/api/auth', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n action: 'sync-token',\n token,\n }),\n });\n } catch (error) {\n console.error('[InsforgeProvider] Failed to sync token to cookie:', error);\n }\n}\n\n// Clear HTTP-only cookie on sign out\nasync function handleSignOut(): Promise<void> {\n try {\n await fetch('/api/auth', { method: 'DELETE' });\n } catch (error) {\n // API route doesn't exist - ignore\n }\n}\n\n/**\n * Insforge Provider for Next.js - wraps @insforge/react provider with Next.js-specific cookie sync\n * \n * Manages user authentication state and provides all necessary context to child components.\n * Automatically syncs authentication tokens to HTTP-only cookies for server-side middleware support.\n * \n * @example\n * ```tsx\n * import { InsforgeProvider } from '@insforge/nextjs';\n * \n * export default function RootLayout({ children }) {\n * return (\n * <InsforgeProvider \n * baseUrl={process.env.NEXT_PUBLIC_INSFORGE_BASE_URL!}\n * afterSignInUrl=\"/dashboard\"\n * >\n * {children}\n * </InsforgeProvider>\n * );\n * }\n * ```\n */\nexport function InsforgeProvider({ \n children, \n baseUrl,\n afterSignInUrl = '/',\n onAuthChange,\n}: InsforgeProviderProps) {\n const providerProps: ExtendedProviderProps = {\n children,\n baseUrl,\n afterSignInUrl,\n onAuthChange,\n onSignIn: handleSignIn,\n onSignOut: handleSignOut,\n };\n\n return <ReactInsforgeProvider {...providerProps} />;\n}\n\n/**\n * Hook to access Insforge context\n * \n * Re-exports the hook from @insforge/react for convenience.\n * \n * @example\n * ```tsx\n * function MyComponent() {\n * const { user, isSignedIn, signOut } = useInsforge();\n * \n * if (!isSignedIn) return <SignIn />;\n * \n * return (\n * <div>\n * <p>Welcome {user.email}</p>\n * <button onClick={signOut}>Sign Out</button>\n * </div>\n * );\n * }\n * ```\n */\nexport const useInsforge = useReactInsforge;\n","import { type ClassValue, clsx } from 'clsx';\r\nimport { twMerge } from 'tailwind-merge';\r\n\r\n/**\r\n * Utility function to merge Tailwind CSS classes with clsx\r\n * Allows users to inject custom className overrides\r\n * \r\n * @example\r\n * ```tsx\r\n * cn(\"text-base\", isDark && \"text-white\", customClassName)\r\n * ```\r\n */\r\nexport function cn(...inputs: ClassValue[]) {\r\n return twMerge(clsx(inputs));\r\n}\r\n\r\n"],"mappings":";;;AAAA,cAAc;;;ACGd;AAAA,EACE,oBAAoB;AAAA,EACpB,eAAe;AAAA,OAEV;AAqFE;AA/DT,eAAe,aAAa,OAA8B;AACxD,MAAI;AACF,UAAM,MAAM,aAAa;AAAA,MACvB,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,MAClB;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB,QAAQ;AAAA,QACR;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH,SAAS,OAAO;AACd,YAAQ,MAAM,sDAAsD,KAAK;AAAA,EAC3E;AACF;AAGA,eAAe,gBAA+B;AAC5C,MAAI;AACF,UAAM,MAAM,aAAa,EAAE,QAAQ,SAAS,CAAC;AAAA,EAC/C,SAAS,OAAO;AAAA,EAEhB;AACF;AAwBO,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA,iBAAiB;AAAA,EACjB;AACF,GAA0B;AACxB,QAAM,gBAAuC;AAAA,IAC3C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV,WAAW;AAAA,EACb;AAEA,SAAO,oBAAC,yBAAuB,GAAG,eAAe;AACnD;AAuBO,IAAM,cAAc;;;ACpH3B,SAA0B,YAAY;AACtC,SAAS,eAAe;AAWjB,SAAS,MAAM,QAAsB;AAC1C,SAAO,QAAQ,KAAK,MAAM,CAAC;AAC7B;;;AFPA,cAAc;AAGd,cAAc;","names":[]}
@@ -30,23 +30,33 @@ var import_server = require("next/server");
30
30
  function InsforgeMiddleware(config) {
31
31
  const {
32
32
  baseUrl,
33
- publicRoutes = ["/auth/callback", "/"],
33
+ publicRoutes = ["/"],
34
34
  signInUrl = "/sign-in",
35
35
  signUpUrl = "/sign-up",
36
36
  cookieName = "insforge_token",
37
37
  useBuiltInAuth = true
38
38
  } = config;
39
39
  return async function middleware(request) {
40
- const { pathname } = request.nextUrl;
41
- if (pathname === "/auth/callback") {
42
- return import_server.NextResponse.next();
40
+ const { pathname, searchParams } = request.nextUrl;
41
+ const accessToken = searchParams.get("access_token");
42
+ if (accessToken) {
43
+ const response = import_server.NextResponse.redirect(new URL(pathname, request.url));
44
+ response.cookies.set(cookieName, accessToken, {
45
+ httpOnly: true,
46
+ secure: process.env.NODE_ENV === "production",
47
+ sameSite: "lax",
48
+ path: "/",
49
+ maxAge: 60 * 60 * 24 * 7
50
+ // 7 days
51
+ });
52
+ return response;
43
53
  }
44
54
  if (useBuiltInAuth && (pathname === signInUrl || pathname === signUpUrl)) {
45
- const callbackUrl = new URL("/auth/callback", request.url);
55
+ const currentUrl = request.url;
46
56
  const backendAuthPath = pathname === signInUrl ? "/auth/sign-in" : "/auth/sign-up";
47
- const ossAuthUrl = new URL(backendAuthPath, baseUrl);
48
- ossAuthUrl.searchParams.set("redirect", callbackUrl.toString());
49
- return import_server.NextResponse.redirect(ossAuthUrl.toString());
57
+ const backendAuthUrl = new URL(backendAuthPath, baseUrl);
58
+ backendAuthUrl.searchParams.set("redirect", currentUrl);
59
+ return import_server.NextResponse.redirect(backendAuthUrl.toString());
50
60
  }
51
61
  const isPublicRoute = publicRoutes.some((route) => {
52
62
  if (route.endsWith("*")) {
@@ -60,9 +70,8 @@ function InsforgeMiddleware(config) {
60
70
  const token = request.cookies.get(cookieName)?.value;
61
71
  if (!token) {
62
72
  if (useBuiltInAuth) {
63
- const callbackUrl = new URL("/auth/callback", request.url);
64
73
  const backendSignInUrl = new URL("/auth/sign-in", baseUrl);
65
- backendSignInUrl.searchParams.set("redirect", callbackUrl.toString());
74
+ backendSignInUrl.searchParams.set("redirect", request.url);
66
75
  return import_server.NextResponse.redirect(backendSignInUrl);
67
76
  } else {
68
77
  const localSignInUrl = new URL(signInUrl, request.url);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/middleware/index.ts","../src/middleware/InsforgeMiddleware.ts"],"sourcesContent":["// Middleware exports - NO 'use client' directive here!\r\n// Middleware runs in Edge Runtime, not in client browser\r\n\r\nexport {\r\n InsforgeMiddleware,\r\n} from './InsforgeMiddleware';\r\n\r\nexport type { InsforgeMiddlewareConfig } from './InsforgeMiddleware';\r\n\r\n","import { NextRequest, NextResponse } from 'next/server';\r\n\r\nexport interface InsforgeMiddlewareConfig {\r\n /**\r\n * Base URL of your Insforge backend\r\n * @example 'https://your-backend.com' or 'http://localhost:3001'\r\n */\r\n baseUrl: string;\r\n\r\n /**\r\n * Routes that are public and don't require authentication\r\n * Note: `/auth/callback` is automatically treated as public\r\n * @default ['/auth/callback', '/']\r\n * @example ['/sign-in', '/sign-up', '/', '/about']\r\n */\r\n publicRoutes?: string[];\r\n\r\n /**\r\n * Local route path for sign-in (can be customized to any path like '/login')\r\n * When using built-in auth, this will redirect to backend's `/auth/sign-in`\r\n * @default '/sign-in'\r\n */\r\n signInUrl?: string;\r\n\r\n /**\r\n * Local route path for sign-up (can be customized to any path like '/register')\r\n * When using built-in auth, this will redirect to backend's `/auth/sign-up`\r\n * @default '/sign-up'\r\n */\r\n signUpUrl?: string;\r\n\r\n /**\r\n * Cookie name for the auth token\r\n * @default 'insforge_token'\r\n */\r\n cookieName?: string;\r\n\r\n /**\r\n * Whether to use built-in authentication pages hosted on the backend\r\n * - When true: redirects to backend's `/auth/sign-in` and `/auth/sign-up` pages\r\n * - When false: redirects to local sign-in/sign-up pages (you provide your own components)\r\n * @default true\r\n */\r\n useBuiltInAuth?: boolean;\r\n}\r\n\r\n/**\r\n * Creates Next.js middleware for protecting routes with Insforge authentication.\r\n * \r\n * This middleware provides lightweight route protection by:\r\n * - Checking for auth token presence in cookies\r\n * - Redirecting unauthenticated users to sign-in page\r\n * - Allowing public routes to be accessed without authentication\r\n * - Always allowing `/auth/callback` through (for token extraction)\r\n * - Mapping local auth routes to backend's fixed paths when using built-in auth\r\n * \r\n * **Important Notes:**\r\n * - This middleware only checks if a token exists, it doesn't validate it\r\n * - `/auth/callback` is automatically treated as public (handles query param tokens)\r\n * - When `useBuiltInAuth: true`, local routes map to backend's `/auth/sign-in` and `/auth/sign-up`\r\n * - You can customize local route paths (e.g., `/login`) while backend paths remain fixed\r\n * \r\n * @param config - Middleware configuration\r\n * @returns Next.js middleware function\r\n *\r\n * @example\r\n * ```ts\r\n * // middleware.ts - Using built-in auth with separate frontend/backend URLs\r\n * import { InsforgeMiddleware } from '@insforge/nextjs/middleware';\r\n *\r\n * export default InsforgeMiddleware({\r\n * baseUrl: process.env.INSFORGE_BASE_URL!, // Backend: http://localhost:3001\r\n * frontendUrl: process.env.INSFORGE_FRONTEND_URL!, // Frontend: http://localhost:3000\r\n * publicRoutes: ['/', '/about'], // /auth/callback is auto-included\r\n * useBuiltInAuth: true, // Use backend's auth pages\r\n * });\r\n *\r\n * export const config = {\r\n * matcher: ['/((?!_next|api|.*\\\\..*).*)'],\r\n * };\r\n * ```\r\n * \r\n * @example\r\n * ```ts\r\n * // middleware.ts - Custom local auth pages with custom paths\r\n * import { InsforgeMiddleware } from '@insforge/nextjs/middleware';\r\n *\r\n * export default InsforgeMiddleware({\r\n * baseUrl: process.env.INSFORGE_BASE_URL!,\r\n * publicRoutes: ['/login', '/register', '/', '/about'],\r\n * signInUrl: '/login', // Custom local path\r\n * signUpUrl: '/register', // Custom local path\r\n * useBuiltInAuth: false, // Use your own SignIn/SignUp components\r\n * });\r\n *\r\n * export const config = {\r\n * matcher: ['/((?!_next|api|.*\\\\..*).*)'],\r\n * };\r\n * ```\r\n * \r\n * @example\r\n * ```ts\r\n * // middleware.ts - Built-in auth with custom local route names\r\n * // Local '/login' and '/register' routes redirect to backend's '/auth/sign-in' and '/auth/sign-up'\r\n * import { InsforgeMiddleware } from '@insforge/nextjs/middleware';\r\n *\r\n * export default InsforgeMiddleware({\r\n * baseUrl: 'https://your-backend.com',\r\n * signInUrl: '/login', // Local path (redirects to backend /auth/sign-in)\r\n * signUpUrl: '/register', // Local path (redirects to backend /auth/sign-up)\r\n * useBuiltInAuth: true,\r\n * });\r\n * ```\r\n */\r\nexport function InsforgeMiddleware(config: InsforgeMiddlewareConfig) {\r\n const {\r\n baseUrl,\r\n publicRoutes = [\"/auth/callback\", \"/\"],\r\n signInUrl = '/sign-in',\r\n signUpUrl = '/sign-up',\r\n cookieName = 'insforge_token',\r\n useBuiltInAuth = true,\r\n } = config;\r\n\r\n return async function middleware(request: NextRequest) {\r\n const { pathname } = request.nextUrl;\r\n\r\n // STEP 1: Always allow /auth/callback through\r\n // This route must be public because it receives the token as a query parameter\r\n // and the InsforgeProvider processes it on the client side\r\n if (pathname === '/auth/callback') {\r\n return NextResponse.next();\r\n }\r\n\r\n // STEP 2: Handle built-in auth redirects\r\n // Map local sign-in/sign-up routes to backend's fixed auth pages\r\n // Example: local '/login' → backend '/auth/sign-in'\r\n if (useBuiltInAuth && (pathname === signInUrl || pathname === signUpUrl)) {\r\n const callbackUrl = new URL('/auth/callback', request.url);\r\n // Backend auth routes are FIXED: /auth/sign-in and /auth/sign-up (with hyphen)\r\n const backendAuthPath = pathname === signInUrl ? '/auth/sign-in' : '/auth/sign-up';\r\n const ossAuthUrl = new URL(backendAuthPath, baseUrl);\r\n ossAuthUrl.searchParams.set('redirect', callbackUrl.toString());\r\n return NextResponse.redirect(ossAuthUrl.toString());\r\n }\r\n\r\n // STEP 3: Check if route is public\r\n const isPublicRoute = publicRoutes.some((route) => {\r\n if (route.endsWith('*')) {\r\n // Wildcard route: /admin/* matches /admin/anything\r\n return pathname.startsWith(route.slice(0, -1));\r\n }\r\n // Exact match or starts with route path\r\n return pathname === route || pathname.startsWith(route + '/');\r\n });\r\n\r\n if (isPublicRoute) {\r\n return NextResponse.next();\r\n }\r\n\r\n // STEP 4: Check for authentication token in cookies\r\n // Note: We only check if token exists, we don't validate it here\r\n const token = request.cookies.get(cookieName)?.value;\r\n\r\n if (!token) {\r\n // No token found, redirect to sign-in\r\n if (useBuiltInAuth) {\r\n // Built-in auth: redirect to backend's /auth/sign-in page\r\n const callbackUrl = new URL('/auth/callback', request.url);\r\n const backendSignInUrl = new URL('/auth/sign-in', baseUrl);\r\n backendSignInUrl.searchParams.set('redirect', callbackUrl.toString());\r\n return NextResponse.redirect(backendSignInUrl);\r\n } else {\r\n // Custom auth: redirect to local sign-in page\r\n const localSignInUrl = new URL(signInUrl, request.url);\r\n localSignInUrl.searchParams.set('redirect', pathname);\r\n return NextResponse.redirect(localSignInUrl);\r\n }\r\n }\r\n\r\n // Token exists, allow request to continue\r\n return NextResponse.next();\r\n };\r\n}"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,oBAA0C;AAkHnC,SAAS,mBAAmB,QAAkC;AACnE,QAAM;AAAA,IACJ;AAAA,IACA,eAAe,CAAC,kBAAkB,GAAG;AAAA,IACrC,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,iBAAiB;AAAA,EACnB,IAAI;AAEJ,SAAO,eAAe,WAAW,SAAsB;AACrD,UAAM,EAAE,SAAS,IAAI,QAAQ;AAK7B,QAAI,aAAa,kBAAkB;AACjC,aAAO,2BAAa,KAAK;AAAA,IAC3B;AAKA,QAAI,mBAAmB,aAAa,aAAa,aAAa,YAAY;AACxE,YAAM,cAAc,IAAI,IAAI,kBAAkB,QAAQ,GAAG;AAEzD,YAAM,kBAAkB,aAAa,YAAY,kBAAkB;AACnE,YAAM,aAAa,IAAI,IAAI,iBAAiB,OAAO;AACnD,iBAAW,aAAa,IAAI,YAAY,YAAY,SAAS,CAAC;AAC9D,aAAO,2BAAa,SAAS,WAAW,SAAS,CAAC;AAAA,IACpD;AAGA,UAAM,gBAAgB,aAAa,KAAK,CAAC,UAAU;AACjD,UAAI,MAAM,SAAS,GAAG,GAAG;AAEvB,eAAO,SAAS,WAAW,MAAM,MAAM,GAAG,EAAE,CAAC;AAAA,MAC/C;AAEA,aAAO,aAAa,SAAS,SAAS,WAAW,QAAQ,GAAG;AAAA,IAC9D,CAAC;AAED,QAAI,eAAe;AACjB,aAAO,2BAAa,KAAK;AAAA,IAC3B;AAIA,UAAM,QAAQ,QAAQ,QAAQ,IAAI,UAAU,GAAG;AAE/C,QAAI,CAAC,OAAO;AAEV,UAAI,gBAAgB;AAElB,cAAM,cAAc,IAAI,IAAI,kBAAkB,QAAQ,GAAG;AACzD,cAAM,mBAAmB,IAAI,IAAI,iBAAiB,OAAO;AACzD,yBAAiB,aAAa,IAAI,YAAY,YAAY,SAAS,CAAC;AACpE,eAAO,2BAAa,SAAS,gBAAgB;AAAA,MAC/C,OAAO;AAEL,cAAM,iBAAiB,IAAI,IAAI,WAAW,QAAQ,GAAG;AACrD,uBAAe,aAAa,IAAI,YAAY,QAAQ;AACpD,eAAO,2BAAa,SAAS,cAAc;AAAA,MAC7C;AAAA,IACF;AAGA,WAAO,2BAAa,KAAK;AAAA,EAC3B;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/middleware/index.ts","../src/middleware/InsforgeMiddleware.ts"],"sourcesContent":["// Middleware exports - NO 'use client' directive here!\r\n// Middleware runs in Edge Runtime, not in client browser\r\n\r\nexport {\r\n InsforgeMiddleware,\r\n} from './InsforgeMiddleware';\r\n\r\nexport type { InsforgeMiddlewareConfig } from './InsforgeMiddleware';\r\n\r\n","import { NextRequest, NextResponse } from 'next/server';\r\n\r\nexport interface InsforgeMiddlewareConfig {\r\n /**\r\n * Base URL of your Insforge backend\r\n * @example 'https://your-backend.com' or 'http://localhost:3001'\r\n */\r\n baseUrl: string;\r\n\r\n /**\r\n * Routes that are public and don't require authentication\r\n * @default ['/']\r\n * @example ['/sign-in', '/sign-up', '/', '/about']\r\n */\r\n publicRoutes?: string[];\r\n\r\n /**\r\n * Local route path for sign-in (can be customized to any path like '/login')\r\n * When using built-in auth, this will redirect to backend's `/auth/sign-in`\r\n * @default '/sign-in'\r\n */\r\n signInUrl?: string;\r\n\r\n /**\r\n * Local route path for sign-up (can be customized to any path like '/register')\r\n * When using built-in auth, this will redirect to backend's `/auth/sign-up`\r\n * @default '/sign-up'\r\n */\r\n signUpUrl?: string;\r\n\r\n /**\r\n * Cookie name for the auth token\r\n * @default 'insforge_token'\r\n */\r\n cookieName?: string;\r\n\r\n /**\r\n * Whether to use built-in authentication pages hosted on the backend\r\n * - When true: redirects to backend's `/auth/sign-in` and `/auth/sign-up` pages\r\n * - When false: redirects to local sign-in/sign-up pages (you provide your own components)\r\n * @default true\r\n */\r\n useBuiltInAuth?: boolean;\r\n}\r\n\r\n/**\r\n * Creates Next.js middleware for protecting routes with Insforge authentication.\r\n * \r\n * This middleware provides lightweight route protection by:\r\n * - Detecting and storing auth tokens from URL parameters (after backend redirect)\r\n * - Checking for auth token presence in cookies\r\n * - Redirecting unauthenticated users to sign-in page\r\n * - Allowing public routes to be accessed without authentication\r\n * - Mapping local auth routes to backend's fixed paths when using built-in auth\r\n * \r\n * **How Authentication Flow Works:**\r\n * 1. User visits protected route → Middleware checks for token\r\n * 2. No token → Redirects to sign-in (backend or local)\r\n * 3. After sign-in Backend redirects to `yourapp.com/destination?access_token=xxx&user_id=xxx...`\r\n * 4. Middleware detects `access_token` in URL → Stores in HTTP-only cookie → Cleans URL → Allows access\r\n * 5. SDK also detects token from URL → Stores in localStorage → Updates auth state\r\n * \r\n * **Important Notes:**\r\n * - This middleware only checks if a token exists, it doesn't validate it\r\n * - Tokens from URL are automatically extracted and stored in cookies\r\n * - When `useBuiltInAuth: true`, local routes map to backend's `/auth/sign-in` and `/auth/sign-up`\r\n * - You can customize local route paths (e.g., `/login`) while backend paths remain fixed\r\n * \r\n * @param config - Middleware configuration\r\n * @returns Next.js middleware function\r\n *\r\n * @example\r\n * ```ts\r\n * // middleware.ts - Using built-in auth\r\n * import { InsforgeMiddleware } from '@insforge/nextjs/middleware';\r\n *\r\n * export default InsforgeMiddleware({\r\n * baseUrl: process.env.INSFORGE_BASE_URL!,\r\n * publicRoutes: ['/', '/about'],\r\n * useBuiltInAuth: true,\r\n * });\r\n *\r\n * export const config = {\r\n * matcher: ['/((?!_next|api|.*\\\\..*).*)'],\r\n * };\r\n * ```\r\n * \r\n * @example\r\n * ```ts\r\n * // middleware.ts - Custom local auth pages with custom paths\r\n * import { InsforgeMiddleware } from '@insforge/nextjs/middleware';\r\n *\r\n * export default InsforgeMiddleware({\r\n * baseUrl: process.env.INSFORGE_BASE_URL!,\r\n * publicRoutes: ['/login', '/register', '/', '/about'],\r\n * signInUrl: '/login',\r\n * signUpUrl: '/register',\r\n * useBuiltInAuth: false,\r\n * });\r\n *\r\n * export const config = {\r\n * matcher: ['/((?!_next|api|.*\\\\..*).*)'],\r\n * };\r\n * ```\r\n * \r\n * @example\r\n * ```ts\r\n * // middleware.ts - Built-in auth with custom local route names\r\n * import { InsforgeMiddleware } from '@insforge/nextjs/middleware';\r\n *\r\n * export default InsforgeMiddleware({\r\n * baseUrl: 'https://your-backend.com',\r\n * signInUrl: '/login',\r\n * signUpUrl: '/register',\r\n * useBuiltInAuth: true,\r\n * });\r\n * ```\r\n */\r\nexport function InsforgeMiddleware(config: InsforgeMiddlewareConfig) {\r\n const {\r\n baseUrl,\r\n publicRoutes = [\"/\"],\r\n signInUrl = '/sign-in',\r\n signUpUrl = '/sign-up',\r\n cookieName = 'insforge_token',\r\n useBuiltInAuth = true,\r\n } = config;\r\n\r\n return async function middleware(request: NextRequest) {\r\n const { pathname, searchParams } = request.nextUrl;\r\n\r\n // STEP 1: Check if URL contains access_token (from backend redirect after auth)\r\n const accessToken = searchParams.get('access_token');\r\n \r\n if (accessToken) {\r\n // Token detected in URL - store it in HTTP-only cookie and clean URL\r\n const response = NextResponse.redirect(new URL(pathname, request.url));\r\n \r\n // Set HTTP-only cookie with secure settings\r\n response.cookies.set(cookieName, accessToken, {\r\n httpOnly: true,\r\n secure: process.env.NODE_ENV === 'production',\r\n sameSite: 'lax',\r\n path: '/',\r\n maxAge: 60 * 60 * 24 * 7, // 7 days\r\n });\r\n \r\n return response;\r\n }\r\n\r\n // STEP 2: Handle built-in auth redirects for sign-in/sign-up pages\r\n // Map local sign-in/sign-up routes to backend's fixed auth pages\r\n if (useBuiltInAuth && (pathname === signInUrl || pathname === signUpUrl)) {\r\n // Backend redirects directly to the current page with token in URL\r\n const currentUrl = request.url;\r\n const backendAuthPath = pathname === signInUrl ? '/auth/sign-in' : '/auth/sign-up';\r\n const backendAuthUrl = new URL(backendAuthPath, baseUrl);\r\n backendAuthUrl.searchParams.set('redirect', currentUrl);\r\n return NextResponse.redirect(backendAuthUrl.toString());\r\n }\r\n\r\n // STEP 3: Check if route is public\r\n const isPublicRoute = publicRoutes.some((route) => {\r\n if (route.endsWith('*')) {\r\n // Wildcard route: /admin/* matches /admin/anything\r\n return pathname.startsWith(route.slice(0, -1));\r\n }\r\n // Exact match or starts with route path\r\n return pathname === route || pathname.startsWith(route + '/');\r\n });\r\n\r\n if (isPublicRoute) {\r\n return NextResponse.next();\r\n }\r\n\r\n // STEP 4: Check for authentication token in cookies\r\n const token = request.cookies.get(cookieName)?.value;\r\n\r\n if (!token) {\r\n // No token found, redirect to sign-in\r\n if (useBuiltInAuth) {\r\n // Built-in auth: redirect to backend's /auth/sign-in page\r\n // Backend will redirect back to current URL with token\r\n const backendSignInUrl = new URL('/auth/sign-in', baseUrl);\r\n backendSignInUrl.searchParams.set('redirect', request.url);\r\n return NextResponse.redirect(backendSignInUrl);\r\n } else {\r\n // Custom auth: redirect to local sign-in page\r\n const localSignInUrl = new URL(signInUrl, request.url);\r\n localSignInUrl.searchParams.set('redirect', pathname);\r\n return NextResponse.redirect(localSignInUrl);\r\n }\r\n }\r\n\r\n // Token exists in cookie, allow request to continue\r\n return NextResponse.next();\r\n };\r\n}"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,oBAA0C;AAsHnC,SAAS,mBAAmB,QAAkC;AACnE,QAAM;AAAA,IACJ;AAAA,IACA,eAAe,CAAC,GAAG;AAAA,IACnB,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,iBAAiB;AAAA,EACnB,IAAI;AAEJ,SAAO,eAAe,WAAW,SAAsB;AACrD,UAAM,EAAE,UAAU,aAAa,IAAI,QAAQ;AAG3C,UAAM,cAAc,aAAa,IAAI,cAAc;AAEnD,QAAI,aAAa;AAEf,YAAM,WAAW,2BAAa,SAAS,IAAI,IAAI,UAAU,QAAQ,GAAG,CAAC;AAGrE,eAAS,QAAQ,IAAI,YAAY,aAAa;AAAA,QAC5C,UAAU;AAAA,QACV,QAAQ,QAAQ,IAAI,aAAa;AAAA,QACjC,UAAU;AAAA,QACV,MAAM;AAAA,QACN,QAAQ,KAAK,KAAK,KAAK;AAAA;AAAA,MACzB,CAAC;AAED,aAAO;AAAA,IACT;AAIA,QAAI,mBAAmB,aAAa,aAAa,aAAa,YAAY;AAExE,YAAM,aAAa,QAAQ;AAC3B,YAAM,kBAAkB,aAAa,YAAY,kBAAkB;AACnE,YAAM,iBAAiB,IAAI,IAAI,iBAAiB,OAAO;AACvD,qBAAe,aAAa,IAAI,YAAY,UAAU;AACtD,aAAO,2BAAa,SAAS,eAAe,SAAS,CAAC;AAAA,IACxD;AAGA,UAAM,gBAAgB,aAAa,KAAK,CAAC,UAAU;AACjD,UAAI,MAAM,SAAS,GAAG,GAAG;AAEvB,eAAO,SAAS,WAAW,MAAM,MAAM,GAAG,EAAE,CAAC;AAAA,MAC/C;AAEA,aAAO,aAAa,SAAS,SAAS,WAAW,QAAQ,GAAG;AAAA,IAC9D,CAAC;AAED,QAAI,eAAe;AACjB,aAAO,2BAAa,KAAK;AAAA,IAC3B;AAGA,UAAM,QAAQ,QAAQ,QAAQ,IAAI,UAAU,GAAG;AAE/C,QAAI,CAAC,OAAO;AAEV,UAAI,gBAAgB;AAGlB,cAAM,mBAAmB,IAAI,IAAI,iBAAiB,OAAO;AACzD,yBAAiB,aAAa,IAAI,YAAY,QAAQ,GAAG;AACzD,eAAO,2BAAa,SAAS,gBAAgB;AAAA,MAC/C,OAAO;AAEL,cAAM,iBAAiB,IAAI,IAAI,WAAW,QAAQ,GAAG;AACrD,uBAAe,aAAa,IAAI,YAAY,QAAQ;AACpD,eAAO,2BAAa,SAAS,cAAc;AAAA,MAC7C;AAAA,IACF;AAGA,WAAO,2BAAa,KAAK;AAAA,EAC3B;AACF;","names":[]}
@@ -5,23 +5,33 @@ import { NextResponse } from "next/server";
5
5
  function InsforgeMiddleware(config) {
6
6
  const {
7
7
  baseUrl,
8
- publicRoutes = ["/auth/callback", "/"],
8
+ publicRoutes = ["/"],
9
9
  signInUrl = "/sign-in",
10
10
  signUpUrl = "/sign-up",
11
11
  cookieName = "insforge_token",
12
12
  useBuiltInAuth = true
13
13
  } = config;
14
14
  return async function middleware(request) {
15
- const { pathname } = request.nextUrl;
16
- if (pathname === "/auth/callback") {
17
- return NextResponse.next();
15
+ const { pathname, searchParams } = request.nextUrl;
16
+ const accessToken = searchParams.get("access_token");
17
+ if (accessToken) {
18
+ const response = NextResponse.redirect(new URL(pathname, request.url));
19
+ response.cookies.set(cookieName, accessToken, {
20
+ httpOnly: true,
21
+ secure: process.env.NODE_ENV === "production",
22
+ sameSite: "lax",
23
+ path: "/",
24
+ maxAge: 60 * 60 * 24 * 7
25
+ // 7 days
26
+ });
27
+ return response;
18
28
  }
19
29
  if (useBuiltInAuth && (pathname === signInUrl || pathname === signUpUrl)) {
20
- const callbackUrl = new URL("/auth/callback", request.url);
30
+ const currentUrl = request.url;
21
31
  const backendAuthPath = pathname === signInUrl ? "/auth/sign-in" : "/auth/sign-up";
22
- const ossAuthUrl = new URL(backendAuthPath, baseUrl);
23
- ossAuthUrl.searchParams.set("redirect", callbackUrl.toString());
24
- return NextResponse.redirect(ossAuthUrl.toString());
32
+ const backendAuthUrl = new URL(backendAuthPath, baseUrl);
33
+ backendAuthUrl.searchParams.set("redirect", currentUrl);
34
+ return NextResponse.redirect(backendAuthUrl.toString());
25
35
  }
26
36
  const isPublicRoute = publicRoutes.some((route) => {
27
37
  if (route.endsWith("*")) {
@@ -35,9 +45,8 @@ function InsforgeMiddleware(config) {
35
45
  const token = request.cookies.get(cookieName)?.value;
36
46
  if (!token) {
37
47
  if (useBuiltInAuth) {
38
- const callbackUrl = new URL("/auth/callback", request.url);
39
48
  const backendSignInUrl = new URL("/auth/sign-in", baseUrl);
40
- backendSignInUrl.searchParams.set("redirect", callbackUrl.toString());
49
+ backendSignInUrl.searchParams.set("redirect", request.url);
41
50
  return NextResponse.redirect(backendSignInUrl);
42
51
  } else {
43
52
  const localSignInUrl = new URL(signInUrl, request.url);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/middleware/InsforgeMiddleware.ts"],"sourcesContent":["import { NextRequest, NextResponse } from 'next/server';\r\n\r\nexport interface InsforgeMiddlewareConfig {\r\n /**\r\n * Base URL of your Insforge backend\r\n * @example 'https://your-backend.com' or 'http://localhost:3001'\r\n */\r\n baseUrl: string;\r\n\r\n /**\r\n * Routes that are public and don't require authentication\r\n * Note: `/auth/callback` is automatically treated as public\r\n * @default ['/auth/callback', '/']\r\n * @example ['/sign-in', '/sign-up', '/', '/about']\r\n */\r\n publicRoutes?: string[];\r\n\r\n /**\r\n * Local route path for sign-in (can be customized to any path like '/login')\r\n * When using built-in auth, this will redirect to backend's `/auth/sign-in`\r\n * @default '/sign-in'\r\n */\r\n signInUrl?: string;\r\n\r\n /**\r\n * Local route path for sign-up (can be customized to any path like '/register')\r\n * When using built-in auth, this will redirect to backend's `/auth/sign-up`\r\n * @default '/sign-up'\r\n */\r\n signUpUrl?: string;\r\n\r\n /**\r\n * Cookie name for the auth token\r\n * @default 'insforge_token'\r\n */\r\n cookieName?: string;\r\n\r\n /**\r\n * Whether to use built-in authentication pages hosted on the backend\r\n * - When true: redirects to backend's `/auth/sign-in` and `/auth/sign-up` pages\r\n * - When false: redirects to local sign-in/sign-up pages (you provide your own components)\r\n * @default true\r\n */\r\n useBuiltInAuth?: boolean;\r\n}\r\n\r\n/**\r\n * Creates Next.js middleware for protecting routes with Insforge authentication.\r\n * \r\n * This middleware provides lightweight route protection by:\r\n * - Checking for auth token presence in cookies\r\n * - Redirecting unauthenticated users to sign-in page\r\n * - Allowing public routes to be accessed without authentication\r\n * - Always allowing `/auth/callback` through (for token extraction)\r\n * - Mapping local auth routes to backend's fixed paths when using built-in auth\r\n * \r\n * **Important Notes:**\r\n * - This middleware only checks if a token exists, it doesn't validate it\r\n * - `/auth/callback` is automatically treated as public (handles query param tokens)\r\n * - When `useBuiltInAuth: true`, local routes map to backend's `/auth/sign-in` and `/auth/sign-up`\r\n * - You can customize local route paths (e.g., `/login`) while backend paths remain fixed\r\n * \r\n * @param config - Middleware configuration\r\n * @returns Next.js middleware function\r\n *\r\n * @example\r\n * ```ts\r\n * // middleware.ts - Using built-in auth with separate frontend/backend URLs\r\n * import { InsforgeMiddleware } from '@insforge/nextjs/middleware';\r\n *\r\n * export default InsforgeMiddleware({\r\n * baseUrl: process.env.INSFORGE_BASE_URL!, // Backend: http://localhost:3001\r\n * frontendUrl: process.env.INSFORGE_FRONTEND_URL!, // Frontend: http://localhost:3000\r\n * publicRoutes: ['/', '/about'], // /auth/callback is auto-included\r\n * useBuiltInAuth: true, // Use backend's auth pages\r\n * });\r\n *\r\n * export const config = {\r\n * matcher: ['/((?!_next|api|.*\\\\..*).*)'],\r\n * };\r\n * ```\r\n * \r\n * @example\r\n * ```ts\r\n * // middleware.ts - Custom local auth pages with custom paths\r\n * import { InsforgeMiddleware } from '@insforge/nextjs/middleware';\r\n *\r\n * export default InsforgeMiddleware({\r\n * baseUrl: process.env.INSFORGE_BASE_URL!,\r\n * publicRoutes: ['/login', '/register', '/', '/about'],\r\n * signInUrl: '/login', // Custom local path\r\n * signUpUrl: '/register', // Custom local path\r\n * useBuiltInAuth: false, // Use your own SignIn/SignUp components\r\n * });\r\n *\r\n * export const config = {\r\n * matcher: ['/((?!_next|api|.*\\\\..*).*)'],\r\n * };\r\n * ```\r\n * \r\n * @example\r\n * ```ts\r\n * // middleware.ts - Built-in auth with custom local route names\r\n * // Local '/login' and '/register' routes redirect to backend's '/auth/sign-in' and '/auth/sign-up'\r\n * import { InsforgeMiddleware } from '@insforge/nextjs/middleware';\r\n *\r\n * export default InsforgeMiddleware({\r\n * baseUrl: 'https://your-backend.com',\r\n * signInUrl: '/login', // Local path (redirects to backend /auth/sign-in)\r\n * signUpUrl: '/register', // Local path (redirects to backend /auth/sign-up)\r\n * useBuiltInAuth: true,\r\n * });\r\n * ```\r\n */\r\nexport function InsforgeMiddleware(config: InsforgeMiddlewareConfig) {\r\n const {\r\n baseUrl,\r\n publicRoutes = [\"/auth/callback\", \"/\"],\r\n signInUrl = '/sign-in',\r\n signUpUrl = '/sign-up',\r\n cookieName = 'insforge_token',\r\n useBuiltInAuth = true,\r\n } = config;\r\n\r\n return async function middleware(request: NextRequest) {\r\n const { pathname } = request.nextUrl;\r\n\r\n // STEP 1: Always allow /auth/callback through\r\n // This route must be public because it receives the token as a query parameter\r\n // and the InsforgeProvider processes it on the client side\r\n if (pathname === '/auth/callback') {\r\n return NextResponse.next();\r\n }\r\n\r\n // STEP 2: Handle built-in auth redirects\r\n // Map local sign-in/sign-up routes to backend's fixed auth pages\r\n // Example: local '/login' → backend '/auth/sign-in'\r\n if (useBuiltInAuth && (pathname === signInUrl || pathname === signUpUrl)) {\r\n const callbackUrl = new URL('/auth/callback', request.url);\r\n // Backend auth routes are FIXED: /auth/sign-in and /auth/sign-up (with hyphen)\r\n const backendAuthPath = pathname === signInUrl ? '/auth/sign-in' : '/auth/sign-up';\r\n const ossAuthUrl = new URL(backendAuthPath, baseUrl);\r\n ossAuthUrl.searchParams.set('redirect', callbackUrl.toString());\r\n return NextResponse.redirect(ossAuthUrl.toString());\r\n }\r\n\r\n // STEP 3: Check if route is public\r\n const isPublicRoute = publicRoutes.some((route) => {\r\n if (route.endsWith('*')) {\r\n // Wildcard route: /admin/* matches /admin/anything\r\n return pathname.startsWith(route.slice(0, -1));\r\n }\r\n // Exact match or starts with route path\r\n return pathname === route || pathname.startsWith(route + '/');\r\n });\r\n\r\n if (isPublicRoute) {\r\n return NextResponse.next();\r\n }\r\n\r\n // STEP 4: Check for authentication token in cookies\r\n // Note: We only check if token exists, we don't validate it here\r\n const token = request.cookies.get(cookieName)?.value;\r\n\r\n if (!token) {\r\n // No token found, redirect to sign-in\r\n if (useBuiltInAuth) {\r\n // Built-in auth: redirect to backend's /auth/sign-in page\r\n const callbackUrl = new URL('/auth/callback', request.url);\r\n const backendSignInUrl = new URL('/auth/sign-in', baseUrl);\r\n backendSignInUrl.searchParams.set('redirect', callbackUrl.toString());\r\n return NextResponse.redirect(backendSignInUrl);\r\n } else {\r\n // Custom auth: redirect to local sign-in page\r\n const localSignInUrl = new URL(signInUrl, request.url);\r\n localSignInUrl.searchParams.set('redirect', pathname);\r\n return NextResponse.redirect(localSignInUrl);\r\n }\r\n }\r\n\r\n // Token exists, allow request to continue\r\n return NextResponse.next();\r\n };\r\n}"],"mappings":";;;AAAA,SAAsB,oBAAoB;AAkHnC,SAAS,mBAAmB,QAAkC;AACnE,QAAM;AAAA,IACJ;AAAA,IACA,eAAe,CAAC,kBAAkB,GAAG;AAAA,IACrC,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,iBAAiB;AAAA,EACnB,IAAI;AAEJ,SAAO,eAAe,WAAW,SAAsB;AACrD,UAAM,EAAE,SAAS,IAAI,QAAQ;AAK7B,QAAI,aAAa,kBAAkB;AACjC,aAAO,aAAa,KAAK;AAAA,IAC3B;AAKA,QAAI,mBAAmB,aAAa,aAAa,aAAa,YAAY;AACxE,YAAM,cAAc,IAAI,IAAI,kBAAkB,QAAQ,GAAG;AAEzD,YAAM,kBAAkB,aAAa,YAAY,kBAAkB;AACnE,YAAM,aAAa,IAAI,IAAI,iBAAiB,OAAO;AACnD,iBAAW,aAAa,IAAI,YAAY,YAAY,SAAS,CAAC;AAC9D,aAAO,aAAa,SAAS,WAAW,SAAS,CAAC;AAAA,IACpD;AAGA,UAAM,gBAAgB,aAAa,KAAK,CAAC,UAAU;AACjD,UAAI,MAAM,SAAS,GAAG,GAAG;AAEvB,eAAO,SAAS,WAAW,MAAM,MAAM,GAAG,EAAE,CAAC;AAAA,MAC/C;AAEA,aAAO,aAAa,SAAS,SAAS,WAAW,QAAQ,GAAG;AAAA,IAC9D,CAAC;AAED,QAAI,eAAe;AACjB,aAAO,aAAa,KAAK;AAAA,IAC3B;AAIA,UAAM,QAAQ,QAAQ,QAAQ,IAAI,UAAU,GAAG;AAE/C,QAAI,CAAC,OAAO;AAEV,UAAI,gBAAgB;AAElB,cAAM,cAAc,IAAI,IAAI,kBAAkB,QAAQ,GAAG;AACzD,cAAM,mBAAmB,IAAI,IAAI,iBAAiB,OAAO;AACzD,yBAAiB,aAAa,IAAI,YAAY,YAAY,SAAS,CAAC;AACpE,eAAO,aAAa,SAAS,gBAAgB;AAAA,MAC/C,OAAO;AAEL,cAAM,iBAAiB,IAAI,IAAI,WAAW,QAAQ,GAAG;AACrD,uBAAe,aAAa,IAAI,YAAY,QAAQ;AACpD,eAAO,aAAa,SAAS,cAAc;AAAA,MAC7C;AAAA,IACF;AAGA,WAAO,aAAa,KAAK;AAAA,EAC3B;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/middleware/InsforgeMiddleware.ts"],"sourcesContent":["import { NextRequest, NextResponse } from 'next/server';\r\n\r\nexport interface InsforgeMiddlewareConfig {\r\n /**\r\n * Base URL of your Insforge backend\r\n * @example 'https://your-backend.com' or 'http://localhost:3001'\r\n */\r\n baseUrl: string;\r\n\r\n /**\r\n * Routes that are public and don't require authentication\r\n * @default ['/']\r\n * @example ['/sign-in', '/sign-up', '/', '/about']\r\n */\r\n publicRoutes?: string[];\r\n\r\n /**\r\n * Local route path for sign-in (can be customized to any path like '/login')\r\n * When using built-in auth, this will redirect to backend's `/auth/sign-in`\r\n * @default '/sign-in'\r\n */\r\n signInUrl?: string;\r\n\r\n /**\r\n * Local route path for sign-up (can be customized to any path like '/register')\r\n * When using built-in auth, this will redirect to backend's `/auth/sign-up`\r\n * @default '/sign-up'\r\n */\r\n signUpUrl?: string;\r\n\r\n /**\r\n * Cookie name for the auth token\r\n * @default 'insforge_token'\r\n */\r\n cookieName?: string;\r\n\r\n /**\r\n * Whether to use built-in authentication pages hosted on the backend\r\n * - When true: redirects to backend's `/auth/sign-in` and `/auth/sign-up` pages\r\n * - When false: redirects to local sign-in/sign-up pages (you provide your own components)\r\n * @default true\r\n */\r\n useBuiltInAuth?: boolean;\r\n}\r\n\r\n/**\r\n * Creates Next.js middleware for protecting routes with Insforge authentication.\r\n * \r\n * This middleware provides lightweight route protection by:\r\n * - Detecting and storing auth tokens from URL parameters (after backend redirect)\r\n * - Checking for auth token presence in cookies\r\n * - Redirecting unauthenticated users to sign-in page\r\n * - Allowing public routes to be accessed without authentication\r\n * - Mapping local auth routes to backend's fixed paths when using built-in auth\r\n * \r\n * **How Authentication Flow Works:**\r\n * 1. User visits protected route → Middleware checks for token\r\n * 2. No token → Redirects to sign-in (backend or local)\r\n * 3. After sign-in Backend redirects to `yourapp.com/destination?access_token=xxx&user_id=xxx...`\r\n * 4. Middleware detects `access_token` in URL → Stores in HTTP-only cookie → Cleans URL → Allows access\r\n * 5. SDK also detects token from URL → Stores in localStorage → Updates auth state\r\n * \r\n * **Important Notes:**\r\n * - This middleware only checks if a token exists, it doesn't validate it\r\n * - Tokens from URL are automatically extracted and stored in cookies\r\n * - When `useBuiltInAuth: true`, local routes map to backend's `/auth/sign-in` and `/auth/sign-up`\r\n * - You can customize local route paths (e.g., `/login`) while backend paths remain fixed\r\n * \r\n * @param config - Middleware configuration\r\n * @returns Next.js middleware function\r\n *\r\n * @example\r\n * ```ts\r\n * // middleware.ts - Using built-in auth\r\n * import { InsforgeMiddleware } from '@insforge/nextjs/middleware';\r\n *\r\n * export default InsforgeMiddleware({\r\n * baseUrl: process.env.INSFORGE_BASE_URL!,\r\n * publicRoutes: ['/', '/about'],\r\n * useBuiltInAuth: true,\r\n * });\r\n *\r\n * export const config = {\r\n * matcher: ['/((?!_next|api|.*\\\\..*).*)'],\r\n * };\r\n * ```\r\n * \r\n * @example\r\n * ```ts\r\n * // middleware.ts - Custom local auth pages with custom paths\r\n * import { InsforgeMiddleware } from '@insforge/nextjs/middleware';\r\n *\r\n * export default InsforgeMiddleware({\r\n * baseUrl: process.env.INSFORGE_BASE_URL!,\r\n * publicRoutes: ['/login', '/register', '/', '/about'],\r\n * signInUrl: '/login',\r\n * signUpUrl: '/register',\r\n * useBuiltInAuth: false,\r\n * });\r\n *\r\n * export const config = {\r\n * matcher: ['/((?!_next|api|.*\\\\..*).*)'],\r\n * };\r\n * ```\r\n * \r\n * @example\r\n * ```ts\r\n * // middleware.ts - Built-in auth with custom local route names\r\n * import { InsforgeMiddleware } from '@insforge/nextjs/middleware';\r\n *\r\n * export default InsforgeMiddleware({\r\n * baseUrl: 'https://your-backend.com',\r\n * signInUrl: '/login',\r\n * signUpUrl: '/register',\r\n * useBuiltInAuth: true,\r\n * });\r\n * ```\r\n */\r\nexport function InsforgeMiddleware(config: InsforgeMiddlewareConfig) {\r\n const {\r\n baseUrl,\r\n publicRoutes = [\"/\"],\r\n signInUrl = '/sign-in',\r\n signUpUrl = '/sign-up',\r\n cookieName = 'insforge_token',\r\n useBuiltInAuth = true,\r\n } = config;\r\n\r\n return async function middleware(request: NextRequest) {\r\n const { pathname, searchParams } = request.nextUrl;\r\n\r\n // STEP 1: Check if URL contains access_token (from backend redirect after auth)\r\n const accessToken = searchParams.get('access_token');\r\n \r\n if (accessToken) {\r\n // Token detected in URL - store it in HTTP-only cookie and clean URL\r\n const response = NextResponse.redirect(new URL(pathname, request.url));\r\n \r\n // Set HTTP-only cookie with secure settings\r\n response.cookies.set(cookieName, accessToken, {\r\n httpOnly: true,\r\n secure: process.env.NODE_ENV === 'production',\r\n sameSite: 'lax',\r\n path: '/',\r\n maxAge: 60 * 60 * 24 * 7, // 7 days\r\n });\r\n \r\n return response;\r\n }\r\n\r\n // STEP 2: Handle built-in auth redirects for sign-in/sign-up pages\r\n // Map local sign-in/sign-up routes to backend's fixed auth pages\r\n if (useBuiltInAuth && (pathname === signInUrl || pathname === signUpUrl)) {\r\n // Backend redirects directly to the current page with token in URL\r\n const currentUrl = request.url;\r\n const backendAuthPath = pathname === signInUrl ? '/auth/sign-in' : '/auth/sign-up';\r\n const backendAuthUrl = new URL(backendAuthPath, baseUrl);\r\n backendAuthUrl.searchParams.set('redirect', currentUrl);\r\n return NextResponse.redirect(backendAuthUrl.toString());\r\n }\r\n\r\n // STEP 3: Check if route is public\r\n const isPublicRoute = publicRoutes.some((route) => {\r\n if (route.endsWith('*')) {\r\n // Wildcard route: /admin/* matches /admin/anything\r\n return pathname.startsWith(route.slice(0, -1));\r\n }\r\n // Exact match or starts with route path\r\n return pathname === route || pathname.startsWith(route + '/');\r\n });\r\n\r\n if (isPublicRoute) {\r\n return NextResponse.next();\r\n }\r\n\r\n // STEP 4: Check for authentication token in cookies\r\n const token = request.cookies.get(cookieName)?.value;\r\n\r\n if (!token) {\r\n // No token found, redirect to sign-in\r\n if (useBuiltInAuth) {\r\n // Built-in auth: redirect to backend's /auth/sign-in page\r\n // Backend will redirect back to current URL with token\r\n const backendSignInUrl = new URL('/auth/sign-in', baseUrl);\r\n backendSignInUrl.searchParams.set('redirect', request.url);\r\n return NextResponse.redirect(backendSignInUrl);\r\n } else {\r\n // Custom auth: redirect to local sign-in page\r\n const localSignInUrl = new URL(signInUrl, request.url);\r\n localSignInUrl.searchParams.set('redirect', pathname);\r\n return NextResponse.redirect(localSignInUrl);\r\n }\r\n }\r\n\r\n // Token exists in cookie, allow request to continue\r\n return NextResponse.next();\r\n };\r\n}"],"mappings":";;;AAAA,SAAsB,oBAAoB;AAsHnC,SAAS,mBAAmB,QAAkC;AACnE,QAAM;AAAA,IACJ;AAAA,IACA,eAAe,CAAC,GAAG;AAAA,IACnB,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,iBAAiB;AAAA,EACnB,IAAI;AAEJ,SAAO,eAAe,WAAW,SAAsB;AACrD,UAAM,EAAE,UAAU,aAAa,IAAI,QAAQ;AAG3C,UAAM,cAAc,aAAa,IAAI,cAAc;AAEnD,QAAI,aAAa;AAEf,YAAM,WAAW,aAAa,SAAS,IAAI,IAAI,UAAU,QAAQ,GAAG,CAAC;AAGrE,eAAS,QAAQ,IAAI,YAAY,aAAa;AAAA,QAC5C,UAAU;AAAA,QACV,QAAQ,QAAQ,IAAI,aAAa;AAAA,QACjC,UAAU;AAAA,QACV,MAAM;AAAA,QACN,QAAQ,KAAK,KAAK,KAAK;AAAA;AAAA,MACzB,CAAC;AAED,aAAO;AAAA,IACT;AAIA,QAAI,mBAAmB,aAAa,aAAa,aAAa,YAAY;AAExE,YAAM,aAAa,QAAQ;AAC3B,YAAM,kBAAkB,aAAa,YAAY,kBAAkB;AACnE,YAAM,iBAAiB,IAAI,IAAI,iBAAiB,OAAO;AACvD,qBAAe,aAAa,IAAI,YAAY,UAAU;AACtD,aAAO,aAAa,SAAS,eAAe,SAAS,CAAC;AAAA,IACxD;AAGA,UAAM,gBAAgB,aAAa,KAAK,CAAC,UAAU;AACjD,UAAI,MAAM,SAAS,GAAG,GAAG;AAEvB,eAAO,SAAS,WAAW,MAAM,MAAM,GAAG,EAAE,CAAC;AAAA,MAC/C;AAEA,aAAO,aAAa,SAAS,SAAS,WAAW,QAAQ,GAAG;AAAA,IAC9D,CAAC;AAED,QAAI,eAAe;AACjB,aAAO,aAAa,KAAK;AAAA,IAC3B;AAGA,UAAM,QAAQ,QAAQ,QAAQ,IAAI,UAAU,GAAG;AAE/C,QAAI,CAAC,OAAO;AAEV,UAAI,gBAAgB;AAGlB,cAAM,mBAAmB,IAAI,IAAI,iBAAiB,OAAO;AACzD,yBAAiB,aAAa,IAAI,YAAY,QAAQ,GAAG;AACzD,eAAO,aAAa,SAAS,gBAAgB;AAAA,MAC/C,OAAO;AAEL,cAAM,iBAAiB,IAAI,IAAI,WAAW,QAAQ,GAAG;AACrD,uBAAe,aAAa,IAAI,YAAY,QAAQ;AACpD,eAAO,aAAa,SAAS,cAAc;AAAA,MAC7C;AAAA,IACF;AAGA,WAAO,aAAa,KAAK;AAAA,EAC3B;AACF;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@insforge/nextjs",
3
- "version": "0.8.8",
3
+ "version": "0.8.10",
4
4
  "description": "Pre-built authentication UI components for Next.js with Insforge backend - zero configuration required",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -50,7 +50,7 @@
50
50
  "react-dom": "^19.0.0"
51
51
  },
52
52
  "dependencies": {
53
- "@insforge/react": "^0.3.2",
53
+ "@insforge/react": "^0.4.6",
54
54
  "@insforge/sdk": "0.0.58-dev.13",
55
55
  "@insforge/shared-schemas": "^1.1.17",
56
56
  "clsx": "^2.1.1",