UNPKG

@frank-auth/react

Version:

Flexible and customizable React UI components for Frank Authentication

1 lines 47.9 kB
{"version":3,"file":"index.cjs","sources":["../../../src/middleware/index.ts"],"sourcesContent":["/**\n * @frank-auth/react - Next.js Middleware Plugin\n *\n * Comprehensive middleware solution for Next.js applications using Frank Auth.\n * Provides authentication routing, session management, and path protection.\n * Now integrated with the storage system for consistent token management.\n */\n\nimport type { AuthStatus, Session, User, UserType } from \"@frank-auth/client\";\nimport {\n\tAuthSDK,\n\tNextJSCookieContext,\n\tcreateHybridAuthStorage,\n} from \"@frank-auth/sdk\";\nimport { NextRequest, NextResponse } from \"next/server\";\nimport type { FrankAuthConfig } from \"../types\";\n\n// ============================================================================\n// Types and Interfaces\n// ============================================================================\n\nexport interface MiddlewareConfig\n\textends Omit<FrankAuthConfig, \"enableDevMode\"> {\n\tstorageKeyPrefix?: string;\n\tsessionCookieName?: string;\n\tuserType?: UserType;\n\tprojectId?: string;\n\tsecretKey?: string;\n\n\t/**\n\t * Paths that are publicly accessible without authentication\n\t * @default []\n\t */\n\tpublicPaths?: string[];\n\n\t/**\n\t * Paths that require authentication (when allPathsPrivate is false)\n\t * @default []\n\t */\n\tprivatePaths?: string[];\n\n\t/**\n\t * Authentication paths that redirect authenticated users away\n\t * These paths are accessible to unauthenticated users but will\n\t * redirect authenticated users to afterSignInPath\n\t * @default ['/sign-in', '/sign-up', '/forgot-password', '/verify-email', '/reset-password']\n\t */\n\tauthPaths?: string[];\n\n\t/**\n\t * Whether all paths are private by default (recommended)\n\t * @default true\n\t */\n\tallPathsPrivate?: boolean;\n\n\t/**\n\t * Path to redirect to for sign in\n\t * @default '/sign-in'\n\t */\n\tsignInPath?: string;\n\n\t/**\n\t * Path to redirect to for sign up\n\t * @default '/sign-up'\n\t */\n\tsignUpPath?: string;\n\n\t/**\n\t * Path to redirect to after successful sign in\n\t * @default '/dashboard'\n\t */\n\tafterSignInPath?: string;\n\n\t/**\n\t * Path to redirect to after successful sign up\n\t * @default '/dashboard'\n\t */\n\tafterSignUpPath?: string;\n\n\t/**\n\t * Path to redirect to after sign out\n\t * @default '/'\n\t */\n\tafterSignOutPath?: string;\n\n\t/**\n\t * Organization selection path for multi-tenant apps\n\t * @default '/select-organization'\n\t */\n\torgSelectionPath?: string;\n\n\t/**\n\t * Custom matcher function for protected routes\n\t */\n\tmatcher?: (path: string) => boolean;\n\n\t/**\n\t * Enable debug logging\n\t * @default false\n\t */\n\tdebug?: boolean;\n\n\t/**\n\t * Custom domain for organization detection\n\t */\n\tcustomDomain?: string;\n\n\t/**\n\t * Enable organization-based routing\n\t * @default false\n\t */\n\tenableOrgRouting?: boolean;\n\n\t/**\n\t * Ignore paths (will not be processed by middleware)\n\t * @default ['/api', '/_next', '/favicon.ico']\n\t */\n\tignorePaths?: string[];\n\n\t/**\n\t * Cookie options for session management\n\t */\n\tcookieOptions?: {\n\t\tsecure?: boolean;\n\t\thttpOnly?: boolean;\n\t\tsameSite?: \"strict\" | \"lax\" | \"none\";\n\t\tdomain?: string;\n\t\tmaxAge?: number;\n\t};\n\n\t/**\n\t * Custom hooks for middleware lifecycle\n\t */\n\thooks?: MiddlewareHooks;\n\n\t/**\n\t * Skip API calls on network errors (useful for development)\n\t * @default false\n\t */\n\tskipApiCallOnNetworkError?: boolean;\n\n\t/**\n\t * Maximum number of retries for API calls\n\t * @default 2\n\t */\n\tmaxRetries?: number;\n\n\t/**\n\t * Timeout for API calls in milliseconds\n\t * @default 5000\n\t */\n\tapiTimeout?: number;\n\n\t/**\n\t * Fallback to local token validation on network errors\n\t * @default true\n\t */\n\tfallbackToLocalTokens?: boolean;\n\n\t/**\n\t * Custom API endpoint override for testing\n\t */\n\tcustomApiEndpoint?: string;\n\n\t/**\n\t * Enable offline mode (skip all API calls)\n\t * @default false\n\t */\n\tofflineMode?: boolean;\n}\n\nexport interface MiddlewareHooks {\n\t/**\n\t * Called before authentication check\n\t */\n\tbeforeAuth?: (req: NextRequest) => Promise<NextRequest | NextResponse>;\n\n\t/**\n\t * Called after authentication check\n\t */\n\tafterAuth?: (\n\t\treq: NextRequest,\n\t\tres: NextResponse,\n\t\tauth: AuthResult,\n\t) => Promise<NextRequest | NextResponse>;\n\n\t/**\n\t * Called when user is authenticated\n\t */\n\tonAuthenticated?: (\n\t\treq: NextRequest,\n\t\tuser: User,\n\t\tsession: Session,\n\t) => Promise<NextRequest | NextResponse>;\n\n\t/**\n\t * Called when user is not authenticated\n\t */\n\tonUnauthenticated?: (req: NextRequest) => Promise<NextRequest | NextResponse>;\n\n\t/**\n\t * Called when organization is required but not selected\n\t */\n\tonOrganizationRequired?: (\n\t\treq: NextRequest,\n\t\tuser: User,\n\t) => Promise<NextRequest | NextResponse>;\n\n\t/**\n\t * Called on authentication error\n\t */\n\tonError?: (\n\t\treq: NextRequest,\n\t\terror: Error,\n\t) => Promise<NextRequest | NextResponse>;\n}\n\nexport interface AuthResult {\n\tisAuthenticated: boolean;\n\tuser: User | null;\n\tsession: Session | null;\n\torganizationId: string | null;\n\terror: Error | null;\n\ttokenInfo?: {\n\t\taccessTokenExpired: boolean;\n\t\trefreshTokenExpired: boolean;\n\t\tcanRefresh: boolean;\n\t};\n}\n\nexport interface MiddlewareContext {\n\treq: NextRequest;\n\tconfig: Required<MiddlewareConfig>;\n\tauth: AuthResult;\n\tauthSDK: AuthSDK;\n\tpath: string;\n\tisPublicPath: boolean;\n\tisPrivatePath: boolean;\n\tisAuthPath: boolean;\n\tresponse: NextResponse;\n}\n\n// ============================================================================\n// Default Configuration\n// ============================================================================\n\nconst DEFAULT_MIDDLEWARE_CONFIG: Partial<MiddlewareConfig> = {\n\tapiUrl: \"http://localhost:8990\",\n\tsessionCookieName: \"frank_sid\",\n\tstorageKeyPrefix: \"frank_auth_\",\n\tpublicPaths: [],\n\tprivatePaths: [],\n\tauthPaths: [\n\t\t\"/sign-in\",\n\t\t\"/sign-up\",\n\t\t\"/forgot-password\",\n\t\t\"/verify-email\",\n\t\t\"/reset-password\",\n\t],\n\tskipApiCallOnNetworkError: false,\n\tmaxRetries: 2,\n\tapiTimeout: 5000,\n\tfallbackToLocalTokens: true,\n\tofflineMode: false,\n\tallPathsPrivate: true,\n\tsignInPath: \"/sign-in\",\n\tsignUpPath: \"/sign-up\",\n\tafterSignInPath: \"/dashboard\",\n\tafterSignUpPath: \"/dashboard\",\n\tafterSignOutPath: \"/\",\n\torgSelectionPath: \"/select-organization\",\n\tdebug: false,\n\tenableOrgRouting: false,\n\tignorePaths: [\n\t\t\"/api\",\n\t\t\"/_next\",\n\t\t\"/favicon.ico\",\n\t\t\"/images\",\n\t\t\"/static\",\n\t\t\"/_vercel\",\n\t],\n\tcookieOptions: {\n\t\tsecure: process.env.NODE_ENV === \"production\",\n\t\thttpOnly: true,\n\t\tsameSite: \"lax\",\n\t\tmaxAge: 60 * 60 * 24 * 7, // 7 days\n\t},\n};\n\n// ============================================================================\n// Utility Functions\n// ============================================================================\n\n/**\n * Check if a path matches any of the patterns\n */\nfunction matchesPath(path: string, patterns: string[]): boolean {\n\treturn patterns.some((pattern) => {\n\t\tif (pattern === path) return true;\n\t\tif (pattern.endsWith(\"*\")) {\n\t\t\tconst prefix = pattern.slice(0, -1);\n\t\t\treturn path.startsWith(prefix);\n\t\t}\n\t\tif (pattern.startsWith(\"/\") && pattern.endsWith(\"/\")) {\n\t\t\tconst regex = new RegExp(pattern.slice(1, -1));\n\t\t\treturn regex.test(path);\n\t\t}\n\t\treturn false;\n\t});\n}\n\n/**\n * Create a NextJS cookie context from request and response\n */\nfunction createCookieContext(\n\treq: NextRequest,\n\tresponse: NextResponse,\n\tconfig: Required<MiddlewareConfig>,\n): NextJSCookieContext {\n\t// Create a proper request object with cookies\n\tconst cookies = req.cookies.getAll();\n\tconst cookieReq = {\n\t\tcookies: {\n\t\t\t...Object.fromEntries(\n\t\t\t\tcookies.map((cookie) => [cookie.name, cookie.value]),\n\t\t\t),\n\t\t\t// Also provide a get method for Next.js cookie compatibility\n\t\t\tget: (name: string) => req.cookies.get(name),\n\t\t\tgetAll: () => req.cookies.getAll(),\n\t\t},\n\t};\n\n\t// Create a response object that can handle Set-Cookie headers\n\tconst cookieRes = {\n\t\tsetHeader: (name: string, value: string | string[]) => {\n\t\t\tif (name === \"Set-Cookie\") {\n\t\t\t\tconst cookies = Array.isArray(value) ? value : [value];\n\t\t\t\tfor (const cookie of cookies) {\n\t\t\t\t\t// Parse the cookie string properly\n\t\t\t\t\tconst [nameValue, ...optionParts] = cookie.split(\";\");\n\t\t\t\t\tconst [cookieName, cookieValue] = nameValue.split(\"=\");\n\n\t\t\t\t\tif (cookieName && cookieValue) {\n\t\t\t\t\t\t// Parse cookie options\n\t\t\t\t\t\tconst options: any = {\n\t\t\t\t\t\t\thttpOnly: config.cookieOptions.httpOnly,\n\t\t\t\t\t\t\tsecure: config.cookieOptions.secure,\n\t\t\t\t\t\t\tsameSite: config.cookieOptions.sameSite,\n\t\t\t\t\t\t\tmaxAge: config.cookieOptions.maxAge,\n\t\t\t\t\t\t\tpath: \"/\", // Ensure path is set\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\t// Override with parsed options\n\t\t\t\t\t\tfor (const optionPart of optionParts) {\n\t\t\t\t\t\t\tconst [key, val] = optionPart.trim().split(\"=\");\n\t\t\t\t\t\t\tswitch (key.toLowerCase()) {\n\t\t\t\t\t\t\t\tcase \"max-age\":\n\t\t\t\t\t\t\t\t\toptions.maxAge = Number.parseInt(val, 10);\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase \"expires\":\n\t\t\t\t\t\t\t\t\toptions.expires = new Date(val);\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase \"path\":\n\t\t\t\t\t\t\t\t\toptions.path = val;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase \"domain\":\n\t\t\t\t\t\t\t\t\toptions.domain = val;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase \"secure\":\n\t\t\t\t\t\t\t\t\toptions.secure = true;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase \"httponly\":\n\t\t\t\t\t\t\t\t\toptions.httpOnly = true;\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\tcase \"samesite\":\n\t\t\t\t\t\t\t\t\toptions.sameSite = val as \"strict\" | \"lax\" | \"none\";\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Set the cookie on the response\n\t\t\t\t\t\tresponse.cookies.set(\n\t\t\t\t\t\t\tcookieName.trim(),\n\t\t\t\t\t\t\tcookieValue.trim(),\n\t\t\t\t\t\t\toptions,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Handle other headers\n\t\t\t\tresponse.headers.set(\n\t\t\t\t\tname,\n\t\t\t\t\tArray.isArray(value) ? value.join(\", \") : value,\n\t\t\t\t);\n\t\t\t}\n\t\t},\n\t\tgetHeader: (name: string) => {\n\t\t\treturn response.headers.get(name);\n\t\t},\n\t};\n\n\treturn new NextJSCookieContext(cookieReq, cookieRes);\n}\n\n/**\n * Create AuthSDK instance with proper storage and cookie context\n */\nfunction createAuthSDK(\n\tconfig: Required<MiddlewareConfig>,\n\treq: NextRequest,\n\tresponse: NextResponse,\n): AuthSDK {\n\t// Create cookie context\n\tconst cookieContext = createCookieContext(req, response, config);\n\n\t// Create hybrid storage that works in server context\n\tconst hybridStorage = createHybridAuthStorage(config.storageKeyPrefix, {\n\t\treq,\n\t\tres: {\n\t\t\tsetHeader: (name: string, value: string | string[]) => {\n\t\t\t\tif (name === \"Set-Cookie\") {\n\t\t\t\t\tconst cookies = Array.isArray(value) ? value : [value];\n\t\t\t\t\tfor (const cookie of cookies) {\n\t\t\t\t\t\t// Parse and set cookies properly\n\t\t\t\t\t\tconst [nameValue, ...optionParts] = cookie.split(\";\");\n\t\t\t\t\t\tconst [cookieName, cookieValue] = nameValue.split(\"=\");\n\n\t\t\t\t\t\tif (cookieName && cookieValue) {\n\t\t\t\t\t\t\tconst options: any = {\n\t\t\t\t\t\t\t\thttpOnly: config.cookieOptions.httpOnly,\n\t\t\t\t\t\t\t\tsecure: config.cookieOptions.secure,\n\t\t\t\t\t\t\t\tsameSite: config.cookieOptions.sameSite,\n\t\t\t\t\t\t\t\tmaxAge: config.cookieOptions.maxAge,\n\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t// Parse additional options from cookie string\n\t\t\t\t\t\t\tfor (const optionPart of optionParts) {\n\t\t\t\t\t\t\t\tconst [key, val] = optionPart.trim().split(\"=\");\n\t\t\t\t\t\t\t\tswitch (key.toLowerCase()) {\n\t\t\t\t\t\t\t\t\tcase \"max-age\":\n\t\t\t\t\t\t\t\t\t\toptions.maxAge = Number.parseInt(val, 10);\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\tcase \"expires\":\n\t\t\t\t\t\t\t\t\t\toptions.expires = new Date(val);\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\tcase \"path\":\n\t\t\t\t\t\t\t\t\t\toptions.path = val;\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\tcase \"domain\":\n\t\t\t\t\t\t\t\t\t\toptions.domain = val;\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\tcase \"secure\":\n\t\t\t\t\t\t\t\t\t\toptions.secure = true;\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\tcase \"httponly\":\n\t\t\t\t\t\t\t\t\t\toptions.httpOnly = true;\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t\tcase \"samesite\":\n\t\t\t\t\t\t\t\t\t\toptions.sameSite = val as \"strict\" | \"lax\" | \"none\";\n\t\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tresponse.cookies.set(\n\t\t\t\t\t\t\t\tcookieName.trim(),\n\t\t\t\t\t\t\t\tcookieValue.trim(),\n\t\t\t\t\t\t\t\toptions,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tresponse.headers.set(\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tArray.isArray(value) ? value.join(\", \") : value,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t},\n\t\t\tgetHeader: (name: string) => {\n\t\t\t\treturn response.headers.get(name);\n\t\t\t},\n\t\t},\n\t});\n\n\tdebugLog(config, \"Storage tokens:\", {\n\t\taccessToken: hybridStorage.getAccessToken() ? \"[PRESENT]\" : \"[MISSING]\",\n\t\trefreshToken: hybridStorage.getRefreshToken() ? \"[PRESENT]\" : \"[MISSING]\",\n\t\tsessionId: hybridStorage.getSessionId() ? \"[PRESENT]\" : \"[MISSING]\",\n\t\tremoteSessionCookie: req.cookies.get(\"frank_sid\")?.value,\n\t\tstorageKeyPrefix: config.storageKeyPrefix,\n\t\tuserType: config.userType,\n\t\tprojectId: config.projectId,\n\t\tsecretKey: config.secretKey,\n\t\tapiUrl: config.apiUrl,\n\t});\n\n\t// Create AuthSDK with proper configuration\n\tconst authSDK = new AuthSDK({\n\t\tapiUrl: config.apiUrl,\n\t\tpublishableKey: config.publishableKey,\n\t\tsessionCookieName: config.sessionCookieName,\n\t\tstorageKeyPrefix: config.storageKeyPrefix,\n\t\tuserType: config.userType,\n\t\tprojectId: config.projectId,\n\t\tsecretKey: config.secretKey,\n\t\tstorage: hybridStorage,\n\t\tdebug: config.debug,\n\t\tdebugConfig: {\n\t\t\tlogLevel: \"debug\",\n\t\t},\n\t});\n\n\treturn authSDK;\n}\n\n/**\n * Authentication validation using AuthSDK\n */\nasync function validateAuthentication(\n\treq: NextRequest,\n\tauthSDK: AuthSDK,\n\tconfig: Required<MiddlewareConfig>,\n): Promise<AuthResult> {\n\ttry {\n\t\tdebugLog(config, \"Validating authentication using AuthSDK\");\n\n\t\t// Check if we have tokens locally\n\t\tconst hasAccessToken = !!authSDK.authStorage.getAccessToken();\n\t\tconst hasRefreshToken = !!authSDK.authStorage.getRefreshToken();\n\n\t\t// Check if we have session cookies\n\t\tconst sessionCookie = req.cookies.get(config.sessionCookieName);\n\t\tconst hasSessionCookie = !!sessionCookie?.value;\n\n\t\tdebugLog(config, \"Authentication state:\", {\n\t\t\thasAccessToken,\n\t\t\thasRefreshToken,\n\t\t\thasSessionCookie,\n\t\t\tsessionCookieName: config.sessionCookieName,\n\t\t});\n\n\t\t// If no tokens AND no session cookie, return unauthenticated immediately\n\t\tif (!hasAccessToken && !hasRefreshToken && !hasSessionCookie) {\n\t\t\tdebugLog(config, \"No tokens or session cookies found, skipping API call\");\n\t\t\treturn {\n\t\t\t\tisAuthenticated: false,\n\t\t\t\tuser: null,\n\t\t\t\tsession: null,\n\t\t\t\torganizationId: null,\n\t\t\t\terror: null,\n\t\t\t\ttokenInfo: {\n\t\t\t\t\taccessTokenExpired: true,\n\t\t\t\t\trefreshTokenExpired: true,\n\t\t\t\t\tcanRefresh: false,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Get token expiration info (will be empty if no tokens)\n\t\tconst tokenInfo = authSDK.getTokenExpirationInfo();\n\n\t\tdebugLog(config, \"Token expiration info:\", {\n\t\t\taccessToken: {\n\t\t\t\tisExpired: tokenInfo.accessToken.isExpired,\n\t\t\t\texpiresIn: tokenInfo.accessToken.expiresIn,\n\t\t\t},\n\t\t\trefreshToken: {\n\t\t\t\tisExpired: tokenInfo.refreshToken.isExpired,\n\t\t\t\texpiresIn: tokenInfo.refreshToken.expiresIn,\n\t\t\t},\n\t\t});\n\n\t\t// Skip API call if running in development mode with network issues\n\t\tif (\n\t\t\tconfig.skipApiCallOnNetworkError &&\n\t\t\tprocess.env.NODE_ENV === \"development\"\n\t\t) {\n\t\t\tdebugLog(\n\t\t\t\tconfig,\n\t\t\t\t\"Skipping API call due to development mode network configuration\",\n\t\t\t);\n\n\t\t\t// Trust local tokens if they exist and aren't expired, or trust session cookies\n\t\t\tif (\n\t\t\t\t(hasAccessToken && !tokenInfo.accessToken.isExpired) ||\n\t\t\t\thasSessionCookie\n\t\t\t) {\n\t\t\t\treturn {\n\t\t\t\t\tisAuthenticated: true,\n\t\t\t\t\tuser: null, // We don't have user data without API call\n\t\t\t\t\tsession: null,\n\t\t\t\t\torganizationId: config.projectId || null,\n\t\t\t\t\terror: null,\n\t\t\t\t\ttokenInfo: {\n\t\t\t\t\t\taccessTokenExpired: tokenInfo.accessToken.isExpired,\n\t\t\t\t\t\trefreshTokenExpired: tokenInfo.refreshToken.isExpired,\n\t\t\t\t\t\tcanRefresh: !tokenInfo.refreshToken.isExpired,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\t// Enhanced request configuration\n\t\tconst authStatusWithTimeout = async (): Promise<AuthStatus> => {\n\t\t\tconst controller = new AbortController();\n\t\t\tconst timeoutId = setTimeout(() => controller.abort(), 5000);\n\t\t\tconst cookieHeader = req.cookies\n\t\t\t\t.getAll()\n\t\t\t\t.map((cookie) => `${cookie.name}=${cookie.value}`)\n\t\t\t\t.join(\"; \");\n\n\t\t\ttry {\n\t\t\t\t// Add proper headers and fetch configuration\n\t\t\t\tconst authStatus = await authSDK.getAuthStatus({\n\t\t\t\t\tcredentials: \"include\",\n\t\t\t\t\tsignal: controller.signal,\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"User-Agent\": \"FrankAuth-Middleware/1.0\",\n\t\t\t\t\t\tAccept: \"application/json\",\n\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t// Copy essential headers only\n\t\t\t\t\t\t\"X-Forwarded-For\": req.headers.get(\"x-forwarded-for\") || \"\",\n\t\t\t\t\t\t\"X-Real-IP\": req.headers.get(\"x-real-ip\") || \"\",\n\t\t\t\t\t\tCookie: cookieHeader,\n\t\t\t\t\t},\n\t\t\t\t\t// Add retry configuration\n\t\t\t\t\tcache: \"no-cache\",\n\t\t\t\t\tkeepalive: false,\n\t\t\t\t});\n\t\t\t\tclearTimeout(timeoutId);\n\t\t\t\treturn authStatus;\n\t\t\t} catch (error) {\n\t\t\t\tclearTimeout(timeoutId);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t};\n\n\t\t// Enhanced retry logic with exponential backoff\n\t\tlet authStatus: AuthStatus;\n\t\tlet lastError: Error | null = null;\n\t\tconst maxRetries = config.maxRetries || 2;\n\n\t\tfor (let attempt = 1; attempt <= maxRetries; attempt++) {\n\t\t\ttry {\n\t\t\t\tdebugLog(config, `Auth status attempt ${attempt}/${maxRetries}`);\n\t\t\t\tauthStatus = await authStatusWithTimeout();\n\t\t\t\tbreak; // Success, exit retry loop\n\t\t\t} catch (error: any) {\n\t\t\t\tlastError = error as Error;\n\t\t\t\tdebugLog(config, `Auth status attempt ${attempt} failed:`, {\n\t\t\t\t\tname: error.name,\n\t\t\t\t\tmessage: error.message,\n\t\t\t\t\tcode: error.code,\n\t\t\t\t});\n\n\t\t\t\t// More intelligent retry logic\n\t\t\t\tif (attempt === maxRetries) {\n\t\t\t\t\t// Last attempt failed - check if we can fall back to local tokens or session cookies\n\t\t\t\t\tif (\n\t\t\t\t\t\tconfig.fallbackToLocalTokens &&\n\t\t\t\t\t\t((hasAccessToken && !tokenInfo.accessToken.isExpired) ||\n\t\t\t\t\t\t\thasSessionCookie)\n\t\t\t\t\t) {\n\t\t\t\t\t\tdebugLog(\n\t\t\t\t\t\t\tconfig,\n\t\t\t\t\t\t\t\"API failed but local token/session is valid, trusting local state\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tisAuthenticated: true,\n\t\t\t\t\t\t\tuser: null,\n\t\t\t\t\t\t\tsession: null,\n\t\t\t\t\t\t\torganizationId: config.projectId || null,\n\t\t\t\t\t\t\terror: error as Error,\n\t\t\t\t\t\t\ttokenInfo: {\n\t\t\t\t\t\t\t\taccessTokenExpired: tokenInfo.accessToken.isExpired,\n\t\t\t\t\t\t\t\trefreshTokenExpired: tokenInfo.refreshToken.isExpired,\n\t\t\t\t\t\t\t\tcanRefresh: !tokenInfo.refreshToken.isExpired,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\n\t\t\t\t// Don't retry on certain error types\n\t\t\t\tif (!isRetryableError(error)) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\n\t\t\t\t// Exponential backoff with jitter\n\t\t\t\tconst backoffMs = Math.min(1000 * Math.pow(2, attempt - 1), 5000);\n\t\t\t\tconst jitter = Math.random() * 0.1 * backoffMs;\n\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, backoffMs + jitter));\n\t\t\t}\n\t\t}\n\n\t\tdebugLog(config, \"Auth status received:\", {\n\t\t\tisAuthenticated: authStatus!.isAuthenticated,\n\t\t\thasUser: !!authStatus!.user,\n\t\t\torganizationId: authStatus!.organizationId,\n\t\t});\n\n\t\treturn {\n\t\t\tisAuthenticated: authStatus!.isAuthenticated,\n\t\t\tuser: authStatus!.user || null,\n\t\t\tsession: authStatus!.session || null,\n\t\t\torganizationId: authStatus!.organizationId || null,\n\t\t\terror: null,\n\t\t\ttokenInfo: {\n\t\t\t\taccessTokenExpired: tokenInfo.accessToken.isExpired,\n\t\t\t\trefreshTokenExpired: tokenInfo.refreshToken.isExpired,\n\t\t\t\tcanRefresh: !tokenInfo.refreshToken.isExpired,\n\t\t\t},\n\t\t};\n\t} catch (error) {\n\t\tdebugLog(config, \"Authentication validation error:\", {\n\t\t\tname: error.name,\n\t\t\tmessage: error.message,\n\t\t\tcode: error.code,\n\t\t});\n\n\t\t// Better error handling - don't mark tokens as expired on network errors\n\t\tconst tokenInfo = authSDK.getTokenExpirationInfo();\n\n\t\treturn {\n\t\t\tisAuthenticated: false,\n\t\t\tuser: null,\n\t\t\tsession: null,\n\t\t\torganizationId: null,\n\t\t\terror: error as Error,\n\t\t\ttokenInfo: {\n\t\t\t\t// Don't mark tokens as expired just because of network errors\n\t\t\t\taccessTokenExpired: tokenInfo.accessToken.isExpired,\n\t\t\t\trefreshTokenExpired: tokenInfo.refreshToken.isExpired,\n\t\t\t\tcanRefresh:\n\t\t\t\t\t!tokenInfo.refreshToken.isExpired && tokenInfo.refreshToken.isValid,\n\t\t\t},\n\t\t};\n\t}\n}\n\n// Enhanced error type checking\nfunction isRetryableError(error: any): boolean {\n\tconst retryableErrors = [\n\t\t\"NETWORK_ERROR\",\n\t\t\"ECONNREFUSED\",\n\t\t\"ENOTFOUND\",\n\t\t\"ECONNRESET\",\n\t\t\"ETIMEDOUT\",\n\t\t\"AbortError\",\n\t];\n\n\treturn (\n\t\t(error?.code && retryableErrors.includes(error.code)) ||\n\t\terror?.name === \"FrankAuthNetworkError\" ||\n\t\terror?.message?.includes(\"fetch failed\") ||\n\t\terror?.message?.includes(\"network\") ||\n\t\terror?.name === \"AbortError\"\n\t);\n}\n\nfunction isNetworkError(error: any): boolean {\n\treturn (\n\t\terror?.name === \"FrankAuthNetworkError\" ||\n\t\terror?.code === \"NETWORK_ERROR\" ||\n\t\terror?.message?.includes(\"fetch failed\") ||\n\t\terror?.message?.includes(\"network\") ||\n\t\terror?.message?.includes(\"interceptors did not return\") ||\n\t\terror?.cause?.code === \"ECONNREFUSED\" ||\n\t\terror?.cause?.code === \"ENOTFOUND\" ||\n\t\terror?.cause?.code === \"ECONNRESET\" ||\n\t\terror?.cause?.code === \"ETIMEDOUT\" ||\n\t\terror?.name === \"AbortError\"\n\t);\n}\n\n/**\n * Extract organization from subdomain or custom domain\n */\nfunction extractOrganization(\n\treq: NextRequest,\n\tconfig: Required<MiddlewareConfig>,\n): string | null {\n\tif (!config.enableOrgRouting) return null;\n\n\tconst hostname = req.nextUrl.hostname;\n\n\tif (config.customDomain && hostname === config.customDomain) {\n\t\treturn req.nextUrl.searchParams.get(\"org\") || null;\n\t}\n\n\tconst parts = hostname.split(\".\");\n\tif (parts.length > 2) {\n\t\treturn parts[0];\n\t}\n\n\treturn null;\n}\n\n/**\n * Debug logger\n */\nfunction debugLog(\n\tconfig: Required<MiddlewareConfig>,\n\tmessage: string,\n\tdata?: any,\n) {\n\tif (config.debug) {\n\t\tconsole.log(`[FrankAuth Middleware] ${message}`, data ? data : \"\");\n\t}\n}\n\n// ============================================================================\n// Core Middleware Logic\n// ============================================================================\n\n/**\n * Process authentication and routing\n */\nasync function processRequest(\n\treq: NextRequest,\n\tconfig: Required<MiddlewareConfig>,\n): Promise<NextResponse> {\n\tconst path = req.nextUrl.pathname;\n\n\tdebugLog(config, `Processing request: ${path}`);\n\n\t// Check if path should be ignored\n\tif (matchesPath(path, config.ignorePaths)) {\n\t\tdebugLog(config, `Ignoring path: ${path}`);\n\t\treturn NextResponse.next();\n\t}\n\n\t// Create response early to capture all cookies and headers\n\tconst response = NextResponse.next();\n\n\t// Execute beforeAuth hook\n\tif (config.hooks?.beforeAuth) {\n\t\tconst hookResult = await config.hooks.beforeAuth(req);\n\t\tif (hookResult instanceof NextResponse) return hookResult;\n\t\tif (hookResult instanceof NextRequest) req = hookResult;\n\t}\n\n\t// Create AuthSDK instance with proper storage context\n\tconst authSDK = createAuthSDK(config, req, response);\n\n\t// Determine path types\n\tconst isPublicPath = matchesPath(path, config.publicPaths);\n\tconst isAuthPath = matchesPath(path, config.authPaths);\n\n\t// Determine if path is private based on configuration\n\tlet isPrivatePath: boolean;\n\tif (config.allPathsPrivate) {\n\t\tisPrivatePath = !isPublicPath && !isAuthPath;\n\t} else {\n\t\tisPrivatePath = matchesPath(path, config.privatePaths);\n\t}\n\n\tdebugLog(config, \"Path analysis:\", {\n\t\tisPublicPath,\n\t\tisPrivatePath,\n\t\tisAuthPath,\n\t\tallPathsPrivate: config.allPathsPrivate,\n\t});\n\n\t// Validate authentication using AuthSDK\n\tconst auth = await validateAuthentication(req, authSDK, config);\n\n\tdebugLog(config, \"Authentication result:\", {\n\t\tisAuthenticated: auth.isAuthenticated,\n\t\thasUser: !!auth.user,\n\t\torganizationId: auth.organizationId,\n\t\ttokenInfo: auth.tokenInfo,\n\t});\n\n\t// Create middleware context\n\tconst context: MiddlewareContext = {\n\t\treq,\n\t\tconfig,\n\t\tauth,\n\t\tauthSDK,\n\t\tpath,\n\t\tisPublicPath,\n\t\tisPrivatePath,\n\t\tisAuthPath,\n\t\tresponse,\n\t};\n\n\t// Execute authentication logic\n\tconst finalResponse = await handleAuthentication(context);\n\n\t// Execute afterAuth hook\n\tif (config.hooks?.afterAuth) {\n\t\tconst hookResult = await config.hooks.afterAuth(req, finalResponse, auth);\n\t\tif (hookResult instanceof NextResponse) return hookResult;\n\t}\n\n\treturn finalResponse;\n}\n\n/**\n * Handle authentication logic based on context\n */\nasync function handleAuthentication(\n\tcontext: MiddlewareContext,\n): Promise<NextResponse> {\n\tconst {\n\t\treq,\n\t\tconfig,\n\t\tauth,\n\t\tauthSDK,\n\t\tpath,\n\t\tisPublicPath,\n\t\tisPrivatePath,\n\t\tisAuthPath,\n\t\tresponse,\n\t} = context;\n\n\ttry {\n\t\t// Handle authenticated users\n\t\tif (auth.isAuthenticated && auth.user) {\n\t\t\tdebugLog(config, \"User is authenticated\");\n\n\t\t\t// Execute onAuthenticated hook\n\t\t\tif (config.hooks?.onAuthenticated && auth.session) {\n\t\t\t\tconst hookResult = await config.hooks.onAuthenticated(\n\t\t\t\t\treq,\n\t\t\t\t\tauth.user,\n\t\t\t\t\tauth.session,\n\t\t\t\t);\n\t\t\t\tif (hookResult instanceof NextResponse) return hookResult;\n\t\t\t}\n\n\t\t\t// Redirect away from auth paths (sign-in, sign-up, etc.)\n\t\t\tif (isAuthPath) {\n\t\t\t\tconst redirectTo =\n\t\t\t\t\treq.nextUrl.searchParams.get(\"redirect_url\") ||\n\t\t\t\t\tconfig.afterSignInPath;\n\t\t\t\tdebugLog(\n\t\t\t\t\tconfig,\n\t\t\t\t\t`Redirecting authenticated user from auth path to: ${redirectTo}`,\n\t\t\t\t);\n\t\t\t\tconst redirectResponse = NextResponse.redirect(\n\t\t\t\t\tnew URL(redirectTo, req.url),\n\t\t\t\t);\n\n\t\t\t\t// Copy cookies from the original response\n\t\t\t\tcopyResponseCookies(response, redirectResponse);\n\t\t\t\treturn redirectResponse;\n\t\t\t}\n\n\t\t\t// Check organization requirement\n\t\t\tif (\n\t\t\t\tconfig.enableOrgRouting &&\n\t\t\t\t!auth.organizationId &&\n\t\t\t\tpath !== config.orgSelectionPath\n\t\t\t) {\n\t\t\t\tdebugLog(config, \"Organization required but not selected\");\n\n\t\t\t\tif (config.hooks?.onOrganizationRequired) {\n\t\t\t\t\tconst hookResult = await config.hooks.onOrganizationRequired(\n\t\t\t\t\t\treq,\n\t\t\t\t\t\tauth.user,\n\t\t\t\t\t);\n\t\t\t\t\tif (hookResult instanceof NextResponse) return hookResult;\n\t\t\t\t}\n\n\t\t\t\tconst redirectResponse = NextResponse.redirect(\n\t\t\t\t\tnew URL(config.orgSelectionPath, req.url),\n\t\t\t\t);\n\t\t\t\tcopyResponseCookies(response, redirectResponse);\n\t\t\t\treturn redirectResponse;\n\t\t\t}\n\n\t\t\t// Allow access to all paths for authenticated users\n\t\t\treturn response;\n\t\t}\n\n\t\t// Handle unauthenticated users\n\t\tdebugLog(config, \"User is not authenticated\");\n\n\t\t// Execute onUnauthenticated hook\n\t\tif (config.hooks?.onUnauthenticated) {\n\t\t\tconst hookResult = await config.hooks.onUnauthenticated(req);\n\t\t\tif (hookResult instanceof NextResponse) return hookResult;\n\t\t}\n\n\t\t// Allow access to public paths and auth paths\n\t\tif (isPublicPath || isAuthPath) {\n\t\t\tdebugLog(config, \"Allowing access to public/auth path\");\n\t\t\treturn response;\n\t\t}\n\n\t\t// Redirect to sign in for private paths\n\t\tif (isPrivatePath) {\n\t\t\tconst signInUrl = new URL(config.signInPath, req.url);\n\t\t\tsignInUrl.searchParams.set(\n\t\t\t\t\"redirect_url\",\n\t\t\t\treq.nextUrl.pathname + req.nextUrl.search,\n\t\t\t);\n\n\t\t\tdebugLog(config, `Redirecting to sign in: ${signInUrl.toString()}`);\n\t\t\tconst redirectResponse = NextResponse.redirect(signInUrl);\n\t\t\tcopyResponseCookies(response, redirectResponse);\n\t\t\treturn redirectResponse;\n\t\t}\n\n\t\treturn response;\n\t} catch (error) {\n\t\tdebugLog(config, \"Error in authentication handling:\", error);\n\n\t\t// Execute onError hook\n\t\tif (config.hooks?.onError) {\n\t\t\tconst hookResult = await config.hooks.onError(req, error as Error);\n\t\t\tif (hookResult instanceof NextResponse) return hookResult;\n\t\t}\n\n\t\t// Default error handling - redirect to sign in\n\t\tconst signInUrl = new URL(config.signInPath, req.url);\n\t\tsignInUrl.searchParams.set(\"error\", \"auth_error\");\n\t\tconst redirectResponse = NextResponse.redirect(signInUrl);\n\t\tcopyResponseCookies(response, redirectResponse);\n\t\treturn redirectResponse;\n\t}\n}\n\n/**\n * Copy cookies from source response to target response\n */\nfunction copyResponseCookies(source: NextResponse, target: NextResponse): void {\n\ttry {\n\t\t// Copy Set-Cookie headers\n\t\tconst setCookieHeaders = source.headers.getSetCookie();\n\t\tif (setCookieHeaders.length > 0) {\n\t\t\tfor (const cookie of setCookieHeaders) {\n\t\t\t\ttarget.headers.append(\"Set-Cookie\", cookie);\n\t\t\t}\n\t\t}\n\n\t\t// Copy individual cookies with validation\n\t\tfor (const cookie of source.cookies.getAll()) {\n\t\t\t// Only copy valid cookies\n\t\t\tif (cookie.name && cookie.value) {\n\t\t\t\ttarget.cookies.set(cookie.name, cookie.value, {\n\t\t\t\t\tdomain: cookie.domain,\n\t\t\t\t\texpires: cookie.expires,\n\t\t\t\t\thttpOnly: cookie.httpOnly,\n\t\t\t\t\tmaxAge: cookie.maxAge,\n\t\t\t\t\tpath: cookie.path || \"/\", // Ensure path is always set\n\t\t\t\t\tsecure: cookie.secure,\n\t\t\t\t\tsameSite: cookie.sameSite,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t} catch (error) {\n\t\tconsole.error(\"Error copying response cookies:\", error);\n\t\t// Don't throw - this should not break the request flow\n\t}\n}\n\n// ============================================================================\n// Main Middleware Factory\n// ============================================================================\n\n/**\n * Create Frank Auth middleware for Next.js\n */\nexport function createFrankAuthMiddleware(userConfig: MiddlewareConfig) {\n\tconst config = {\n\t\t...DEFAULT_MIDDLEWARE_CONFIG,\n\t\t...userConfig,\n\t} as Required<MiddlewareConfig>;\n\n\t// Validate required configuration\n\tif (!config.publishableKey) {\n\t\tthrow new Error(\"publishableKey is required for Frank Auth middleware\");\n\t}\n\n\tif (!config.storageKeyPrefix) {\n\t\tconfig.storageKeyPrefix = \"frank_auth\";\n\t}\n\n\tdebugLog(config, \"Frank Auth middleware initialized with config:\", {\n\t\tpublicPaths: config.publicPaths,\n\t\tprivatePaths: config.privatePaths,\n\t\tauthPaths: config.authPaths,\n\t\tallPathsPrivate: config.allPathsPrivate,\n\t\tsignInPath: config.signInPath,\n\t\tenableOrgRouting: config.enableOrgRouting,\n\t\tstorageKeyPrefix: config.storageKeyPrefix,\n\t});\n\n\treturn async function middleware(req: NextRequest): Promise<NextResponse> {\n\t\treturn processRequest(req, config);\n\t};\n}\n\n// ============================================================================\n// Middleware Utilities\n// ============================================================================\n\n/**\n * Create a custom matcher function for complex routing logic\n */\nexport function createMatcher(patterns: {\n\tinclude?: string[];\n\texclude?: string[];\n\tcustom?: (path: string) => boolean;\n}) {\n\treturn function matcher(path: string): boolean {\n\t\tif (patterns.custom) {\n\t\t\treturn patterns.custom(path);\n\t\t}\n\n\t\tif (patterns.exclude && matchesPath(path, patterns.exclude)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (patterns.include && matchesPath(path, patterns.include)) {\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t};\n}\n\n/**\n * Utility to check if user has specific permission in middleware\n */\nexport async function checkPermission(\n\treq: NextRequest,\n\tpermission: string,\n\tconfig: MiddlewareConfig,\n): Promise<boolean> {\n\ttry {\n\t\tconst response = NextResponse.next();\n\t\tconst authSDK = createAuthSDK(\n\t\t\tconfig as Required<MiddlewareConfig>,\n\t\t\treq,\n\t\t\tresponse,\n\t\t);\n\n\t\tconst cookieHeader = req.cookies\n\t\t\t.getAll()\n\t\t\t.map((cookie) => `${cookie.name}=${cookie.value}`)\n\t\t\t.join(\"; \");\n\n\t\t// This would require implementing a permissions check method in AuthSDK\n\t\t// For now, we'll return true if user is authenticated\n\t\tconst authStatus = await authSDK.getAuthStatus({\n\t\t\theaders: {\n\t\t\t\tCookie: cookieHeader,\n\t\t\t},\n\t\t});\n\t\treturn authStatus.isAuthenticated;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Utility to get organization from request\n */\nexport function getOrganizationFromRequest(\n\treq: NextRequest,\n\tconfig: MiddlewareConfig,\n): string | null {\n\treturn extractOrganization(req, config as Required<MiddlewareConfig>);\n}\n\n/**\n * Utility to get AuthSDK instance in middleware context\n */\nexport function getAuthSDKFromRequest(\n\treq: NextRequest,\n\tconfig: MiddlewareConfig,\n): AuthSDK {\n\tconst response = NextResponse.next();\n\treturn createAuthSDK(config as Required<MiddlewareConfig>, req, response);\n}\n\n/**\n * Utility to check authentication status without redirecting\n */\nexport async function checkAuthStatus(\n\treq: NextRequest,\n\tconfig: MiddlewareConfig,\n): Promise<AuthResult> {\n\tconst response = NextResponse.next();\n\tconst authSDK = createAuthSDK(\n\t\tconfig as Required<MiddlewareConfig>,\n\t\treq,\n\t\tresponse,\n\t);\n\treturn validateAuthentication(\n\t\treq,\n\t\tauthSDK,\n\t\tconfig as Required<MiddlewareConfig>,\n\t);\n}\n"],"names":["DEFAULT_MIDDLEWARE_CONFIG","matchesPath","path","patterns","pattern","prefix","createCookieContext","req","response","config","cookies","cookieReq","cookie","name","cookieRes","value","nameValue","optionParts","cookieName","cookieValue","options","optionPart","key","val","NextJSCookieContext","createAuthSDK","hybridStorage","createHybridAuthStorage","debugLog","AuthSDK","validateAuthentication","authSDK","hasAccessToken","hasRefreshToken","hasSessionCookie","tokenInfo","authStatusWithTimeout","controller","timeoutId","cookieHeader","authStatus","error","lastError","maxRetries","attempt","isRetryableError","backoffMs","jitter","resolve","retryableErrors","extractOrganization","hostname","parts","message","data","processRequest","NextResponse","hookResult","NextRequest","isPublicPath","isAuthPath","isPrivatePath","auth","finalResponse","handleAuthentication","context","redirectTo","redirectResponse","copyResponseCookies","signInUrl","source","target","setCookieHeaders","createFrankAuthMiddleware","userConfig","createMatcher","checkPermission","permission","getOrganizationFromRequest","getAuthSDKFromRequest","checkAuthStatus"],"mappings":"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"}