@logickernel/bridge 0.13.2 → 0.13.3
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/dist/index.cjs +0 -55
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -55
- package/dist/index.d.ts +3 -55
- package/dist/index.js +1 -50
- package/dist/index.js.map +1 -1
- package/dist/next/components.cjs.map +1 -1
- package/dist/next/components.js.map +1 -1
- package/dist/next/index.cjs +40 -20
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.d.cts +3 -3
- package/dist/next/index.d.ts +3 -3
- package/dist/next/index.js +40 -20
- package/dist/next/index.js.map +1 -1
- package/dist/{types-YvOY9KNP.d.cts → types-BndSEjVw.d.cts} +2 -13
- package/dist/{types-YvOY9KNP.d.ts → types-BndSEjVw.d.ts} +2 -13
- package/package.json +1 -1
package/dist/next/index.cjs
CHANGED
|
@@ -117,24 +117,6 @@ function hasAnyRole(userRoles, requiredRoles) {
|
|
|
117
117
|
return requiredRoles.some((role) => userRoles.includes(role));
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
// src/routes.ts
|
|
121
|
-
function findMatchingProtectedRoute(pathname, protectedRoutes) {
|
|
122
|
-
for (const [route, roles] of Object.entries(protectedRoutes)) {
|
|
123
|
-
if (pathname.startsWith(route)) {
|
|
124
|
-
return { route, requiredRoles: roles };
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
return void 0;
|
|
128
|
-
}
|
|
129
|
-
function buildSignInUrl(callbackUrl) {
|
|
130
|
-
const authUrl = getAuthUrl();
|
|
131
|
-
const url = new URL("/core/auth/signin", authUrl);
|
|
132
|
-
if (callbackUrl) {
|
|
133
|
-
url.searchParams.set("callbackUrl", callbackUrl);
|
|
134
|
-
}
|
|
135
|
-
return url;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
120
|
// src/next/session.ts
|
|
139
121
|
async function getSessionCookie() {
|
|
140
122
|
const cookieStore = await headers.cookies();
|
|
@@ -309,6 +291,18 @@ async function getSessionFromRequest(request) {
|
|
|
309
291
|
const validation = await validateSession(request);
|
|
310
292
|
return validation.success ? validation.session : null;
|
|
311
293
|
}
|
|
294
|
+
|
|
295
|
+
// src/routes.ts
|
|
296
|
+
function findMatchingProtectedRoute(pathname, protectedRoutes) {
|
|
297
|
+
for (const [route, roles] of Object.entries(protectedRoutes)) {
|
|
298
|
+
if (pathname.startsWith(route)) {
|
|
299
|
+
return { route, requiredRoles: roles };
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return void 0;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// src/next/proxy.ts
|
|
312
306
|
function matchDynamicRoute(pathname, pattern, basePath) {
|
|
313
307
|
const relativePath = pathname.replace(basePath, "") || "/";
|
|
314
308
|
const paramNames = [];
|
|
@@ -360,7 +354,8 @@ function createProxyHandler(options) {
|
|
|
360
354
|
}
|
|
361
355
|
const cookie = extractSessionCookie(request.cookies);
|
|
362
356
|
if (!cookie) {
|
|
363
|
-
const signInUrl =
|
|
357
|
+
const signInUrl = new URL("/core/auth/signin", request.nextUrl.origin);
|
|
358
|
+
signInUrl.searchParams.set("callbackUrl", request.url);
|
|
364
359
|
return server.NextResponse.redirect(signInUrl);
|
|
365
360
|
}
|
|
366
361
|
const secret = process.env.AUTH_SECRET;
|
|
@@ -371,9 +366,34 @@ function createProxyHandler(options) {
|
|
|
371
366
|
}
|
|
372
367
|
const result = await decodeSessionToken(cookie.value, secret, cookie.isSecure);
|
|
373
368
|
if (!result.success) {
|
|
374
|
-
const signInUrl =
|
|
369
|
+
const signInUrl = new URL("/core/auth/signin", request.nextUrl.origin);
|
|
375
370
|
return server.NextResponse.redirect(signInUrl);
|
|
376
371
|
}
|
|
372
|
+
const requiredRoles = dynamicMatch?.requiredRoles || staticProtectedMatch?.requiredRoles || [];
|
|
373
|
+
if (requiredRoles.length > 0) {
|
|
374
|
+
const organizationId = dynamicMatch?.params.organization_id || request.headers.get("x-organization-id");
|
|
375
|
+
if (!organizationId) {
|
|
376
|
+
const signInUrl = new URL("/core/auth/signin", request.nextUrl.origin);
|
|
377
|
+
signInUrl.searchParams.set("callbackUrl", request.url);
|
|
378
|
+
return server.NextResponse.redirect(signInUrl);
|
|
379
|
+
}
|
|
380
|
+
const rolesResult = await fetchUserRoles({
|
|
381
|
+
organizationId,
|
|
382
|
+
sessionToken: cookie.value,
|
|
383
|
+
isSecure: cookie.isSecure
|
|
384
|
+
});
|
|
385
|
+
if (!rolesResult.success) {
|
|
386
|
+
console.error("[bridge] Failed to fetch roles:", rolesResult.error);
|
|
387
|
+
const signInUrl = new URL("/core/auth/signin", request.nextUrl.origin);
|
|
388
|
+
signInUrl.searchParams.set("callbackUrl", request.url);
|
|
389
|
+
return server.NextResponse.redirect(signInUrl);
|
|
390
|
+
}
|
|
391
|
+
if (!hasAnyRole(rolesResult.roles, requiredRoles)) {
|
|
392
|
+
const signInUrl = new URL("/core/auth/signin", request.nextUrl.origin);
|
|
393
|
+
signInUrl.searchParams.set("callbackUrl", request.url);
|
|
394
|
+
return server.NextResponse.redirect(signInUrl);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
377
397
|
const requestHeaders = new Headers(request.headers);
|
|
378
398
|
requestHeaders.set("x-user-id", result.session.user.id);
|
|
379
399
|
requestHeaders.set("x-user-email", result.session.user.email);
|
package/dist/next/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/jwt.ts","../../src/roles.ts","../../src/routes.ts","../../src/next/session.ts","../../src/next/page.ts","../../src/next/api.ts","../../src/next/proxy.ts"],"names":["nextAuthDecode","cookies","NextResponse"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Kernel Bridge JWT\n * Framework-agnostic JWT decoding for NextAuth v4 tokens\n * Uses NextAuth's internal decode function for guaranteed compatibility\n */\n\nimport { decode as nextAuthDecode } from \"next-auth/jwt\"\nimport type { Session, SessionCookie, DecodeResult, DecodedToken } from \"./types\"\n\n/**\n * Get the base cookie name from AUTH_COOKIE env var or default to next-auth.session-token\n * Strips __Secure- prefix if present, as it will be added automatically for secure cookies\n */\nfunction getBaseCookieName(): string {\n const cookieName = process.env.AUTH_COOKIE || \"next-auth.session-token\"\n // Remove __Secure- prefix if present, as we'll add it back for secure cookies\n return cookieName.startsWith(\"__Secure-\") ? cookieName.slice(10) : cookieName\n}\n\n/**\n * Cookie names used by NextAuth v4\n * Defaults to next-auth.session-token, can be overridden via AUTH_COOKIE env var\n */\nexport const COOKIE_NAMES = {\n get secure(): string {\n const baseName = getBaseCookieName()\n // Secure cookies must start with __Secure- prefix\n return `__Secure-${baseName}`\n },\n get dev(): string {\n return getBaseCookieName()\n },\n} as const\n\n/**\n * Get the AUTH_URL from environment variables\n * This is used internally for API calls to the kernel\n */\nexport function getAuthUrl(): string {\n return process.env.AUTH_URL || process.env.BASE_URL || \"http://localhost:3000\"\n}\n\n/**\n * Get the cookie name based on security mode\n */\nexport function getCookieName(isSecure: boolean): string {\n return isSecure ? COOKIE_NAMES.secure : COOKIE_NAMES.dev\n}\n\n/**\n * Check if a token is expired\n */\nexport function isTokenExpired(decoded: DecodedToken): boolean {\n if (!decoded.exp) return false\n return decoded.exp * 1000 < Date.now()\n}\n\n/**\n * Map decoded JWT payload to Session structure\n */\nexport function mapToSession(decoded: DecodedToken): Session {\n return {\n user: {\n id: decoded.sub as string,\n email: decoded.email as string,\n name: (decoded.name as string) || null,\n image: (decoded.picture as string) || null,\n },\n expires: decoded.exp\n ? new Date(decoded.exp * 1000).toISOString()\n : new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(), // Default 30 days\n }\n}\n\n/**\n * Decode and validate a session token\n *\n * This is the core function - framework-agnostic JWT decoding.\n * The caller is responsible for extracting the token from their framework's\n * cookie/header mechanism.\n *\n * @param token - The JWT session token\n * @param secret - The AUTH_SECRET used to sign the token\n * @param isSecure - Whether the token came from a secure cookie\n * @returns DecodeResult with session data or error\n *\n * @example\n * ```typescript\n * const result = await decodeSessionToken(token, process.env.AUTH_SECRET, isSecure)\n * if (result.success) {\n * console.log(result.session.user.email)\n * } else {\n * console.error(result.error)\n * }\n * ```\n */\nexport async function decodeSessionToken(\n token: string | undefined,\n secret: string | undefined,\n _isSecure: boolean = false\n): Promise<DecodeResult> {\n if (!token) {\n return { success: false, error: \"missing_token\" }\n }\n\n if (!secret) {\n return { success: false, error: \"missing_secret\" }\n }\n\n try {\n // Use NextAuth's decode function directly for session tokens\n // For session tokens, NextAuth uses an empty salt (\"\")\n const decoded = await nextAuthDecode({\n token,\n secret,\n salt: \"\", // Empty salt means session token in NextAuth\n })\n\n if (!decoded) {\n return { success: false, error: \"decode_error\" }\n }\n\n // Check if token is expired\n if (decoded.exp && typeof decoded.exp === \"number\" && decoded.exp * 1000 < Date.now()) {\n return { success: false, error: \"expired\" }\n }\n\n const session = mapToSession(decoded as DecodedToken)\n return {\n success: true,\n session,\n decoded: decoded as DecodedToken,\n }\n } catch (error) {\n // Log the actual error for debugging\n const errorMessage = error instanceof Error ? error.message : String(error)\n console.error(\"[bridge] JWT decode error:\", errorMessage)\n return { success: false, error: \"decode_error\" }\n }\n}\n\n/**\n * Extract session cookie from a cookies object (generic interface)\n *\n * This works with any object that has a `get(name)` method returning\n * `{ value: string } | undefined`, like Next.js cookies() or similar.\n *\n * @param cookies - Object with get(name) method\n * @returns SessionCookie or undefined if no session cookie found\n */\nexport function extractSessionCookie(cookies: {\n get(name: string): { value: string } | undefined\n}): SessionCookie | undefined {\n // Try secure cookie first (production with HTTPS)\n const secureCookie = cookies.get(COOKIE_NAMES.secure)\n if (secureCookie?.value) {\n return { value: secureCookie.value, isSecure: true }\n }\n\n // Fall back to non-secure cookie (development)\n const devCookie = cookies.get(COOKIE_NAMES.dev)\n if (devCookie?.value) {\n return { value: devCookie.value, isSecure: false }\n }\n\n return undefined\n}\n","/**\n * Kernel Bridge Roles\n * Role checking and permission utilities\n */\n\nimport { getCookieName, getAuthUrl } from \"./jwt\"\n\n/**\n * Options for fetching user roles from the kernel\n */\nexport interface FetchRolesOptions {\n /**\n * Organization ID to fetch roles in\n */\n organizationId: string\n\n /**\n * Session token for authentication (user ID is determined from the token)\n */\n sessionToken: string\n\n /**\n * Whether using secure cookie\n * Defaults to NODE_ENV === \"production\" if not provided\n */\n isSecure?: boolean\n\n /**\n * Custom fetch function (optional, for testing or SSR)\n */\n fetchFn?: typeof fetch\n}\n\n/**\n * Result of role fetching operation\n */\nexport type FetchRolesResult =\n | { success: true; roles: string[] }\n | { success: false; error: string }\n\n/**\n * Fetch user roles from the kernel API\n *\n * This is a framework-agnostic function that makes a fetch request\n * to the kernel's roles endpoint. Uses AUTH_URL environment variable internally.\n * The user ID is determined from the session token.\n *\n * @param options - Configuration for fetching roles\n * @returns Promise with roles array or error\n *\n * @example\n * ```typescript\n * const result = await fetchUserRoles({\n * organizationId: \"org-123\",\n * sessionToken: token,\n * // isSecure defaults to NODE_ENV === \"production\"\n * })\n * if (result.success) {\n * console.log(result.roles)\n * }\n * ```\n */\nexport async function fetchUserRoles(\n options: FetchRolesOptions\n): Promise<FetchRolesResult> {\n const { organizationId, sessionToken, fetchFn = fetch } = options\n // Default isSecure to production mode\n const isSecure = options.isSecure ?? (process.env.NODE_ENV === \"production\")\n const kernelUrl = getAuthUrl()\n const cookieName = getCookieName(isSecure)\n\n try {\n const response = await fetchFn(\n `${kernelUrl}/core/api/user/organizations/${organizationId}/roles`,\n {\n headers: {\n Cookie: `${cookieName}=${sessionToken}`,\n },\n cache: \"no-store\",\n }\n )\n\n if (!response.ok) {\n return {\n success: false,\n error: `Failed to fetch roles: ${response.status}`,\n }\n }\n\n const data = await response.json()\n const roles = data.roles?.map((r: { roleName: string }) => r.roleName) || []\n\n return { success: true, roles }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Unknown error\",\n }\n }\n}\n\n/**\n * Check if user has at least one of the required roles\n */\nexport function hasAnyRole(userRoles: string[], requiredRoles: string[]): boolean {\n if (requiredRoles.length === 0) return true // No roles required = any authenticated user\n return requiredRoles.some((role) => userRoles.includes(role))\n}\n\n/**\n * Check if user has all of the required roles\n */\nexport function hasAllRoles(userRoles: string[], requiredRoles: string[]): boolean {\n if (requiredRoles.length === 0) return true\n return requiredRoles.every((role) => userRoles.includes(role))\n}\n\n/**\n * Check if user has a specific role\n */\nexport function hasRole(userRoles: string[], role: string): boolean {\n return userRoles.includes(role)\n}\n\n","/**\n * Kernel Bridge Routes\n * Route matching and protection utilities\n */\n\nimport { getAuthUrl } from \"./jwt\"\nimport type { RouteConfig, RouteMatch, MicroFrontendConfig } from \"./types\"\n\n/**\n * Check if a pathname matches any route in the list\n */\nexport function matchesAnyRoute(pathname: string, routes: string[]): boolean {\n return routes.some((route) => pathname.startsWith(route))\n}\n\n/**\n * Find the first matching protected route for a pathname\n */\nexport function findMatchingProtectedRoute(\n pathname: string,\n protectedRoutes: RouteConfig\n): { route: string; requiredRoles: string[] } | undefined {\n for (const [route, roles] of Object.entries(protectedRoutes)) {\n if (pathname.startsWith(route)) {\n return { route, requiredRoles: roles }\n }\n }\n return undefined\n}\n\n/**\n * Match a pathname against route configuration\n *\n * @param pathname - The URL pathname to match\n * @param config - Micro-frontend configuration\n * @returns RouteMatch indicating the type of route\n *\n * @example\n * ```typescript\n * const match = matchRoute(\"/dashboard\", {\n * publicRoutes: [\"/\", \"/api/health\"],\n * protectedRoutes: { \"/dashboard\": [], \"/admin\": [\"organization.owner\"] },\n * })\n * // Returns: { type: \"protected\", requiredRoles: [] }\n * ```\n */\nexport function matchRoute(\n pathname: string,\n config: Pick<MicroFrontendConfig, \"publicRoutes\" | \"protectedRoutes\">\n): RouteMatch {\n // Check public routes first (if specified)\n if (config.publicRoutes && matchesAnyRoute(pathname, config.publicRoutes)) {\n return { type: \"public\" }\n }\n\n // Check protected routes\n const match = findMatchingProtectedRoute(pathname, config.protectedRoutes)\n if (match) {\n return { type: \"protected\", requiredRoles: match.requiredRoles }\n }\n\n // Not explicitly configured = unprotected\n return { type: \"unprotected\" }\n}\n\n/**\n * Create a default configuration for a micro-frontend\n */\nexport function createConfig(\n options: Partial<MicroFrontendConfig> & Pick<MicroFrontendConfig, \"basePath\">\n): MicroFrontendConfig {\n return {\n basePath: options.basePath,\n protectedRoutes: options.protectedRoutes || {\n \"/dashboard\": [],\n \"/admin\": [\"organization.owner\"],\n },\n publicRoutes: options.publicRoutes || [],\n }\n}\n\n/**\n * Build a sign-in redirect URL\n * Uses AUTH_URL environment variable internally.\n * \n * @param callbackUrl - Optional callback URL to return to after sign-in\n */\nexport function buildSignInUrl(callbackUrl?: string): URL {\n const authUrl = getAuthUrl()\n const url = new URL(\"/core/auth/signin\", authUrl)\n if (callbackUrl) {\n url.searchParams.set(\"callbackUrl\", callbackUrl)\n }\n return url\n}\n\n/**\n * Build a sign-out redirect URL\n * Uses AUTH_URL environment variable internally.\n * \n * @param callbackUrl - Optional callback URL to return to after sign-out\n */\nexport function buildSignOutUrl(callbackUrl?: string): URL {\n const authUrl = getAuthUrl()\n const url = new URL(\"/core/auth/signout\", authUrl)\n if (callbackUrl) {\n url.searchParams.set(\"callbackUrl\", callbackUrl)\n }\n return url\n}\n\n","/**\n * Kernel Bridge Next.js Session\n * Server-side session utilities for Next.js\n */\n\nimport { cookies } from \"next/headers\"\nimport {\n decodeSessionToken,\n extractSessionCookie,\n fetchUserRoles,\n} from \"../index\"\nimport type { Session, SessionCookie } from \"../types\"\n\n/**\n * Get the session cookie from Next.js cookies\n */\nexport async function getSessionCookie(): Promise<SessionCookie | undefined> {\n const cookieStore = await cookies()\n return extractSessionCookie(cookieStore)\n}\n\n/**\n * Get the session token value from cookies\n */\nexport async function getSessionToken(): Promise<string | undefined> {\n const cookie = await getSessionCookie()\n return cookie?.value\n}\n\n/**\n * Check if secure cookie is being used\n */\nexport async function isSecureCookie(): Promise<boolean> {\n const cookie = await getSessionCookie()\n return cookie?.isSecure ?? false\n}\n\n/**\n * Get the current session from cookies\n *\n * This reads the NextAuth session cookie and decodes the JWT.\n * Use this in Server Components, Server Actions, or API routes.\n *\n * @returns Session object or null if not authenticated\n *\n * @example\n * ```typescript\n * import { getSession } from \"@logickernel/bridge/next\"\n *\n * export default async function Page() {\n * const session = await getSession()\n * if (!session) {\n * redirect(\"/auth/signin\")\n * }\n * return <div>Hello {session.user.email}</div>\n * }\n * ```\n */\nexport async function getSession(): Promise<Session | null> {\n const cookie = await getSessionCookie()\n\n if (!cookie) {\n return null\n }\n\n const secret = process.env.AUTH_SECRET\n if (!secret) {\n console.error(\"[bridge] AUTH_SECRET is not configured\")\n return null\n }\n\n const result = await decodeSessionToken(cookie.value, secret, cookie.isSecure)\n\n if (!result.success) {\n if (result.error !== \"missing_token\") {\n console.error(\"[bridge] Session decode failed:\", result.error)\n }\n return null\n }\n\n return result.session\n}\n\n/**\n * Get the base URL (facade/load balancer) for user-facing links and redirects\n * \n * This is the URL that users access through the facade/load balancer.\n * Use this for:\n * - Sign-in redirect URLs\n * - User-facing links to the kernel\n * - Any URLs that users will see or click\n */\nexport function getBaseUrl(): string {\n return process.env.BASE_URL || \"http://localhost:7001\"\n}\n\n/**\n * Get user roles in an organization\n *\n * Fetches roles from the kernel API using the current session.\n * The user ID is determined from the session token.\n *\n * @param organizationId - Organization ID to fetch roles in\n * @returns Array of role names (empty if error or no roles)\n *\n * @example\n * ```typescript\n * import { getUserRoles } from \"@logickernel/bridge/next\"\n *\n * const roles = await getUserRoles(\"org-123\")\n * const isOwner = roles.includes(\"organization.owner\")\n * ```\n */\nexport async function getUserRoles(\n organizationId: string\n): Promise<string[]> {\n const cookie = await getSessionCookie()\n\n if (!cookie) {\n return []\n }\n\n const result = await fetchUserRoles({\n organizationId,\n sessionToken: cookie.value,\n isSecure: cookie.isSecure,\n })\n\n if (!result.success) {\n console.error(\"[bridge] Failed to fetch user roles:\", result.error)\n return []\n }\n\n return result.roles\n}\n\n","/**\n * Kernel Bridge Next.js Page\n * Authentication utilities for pages/server components\n */\n\nimport { getSession, getUserRoles, getBaseUrl } from \"./session\"\nimport { hasAnyRole } from \"../index\"\nimport type { Session } from \"../types\"\nimport type { PageAuthOptions, PageAuthResult } from \"./types\"\n\n// Re-export session utilities for convenience\nexport { getSession, getUserRoles, getBaseUrl } from \"./session\"\n\n/**\n * Check authentication and optionally roles for a page\n *\n * This is a helper for pages that need both auth and role checking.\n * For simple auth-only checks, use getSession() directly.\n *\n * @param options - Authentication options\n * @returns Auth result with session/roles or redirect URL\n *\n * @example\n * ```typescript\n * import { checkPageAuth } from \"@logickernel/bridge/next\"\n * import { redirect } from \"next/navigation\"\n *\n * export default async function AdminPage({ params }) {\n * const { organization_id } = await params\n *\n * const auth = await checkPageAuth({\n * requiredRoles: [\"organization.owner\"],\n * organizationId: organization_id,\n * })\n *\n * if (!auth.authenticated) {\n * redirect(auth.redirectUrl)\n * }\n *\n * return <div>Welcome {auth.session.user.email}</div>\n * }\n * ```\n */\nexport async function checkPageAuth(\n options: PageAuthOptions = {}\n): Promise<PageAuthResult> {\n const { requiredRoles = [], organizationId } = options\n // Use base URL (facade) for user-facing redirects, not kernel URL (direct microservice)\n const baseUrl = getBaseUrl()\n\n // Get session\n const session = await getSession()\n\n if (!session) {\n return {\n authenticated: false,\n redirectUrl: `${baseUrl}/core/auth/signin`,\n }\n }\n\n // If no roles required, just return session\n if (requiredRoles.length === 0) {\n return {\n authenticated: true,\n session,\n roles: [],\n }\n }\n\n // Roles required - need organization ID\n if (!organizationId) {\n console.error(\"[bridge] Organization ID required for role checking\")\n return {\n authenticated: false,\n redirectUrl: `${baseUrl}/core/auth/signin`,\n }\n }\n\n // Fetch user roles\n const roles = await getUserRoles(organizationId)\n\n // Check if user has required role\n if (!hasAnyRole(roles, requiredRoles)) {\n return {\n authenticated: true, // User is authenticated but lacks roles\n session,\n roles,\n }\n }\n\n return {\n authenticated: true,\n session,\n roles,\n }\n}\n\n/**\n * Require authentication for a page\n *\n * This is a simpler helper that throws if not authenticated.\n * Use with redirect() in the catch block.\n *\n * @returns Session if authenticated\n * @throws RedirectInfo if not authenticated\n *\n * @example\n * ```typescript\n * import { requireAuth } from \"@logickernel/bridge/next\"\n * import { redirect } from \"next/navigation\"\n *\n * export default async function DashboardPage() {\n * const session = await requireAuth()\n * // If we get here, user is authenticated\n * return <div>Hello {session.user.email}</div>\n * }\n * ```\n */\nexport async function requireAuth(): Promise<Session> {\n const session = await getSession()\n\n if (!session) {\n // Use base URL (facade) for user-facing redirects, not kernel URL (direct microservice)\n const baseUrl = getBaseUrl()\n // We can't use redirect() here because it's a Next.js function\n // Instead, throw an error that the caller can catch\n const error = new Error(\"Authentication required\")\n ;(error as Error & { redirectUrl: string }).redirectUrl = `${baseUrl}/core/auth/signin`\n throw error\n }\n\n return session\n}\n\n/**\n * Check if user has any of the required roles\n *\n * The user ID is determined from the session token in cookies.\n *\n * @param organizationId - Organization to check roles in\n * @param requiredRoles - Roles to check for\n * @returns True if user has at least one required role\n *\n * @example\n * ```typescript\n * const hasAccess = await hasRequiredRole(\n * \"org-123\",\n * [\"organization.owner\", \"organization.editor\"]\n * )\n * ```\n */\nexport async function hasRequiredRole(\n organizationId: string,\n requiredRoles: string[]\n): Promise<boolean> {\n if (requiredRoles.length === 0) {\n return true // No roles required\n }\n\n const roles = await getUserRoles(organizationId)\n return hasAnyRole(roles, requiredRoles)\n}\n\n","/**\n * Kernel Bridge Next.js API\n * Authentication utilities for API routes\n */\n\nimport { NextRequest, NextResponse } from \"next/server\"\nimport {\n decodeSessionToken,\n extractSessionCookie,\n fetchUserRoles,\n hasAnyRole,\n} from \"../index\"\nimport type { Session } from \"../types\"\nimport type { AuthContext, AuthenticatedHandler, WithAuthOptions } from \"./types\"\n\n/**\n * Validate session from request cookies\n */\nasync function validateSession(\n request: NextRequest\n): Promise<{ success: true; session: Session; cookie: { value: string; isSecure: boolean } } | { success: false; error: string }> {\n const cookie = extractSessionCookie(request.cookies)\n\n if (!cookie) {\n return { success: false, error: \"No session cookie\" }\n }\n\n const secret = process.env.AUTH_SECRET\n if (!secret) {\n console.error(\"[bridge] AUTH_SECRET is not configured\")\n return { success: false, error: \"Server configuration error\" }\n }\n\n const result = await decodeSessionToken(cookie.value, secret, cookie.isSecure)\n\n if (!result.success) {\n return { success: false, error: result.error }\n }\n\n return { success: true, session: result.session, cookie }\n}\n\n/**\n * Wrap an API route handler with authentication\n *\n * This handles JWT validation and optionally role checking.\n * The handler receives the session and can focus on business logic.\n *\n * @param handler - The authenticated handler function\n * @param options - Authentication options\n * @returns A Next.js route handler\n *\n * @example\n * ```typescript\n * // Any authenticated user\n * export const GET = withAuth(async (request, { session }) => {\n * return NextResponse.json({ user: session.user })\n * })\n *\n * // With role requirement\n * export const DELETE = withAuth(\n * async (request, { session }) => {\n * return NextResponse.json({ deleted: true })\n * },\n * { requiredRoles: [\"organization.owner\"] }\n * )\n * ```\n */\nexport function withAuth(\n handler: AuthenticatedHandler,\n options: WithAuthOptions = {}\n): (request: NextRequest, context?: { params?: Promise<Record<string, string>> }) => Promise<Response> {\n return async (\n request: NextRequest,\n context?: { params?: Promise<Record<string, string>> }\n ): Promise<Response> => {\n // Validate session\n const validation = await validateSession(request)\n\n if (!validation.success) {\n return NextResponse.json(\n { error: \"Unauthorized\" },\n { status: 401 }\n )\n }\n\n const { session, cookie } = validation\n\n // Get route params if available\n const params = context?.params ? await context.params : undefined\n\n // Check roles if required\n if (options.requiredRoles && options.requiredRoles.length > 0) {\n // Determine organization ID from params or headers\n const organizationId =\n (options.organizationId && params?.[options.organizationId]) ||\n params?.organization_id ||\n request.headers.get(\"x-organization-id\")\n\n if (!organizationId) {\n return NextResponse.json(\n { error: \"Organization ID required for role-based access\" },\n { status: 400 }\n )\n }\n\n // Fetch user roles\n const rolesResult = await fetchUserRoles({\n organizationId,\n sessionToken: cookie.value,\n isSecure: cookie.isSecure,\n })\n\n if (!rolesResult.success) {\n console.error(\"[bridge] Failed to fetch roles:\", rolesResult.error)\n return NextResponse.json(\n { error: \"Failed to verify permissions\" },\n { status: 500 }\n )\n }\n\n if (!hasAnyRole(rolesResult.roles, options.requiredRoles)) {\n return NextResponse.json(\n { error: \"Forbidden\" },\n { status: 403 }\n )\n }\n }\n\n // Call the handler with auth context\n const authContext: AuthContext = {\n session,\n params,\n }\n\n return handler(request, authContext)\n }\n}\n\n/**\n * Get session from a request (for manual handling)\n *\n * Use this when you need more control than withAuth provides.\n *\n * @param request - The Next.js request\n * @returns Session or null\n *\n * @example\n * ```typescript\n * export async function GET(request: NextRequest) {\n * const session = await getSessionFromRequest(request)\n * if (!session) {\n * return NextResponse.json({ error: \"Unauthorized\" }, { status: 401 })\n * }\n * // Custom logic...\n * }\n * ```\n */\nexport async function getSessionFromRequest(\n request: NextRequest\n): Promise<Session | null> {\n const validation = await validateSession(request)\n return validation.success ? validation.session : null\n}\n\n","/**\n * Kernel Bridge Next.js Proxy\n * Middleware/proxy utilities for Next.js\n */\n\nimport { NextResponse } from \"next/server\"\nimport type { NextRequest } from \"next/server\"\nimport {\n decodeSessionToken,\n extractSessionCookie,\n findMatchingProtectedRoute,\n buildSignInUrl,\n} from \"../index\"\nimport type { ProxyOptions, DynamicRoutePattern, DynamicRouteMatch } from \"./types\"\n\n/**\n * Parse a dynamic route pattern and match against pathname\n *\n * @example\n * matchDynamicRoute(\"/abc123/admin\", \"/[organization_id]/admin\")\n * // Returns: { matched: true, params: { organization_id: \"abc123\" } }\n */\nfunction matchDynamicRoute(\n pathname: string,\n pattern: string,\n basePath: string\n): Omit<DynamicRouteMatch, \"requiredRoles\"> {\n // Remove basePath from pathname\n const relativePath = pathname.replace(basePath, \"\") || \"/\"\n\n // Convert pattern to regex\n // e.g., \"/[organization_id]/admin\" -> /^\\/([^/]+)\\/admin/\n const paramNames: string[] = []\n const regexPattern = pattern.replace(/\\[([^\\]]+)\\]/g, (_, paramName) => {\n paramNames.push(paramName)\n return \"([^/]+)\"\n })\n\n const regex = new RegExp(`^${regexPattern}(?:/.*)?$`)\n const match = relativePath.match(regex)\n\n if (!match) {\n return { matched: false, params: {} }\n }\n\n // Extract params from match groups\n const params: Record<string, string> = {}\n paramNames.forEach((name, index) => {\n const value = match[index + 1]\n if (value !== undefined) {\n params[name] = value\n }\n })\n\n return { matched: true, params }\n}\n\n/**\n * Find matching dynamic route pattern\n */\nfunction findDynamicRouteMatch(\n pathname: string,\n patterns: DynamicRoutePattern[],\n basePath: string\n): DynamicRouteMatch | null {\n for (const pattern of patterns) {\n const result = matchDynamicRoute(pathname, pattern.pattern, basePath)\n if (result.matched) {\n return { ...result, requiredRoles: pattern.requiredRoles }\n }\n }\n return null\n}\n\n/**\n * Create a proxy/middleware handler function for Next.js\n *\n * This creates a fully configured middleware handler that:\n * - Validates authentication for protected routes\n * - Handles dynamic routes with organization IDs\n * - Redirects unauthenticated users to sign in\n * - Adds user info to request headers for downstream use\n *\n * Note: The `config` export must be defined statically in your proxy.ts file\n * for Next.js to analyze it at compile time.\n *\n * @param options - Proxy configuration\n * @returns A Next.js proxy handler function\n *\n * @example\n * ```typescript\n * // src/proxy.ts\n * import { createProxyHandler, type ProxyOptions } from \"@logickernel/bridge/next\"\n *\n * const proxyConfig: ProxyOptions = {\n * basePath: \"/aura\",\n * protectedRoutes: { \"/dashboard\": [] },\n * dynamicRoutes: [\n * { pattern: \"/[organization_id]/admin\", requiredRoles: [\"organization.owner\"] },\n * ],\n * }\n *\n * export const proxy = createProxyHandler(proxyConfig)\n *\n * // Config must be static for Next.js\n * export const config = {\n * matcher: [\"/((?!_next/static|_next/image|favicon.ico|public).*)\"],\n * }\n * ```\n */\nexport function createProxyHandler(\n options: ProxyOptions\n): (request: NextRequest) => Promise<Response> {\n const {\n basePath,\n protectedRoutes,\n dynamicRoutes = [],\n errorPath,\n } = options\n\n async function proxy(request: NextRequest): Promise<Response> {\n const { pathname } = request.nextUrl\n\n // Check dynamic route patterns first\n const dynamicMatch = findDynamicRouteMatch(\n pathname,\n dynamicRoutes,\n basePath\n )\n\n // Check if route matches protected routes\n const staticProtectedMatch = findMatchingProtectedRoute(pathname, protectedRoutes)\n\n // If route is not in protectedRoutes or dynamicRoutes, it's public (no auth needed)\n const isProtected = staticProtectedMatch !== undefined || dynamicMatch?.matched === true\n\n if (!isProtected) {\n return NextResponse.next()\n }\n\n // Protected routes - validate session\n const cookie = extractSessionCookie(request.cookies)\n\n if (!cookie) {\n const signInUrl = buildSignInUrl(request.url)\n return NextResponse.redirect(signInUrl)\n }\n\n const secret = process.env.AUTH_SECRET\n if (!secret) {\n console.error(\"[bridge] AUTH_SECRET is not configured\")\n const errorUrl = errorPath\n ? new URL(errorPath, request.url)\n : new URL(\"/error?code=config\", request.url)\n return NextResponse.redirect(errorUrl)\n }\n\n const result = await decodeSessionToken(cookie.value, secret, cookie.isSecure)\n\n if (!result.success) {\n const signInUrl = buildSignInUrl()\n return NextResponse.redirect(signInUrl)\n }\n\n // Add user info to headers for downstream use\n const requestHeaders = new Headers(request.headers)\n requestHeaders.set(\"x-user-id\", result.session.user.id)\n requestHeaders.set(\"x-user-email\", result.session.user.email)\n\n // Add organization ID if from dynamic route\n if (dynamicMatch?.matched && dynamicMatch.params.organization_id) {\n requestHeaders.set(\"x-organization-id\", dynamicMatch.params.organization_id)\n }\n\n // Add any other dynamic params\n if (dynamicMatch?.matched) {\n for (const [key, value] of Object.entries(dynamicMatch.params)) {\n requestHeaders.set(`x-param-${key}`, value)\n }\n }\n\n return NextResponse.next({\n request: {\n headers: requestHeaders,\n },\n })\n }\n\n return proxy\n}\n\n/**\n * Default matcher config for proxy\n * Can be used if you don't want to define your own config\n */\nexport const defaultProxyConfig = {\n matcher: [\n /*\n * Match all request paths except:\n * - _next/static (static files)\n * - _next/image (image optimization files)\n * - favicon.ico (favicon file)\n * - public files (public folder)\n */\n \"/((?!_next/static|_next/image|favicon.ico|public).*)\",\n ],\n}\n\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/jwt.ts","../../src/roles.ts","../../src/next/session.ts","../../src/next/page.ts","../../src/next/api.ts","../../src/routes.ts","../../src/next/proxy.ts"],"names":["nextAuthDecode","cookies","NextResponse"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Kernel Bridge JWT\n * Framework-agnostic JWT decoding for NextAuth v4 tokens\n * Uses NextAuth's internal decode function for guaranteed compatibility\n */\n\nimport { decode as nextAuthDecode } from \"next-auth/jwt\"\nimport type { Session, SessionCookie, DecodeResult, DecodedToken } from \"./types\"\n\n/**\n * Get the base cookie name from AUTH_COOKIE env var or default to next-auth.session-token\n * Strips __Secure- prefix if present, as it will be added automatically for secure cookies\n */\nfunction getBaseCookieName(): string {\n const cookieName = process.env.AUTH_COOKIE || \"next-auth.session-token\"\n // Remove __Secure- prefix if present, as we'll add it back for secure cookies\n return cookieName.startsWith(\"__Secure-\") ? cookieName.slice(10) : cookieName\n}\n\n/**\n * Cookie names used by NextAuth v4\n * Defaults to next-auth.session-token, can be overridden via AUTH_COOKIE env var\n */\nexport const COOKIE_NAMES = {\n get secure(): string {\n const baseName = getBaseCookieName()\n // Secure cookies must start with __Secure- prefix\n return `__Secure-${baseName}`\n },\n get dev(): string {\n return getBaseCookieName()\n },\n} as const\n\n/**\n * Get the AUTH_URL from environment variables\n * This is used internally for API calls to the kernel\n */\nexport function getAuthUrl(): string {\n return process.env.AUTH_URL || process.env.BASE_URL || \"http://localhost:3000\"\n}\n\n/**\n * Get the cookie name based on security mode\n */\nexport function getCookieName(isSecure: boolean): string {\n return isSecure ? COOKIE_NAMES.secure : COOKIE_NAMES.dev\n}\n\n/**\n * Check if a token is expired\n */\nexport function isTokenExpired(decoded: DecodedToken): boolean {\n if (!decoded.exp) return false\n return decoded.exp * 1000 < Date.now()\n}\n\n/**\n * Map decoded JWT payload to Session structure\n */\nexport function mapToSession(decoded: DecodedToken): Session {\n return {\n user: {\n id: decoded.sub as string,\n email: decoded.email as string,\n name: (decoded.name as string) || null,\n image: (decoded.picture as string) || null,\n },\n expires: decoded.exp\n ? new Date(decoded.exp * 1000).toISOString()\n : new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(), // Default 30 days\n }\n}\n\n/**\n * Decode and validate a session token\n *\n * This is the core function - framework-agnostic JWT decoding.\n * The caller is responsible for extracting the token from their framework's\n * cookie/header mechanism.\n *\n * @param token - The JWT session token\n * @param secret - The AUTH_SECRET used to sign the token\n * @param isSecure - Whether the token came from a secure cookie\n * @returns DecodeResult with session data or error\n *\n * @example\n * ```typescript\n * const result = await decodeSessionToken(token, process.env.AUTH_SECRET, isSecure)\n * if (result.success) {\n * console.log(result.session.user.email)\n * } else {\n * console.error(result.error)\n * }\n * ```\n */\nexport async function decodeSessionToken(\n token: string | undefined,\n secret: string | undefined,\n _isSecure: boolean = false\n): Promise<DecodeResult> {\n if (!token) {\n return { success: false, error: \"missing_token\" }\n }\n\n if (!secret) {\n return { success: false, error: \"missing_secret\" }\n }\n\n try {\n // Use NextAuth's decode function directly for session tokens\n // For session tokens, NextAuth uses an empty salt (\"\")\n const decoded = await nextAuthDecode({\n token,\n secret,\n salt: \"\", // Empty salt means session token in NextAuth\n })\n\n if (!decoded) {\n return { success: false, error: \"decode_error\" }\n }\n\n // Check if token is expired\n if (decoded.exp && typeof decoded.exp === \"number\" && decoded.exp * 1000 < Date.now()) {\n return { success: false, error: \"expired\" }\n }\n\n const session = mapToSession(decoded as DecodedToken)\n return {\n success: true,\n session,\n decoded: decoded as DecodedToken,\n }\n } catch (error) {\n // Log the actual error for debugging\n const errorMessage = error instanceof Error ? error.message : String(error)\n console.error(\"[bridge] JWT decode error:\", errorMessage)\n return { success: false, error: \"decode_error\" }\n }\n}\n\n/**\n * Extract session cookie from a cookies object (generic interface)\n *\n * This works with any object that has a `get(name)` method returning\n * `{ value: string } | undefined`, like Next.js cookies() or similar.\n *\n * @param cookies - Object with get(name) method\n * @returns SessionCookie or undefined if no session cookie found\n */\nexport function extractSessionCookie(cookies: {\n get(name: string): { value: string } | undefined\n}): SessionCookie | undefined {\n // Try secure cookie first (production with HTTPS)\n const secureCookie = cookies.get(COOKIE_NAMES.secure)\n if (secureCookie?.value) {\n return { value: secureCookie.value, isSecure: true }\n }\n\n // Fall back to non-secure cookie (development)\n const devCookie = cookies.get(COOKIE_NAMES.dev)\n if (devCookie?.value) {\n return { value: devCookie.value, isSecure: false }\n }\n\n return undefined\n}\n","/**\n * Kernel Bridge Roles\n * Role checking and permission utilities\n */\n\nimport { getCookieName, getAuthUrl } from \"./jwt\"\n\n/**\n * Options for fetching user roles from the kernel\n */\nexport interface FetchRolesOptions {\n /**\n * Organization ID to fetch roles in\n */\n organizationId: string\n\n /**\n * Session token for authentication (user ID is determined from the token)\n */\n sessionToken: string\n\n /**\n * Whether using secure cookie\n * Defaults to NODE_ENV === \"production\" if not provided\n */\n isSecure?: boolean\n\n /**\n * Custom fetch function (optional, for testing or SSR)\n */\n fetchFn?: typeof fetch\n}\n\n/**\n * Result of role fetching operation\n */\nexport type FetchRolesResult =\n | { success: true; roles: string[] }\n | { success: false; error: string }\n\n/**\n * Fetch user roles from the kernel API\n *\n * This is a framework-agnostic function that makes a fetch request\n * to the kernel's roles endpoint. Uses AUTH_URL environment variable internally.\n * The user ID is determined from the session token.\n *\n * @param options - Configuration for fetching roles\n * @returns Promise with roles array or error\n *\n * @example\n * ```typescript\n * const result = await fetchUserRoles({\n * organizationId: \"org-123\",\n * sessionToken: token,\n * // isSecure defaults to NODE_ENV === \"production\"\n * })\n * if (result.success) {\n * console.log(result.roles)\n * }\n * ```\n */\nexport async function fetchUserRoles(\n options: FetchRolesOptions\n): Promise<FetchRolesResult> {\n const { organizationId, sessionToken, fetchFn = fetch } = options\n // Default isSecure to production mode\n const isSecure = options.isSecure ?? (process.env.NODE_ENV === \"production\")\n const kernelUrl = getAuthUrl()\n const cookieName = getCookieName(isSecure)\n\n try {\n const response = await fetchFn(\n `${kernelUrl}/core/api/user/organizations/${organizationId}/roles`,\n {\n headers: {\n Cookie: `${cookieName}=${sessionToken}`,\n },\n cache: \"no-store\",\n }\n )\n\n if (!response.ok) {\n return {\n success: false,\n error: `Failed to fetch roles: ${response.status}`,\n }\n }\n\n const data = await response.json()\n const roles = data.roles?.map((r: { roleName: string }) => r.roleName) || []\n\n return { success: true, roles }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : \"Unknown error\",\n }\n }\n}\n\n/**\n * Check if user has at least one of the required roles\n */\nexport function hasAnyRole(userRoles: string[], requiredRoles: string[]): boolean {\n if (requiredRoles.length === 0) return true // No roles required = any authenticated user\n return requiredRoles.some((role) => userRoles.includes(role))\n}\n\n/**\n * Check if user has all of the required roles\n */\nexport function hasAllRoles(userRoles: string[], requiredRoles: string[]): boolean {\n if (requiredRoles.length === 0) return true\n return requiredRoles.every((role) => userRoles.includes(role))\n}\n\n/**\n * Check if user has a specific role\n */\nexport function hasRole(userRoles: string[], role: string): boolean {\n return userRoles.includes(role)\n}\n\n","/**\n * Kernel Bridge Next.js Session\n * Server-side session utilities for Next.js\n */\n\nimport { cookies } from \"next/headers\"\nimport {\n decodeSessionToken,\n extractSessionCookie,\n fetchUserRoles,\n} from \"../index\"\nimport type { Session, SessionCookie } from \"../types\"\n\n/**\n * Get the session cookie from Next.js cookies\n */\nexport async function getSessionCookie(): Promise<SessionCookie | undefined> {\n const cookieStore = await cookies()\n return extractSessionCookie(cookieStore)\n}\n\n/**\n * Get the session token value from cookies\n */\nexport async function getSessionToken(): Promise<string | undefined> {\n const cookie = await getSessionCookie()\n return cookie?.value\n}\n\n/**\n * Check if secure cookie is being used\n */\nexport async function isSecureCookie(): Promise<boolean> {\n const cookie = await getSessionCookie()\n return cookie?.isSecure ?? false\n}\n\n/**\n * Get the current session from cookies\n *\n * This reads the NextAuth session cookie and decodes the JWT.\n * Use this in Server Components, Server Actions, or API routes.\n *\n * @returns Session object or null if not authenticated\n *\n * @example\n * ```typescript\n * import { getSession } from \"@logickernel/bridge/next\"\n *\n * export default async function Page() {\n * const session = await getSession()\n * if (!session) {\n * redirect(\"/auth/signin\")\n * }\n * return <div>Hello {session.user.email}</div>\n * }\n * ```\n */\nexport async function getSession(): Promise<Session | null> {\n const cookie = await getSessionCookie()\n\n if (!cookie) {\n return null\n }\n\n const secret = process.env.AUTH_SECRET\n if (!secret) {\n console.error(\"[bridge] AUTH_SECRET is not configured\")\n return null\n }\n\n const result = await decodeSessionToken(cookie.value, secret, cookie.isSecure)\n\n if (!result.success) {\n if (result.error !== \"missing_token\") {\n console.error(\"[bridge] Session decode failed:\", result.error)\n }\n return null\n }\n\n return result.session\n}\n\n/**\n * Get the base URL (facade/load balancer) for user-facing links and redirects\n * \n * This is the URL that users access through the facade/load balancer.\n * Use this for:\n * - Sign-in redirect URLs\n * - User-facing links to the kernel\n * - Any URLs that users will see or click\n */\nexport function getBaseUrl(): string {\n return process.env.BASE_URL || \"http://localhost:7001\"\n}\n\n/**\n * Get user roles in an organization\n *\n * Fetches roles from the kernel API using the current session.\n * The user ID is determined from the session token.\n *\n * @param organizationId - Organization ID to fetch roles in\n * @returns Array of role names (empty if error or no roles)\n *\n * @example\n * ```typescript\n * import { getUserRoles } from \"@logickernel/bridge/next\"\n *\n * const roles = await getUserRoles(\"org-123\")\n * const isOwner = roles.includes(\"organization.owner\")\n * ```\n */\nexport async function getUserRoles(\n organizationId: string\n): Promise<string[]> {\n const cookie = await getSessionCookie()\n\n if (!cookie) {\n return []\n }\n\n const result = await fetchUserRoles({\n organizationId,\n sessionToken: cookie.value,\n isSecure: cookie.isSecure,\n })\n\n if (!result.success) {\n console.error(\"[bridge] Failed to fetch user roles:\", result.error)\n return []\n }\n\n return result.roles\n}\n\n","/**\n * Kernel Bridge Next.js Page\n * Authentication utilities for pages/server components\n */\n\nimport { getSession, getUserRoles, getBaseUrl } from \"./session\"\nimport { hasAnyRole } from \"../index\"\nimport type { Session } from \"../types\"\nimport type { PageAuthOptions, PageAuthResult } from \"./types\"\n\n// Re-export session utilities for convenience\nexport { getSession, getUserRoles, getBaseUrl } from \"./session\"\n\n/**\n * Check authentication and optionally roles for a page\n *\n * This is a helper for pages that need both auth and role checking.\n * For simple auth-only checks, use getSession() directly.\n *\n * @param options - Authentication options\n * @returns Auth result with session/roles or redirect URL\n *\n * @example\n * ```typescript\n * import { checkPageAuth } from \"@logickernel/bridge/next\"\n * import { redirect } from \"next/navigation\"\n *\n * export default async function AdminPage({ params }) {\n * const { organization_id } = await params\n *\n * const auth = await checkPageAuth({\n * requiredRoles: [\"organization.owner\"],\n * organizationId: organization_id,\n * })\n *\n * if (!auth.authenticated) {\n * redirect(auth.redirectUrl)\n * }\n *\n * return <div>Welcome {auth.session.user.email}</div>\n * }\n * ```\n */\nexport async function checkPageAuth(\n options: PageAuthOptions = {}\n): Promise<PageAuthResult> {\n const { requiredRoles = [], organizationId } = options\n // Use base URL (facade) for user-facing redirects, not kernel URL (direct microservice)\n const baseUrl = getBaseUrl()\n\n // Get session\n const session = await getSession()\n\n if (!session) {\n return {\n authenticated: false,\n redirectUrl: `${baseUrl}/core/auth/signin`,\n }\n }\n\n // If no roles required, just return session\n if (requiredRoles.length === 0) {\n return {\n authenticated: true,\n session,\n roles: [],\n }\n }\n\n // Roles required - need organization ID\n if (!organizationId) {\n console.error(\"[bridge] Organization ID required for role checking\")\n return {\n authenticated: false,\n redirectUrl: `${baseUrl}/core/auth/signin`,\n }\n }\n\n // Fetch user roles\n const roles = await getUserRoles(organizationId)\n\n // Check if user has required role\n if (!hasAnyRole(roles, requiredRoles)) {\n return {\n authenticated: true, // User is authenticated but lacks roles\n session,\n roles,\n }\n }\n\n return {\n authenticated: true,\n session,\n roles,\n }\n}\n\n/**\n * Require authentication for a page\n *\n * This is a simpler helper that throws if not authenticated.\n * Use with redirect() in the catch block.\n *\n * @returns Session if authenticated\n * @throws RedirectInfo if not authenticated\n *\n * @example\n * ```typescript\n * import { requireAuth } from \"@logickernel/bridge/next\"\n * import { redirect } from \"next/navigation\"\n *\n * export default async function DashboardPage() {\n * const session = await requireAuth()\n * // If we get here, user is authenticated\n * return <div>Hello {session.user.email}</div>\n * }\n * ```\n */\nexport async function requireAuth(): Promise<Session> {\n const session = await getSession()\n\n if (!session) {\n // Use base URL (facade) for user-facing redirects, not kernel URL (direct microservice)\n const baseUrl = getBaseUrl()\n // We can't use redirect() here because it's a Next.js function\n // Instead, throw an error that the caller can catch\n const error = new Error(\"Authentication required\")\n ;(error as Error & { redirectUrl: string }).redirectUrl = `${baseUrl}/core/auth/signin`\n throw error\n }\n\n return session\n}\n\n/**\n * Check if user has any of the required roles\n *\n * The user ID is determined from the session token in cookies.\n *\n * @param organizationId - Organization to check roles in\n * @param requiredRoles - Roles to check for\n * @returns True if user has at least one required role\n *\n * @example\n * ```typescript\n * const hasAccess = await hasRequiredRole(\n * \"org-123\",\n * [\"organization.owner\", \"organization.editor\"]\n * )\n * ```\n */\nexport async function hasRequiredRole(\n organizationId: string,\n requiredRoles: string[]\n): Promise<boolean> {\n if (requiredRoles.length === 0) {\n return true // No roles required\n }\n\n const roles = await getUserRoles(organizationId)\n return hasAnyRole(roles, requiredRoles)\n}\n\n","/**\n * Kernel Bridge Next.js API\n * Authentication utilities for API routes\n */\n\nimport { NextRequest, NextResponse } from \"next/server\"\nimport {\n decodeSessionToken,\n extractSessionCookie,\n fetchUserRoles,\n hasAnyRole,\n} from \"../index\"\nimport type { Session } from \"../types\"\nimport type { AuthContext, AuthenticatedHandler, WithAuthOptions } from \"./types\"\n\n/**\n * Validate session from request cookies\n */\nasync function validateSession(\n request: NextRequest\n): Promise<{ success: true; session: Session; cookie: { value: string; isSecure: boolean } } | { success: false; error: string }> {\n const cookie = extractSessionCookie(request.cookies)\n\n if (!cookie) {\n return { success: false, error: \"No session cookie\" }\n }\n\n const secret = process.env.AUTH_SECRET\n if (!secret) {\n console.error(\"[bridge] AUTH_SECRET is not configured\")\n return { success: false, error: \"Server configuration error\" }\n }\n\n const result = await decodeSessionToken(cookie.value, secret, cookie.isSecure)\n\n if (!result.success) {\n return { success: false, error: result.error }\n }\n\n return { success: true, session: result.session, cookie }\n}\n\n/**\n * Wrap an API route handler with authentication\n *\n * This handles JWT validation and optionally role checking.\n * The handler receives the session and can focus on business logic.\n *\n * @param handler - The authenticated handler function\n * @param options - Authentication options\n * @returns A Next.js route handler\n *\n * @example\n * ```typescript\n * // Any authenticated user\n * export const GET = withAuth(async (request, { session }) => {\n * return NextResponse.json({ user: session.user })\n * })\n *\n * // With role requirement\n * export const DELETE = withAuth(\n * async (request, { session }) => {\n * return NextResponse.json({ deleted: true })\n * },\n * { requiredRoles: [\"organization.owner\"] }\n * )\n * ```\n */\nexport function withAuth(\n handler: AuthenticatedHandler,\n options: WithAuthOptions = {}\n): (request: NextRequest, context?: { params?: Promise<Record<string, string>> }) => Promise<Response> {\n return async (\n request: NextRequest,\n context?: { params?: Promise<Record<string, string>> }\n ): Promise<Response> => {\n // Validate session\n const validation = await validateSession(request)\n\n if (!validation.success) {\n return NextResponse.json(\n { error: \"Unauthorized\" },\n { status: 401 }\n )\n }\n\n const { session, cookie } = validation\n\n // Get route params if available\n const params = context?.params ? await context.params : undefined\n\n // Check roles if required\n if (options.requiredRoles && options.requiredRoles.length > 0) {\n // Determine organization ID from params or headers\n const organizationId =\n (options.organizationId && params?.[options.organizationId]) ||\n params?.organization_id ||\n request.headers.get(\"x-organization-id\")\n\n if (!organizationId) {\n return NextResponse.json(\n { error: \"Organization ID required for role-based access\" },\n { status: 400 }\n )\n }\n\n // Fetch user roles\n const rolesResult = await fetchUserRoles({\n organizationId,\n sessionToken: cookie.value,\n isSecure: cookie.isSecure,\n })\n\n if (!rolesResult.success) {\n console.error(\"[bridge] Failed to fetch roles:\", rolesResult.error)\n return NextResponse.json(\n { error: \"Failed to verify permissions\" },\n { status: 500 }\n )\n }\n\n if (!hasAnyRole(rolesResult.roles, options.requiredRoles)) {\n return NextResponse.json(\n { error: \"Forbidden\" },\n { status: 403 }\n )\n }\n }\n\n // Call the handler with auth context\n const authContext: AuthContext = {\n session,\n params,\n }\n\n return handler(request, authContext)\n }\n}\n\n/**\n * Get session from a request (for manual handling)\n *\n * Use this when you need more control than withAuth provides.\n *\n * @param request - The Next.js request\n * @returns Session or null\n *\n * @example\n * ```typescript\n * export async function GET(request: NextRequest) {\n * const session = await getSessionFromRequest(request)\n * if (!session) {\n * return NextResponse.json({ error: \"Unauthorized\" }, { status: 401 })\n * }\n * // Custom logic...\n * }\n * ```\n */\nexport async function getSessionFromRequest(\n request: NextRequest\n): Promise<Session | null> {\n const validation = await validateSession(request)\n return validation.success ? validation.session : null\n}\n\n","/**\n * Kernel Bridge Routes\n * Route matching and protection utilities\n */\n\nimport { getAuthUrl } from \"./jwt\"\nimport type { RouteConfig, RouteMatch, MicroFrontendConfig } from \"./types\"\n\n/**\n * Check if a pathname matches any route in the list\n */\nexport function matchesAnyRoute(pathname: string, routes: string[]): boolean {\n return routes.some((route) => pathname.startsWith(route))\n}\n\n/**\n * Find the first matching protected route for a pathname\n */\nexport function findMatchingProtectedRoute(\n pathname: string,\n protectedRoutes: RouteConfig\n): { route: string; requiredRoles: string[] } | undefined {\n for (const [route, roles] of Object.entries(protectedRoutes)) {\n if (pathname.startsWith(route)) {\n return { route, requiredRoles: roles }\n }\n }\n return undefined\n}\n\n/**\n * Match a pathname against route configuration\n *\n * @param pathname - The URL pathname to match\n * @param config - Micro-frontend configuration\n * @returns RouteMatch indicating the type of route\n *\n * @example\n * ```typescript\n * const match = matchRoute(\"/dashboard\", {\n * publicRoutes: [\"/\", \"/api/health\"],\n * protectedRoutes: { \"/dashboard\": [], \"/admin\": [\"organization.owner\"] },\n * })\n * // Returns: { type: \"protected\", requiredRoles: [] }\n * ```\n */\nexport function matchRoute(\n pathname: string,\n config: Pick<MicroFrontendConfig, \"publicRoutes\" | \"protectedRoutes\">\n): RouteMatch {\n // Check public routes first (if specified)\n if (config.publicRoutes && matchesAnyRoute(pathname, config.publicRoutes)) {\n return { type: \"public\" }\n }\n\n // Check protected routes\n const match = findMatchingProtectedRoute(pathname, config.protectedRoutes)\n if (match) {\n return { type: \"protected\", requiredRoles: match.requiredRoles }\n }\n\n // Not explicitly configured = unprotected\n return { type: \"unprotected\" }\n}\n\n/**\n * Create a default configuration for a micro-frontend\n */\nexport function createConfig(\n options: Partial<MicroFrontendConfig> & Pick<MicroFrontendConfig, \"basePath\">\n): MicroFrontendConfig {\n return {\n basePath: options.basePath,\n protectedRoutes: options.protectedRoutes || {\n \"/dashboard\": [],\n \"/admin\": [\"organization.owner\"],\n },\n publicRoutes: options.publicRoutes || [],\n }\n}\n\n/**\n * Build a sign-in redirect URL\n * Uses AUTH_URL environment variable internally.\n * \n * @param callbackUrl - Optional callback URL to return to after sign-in\n */\nexport function buildSignInUrl(callbackUrl?: string): URL {\n const authUrl = getAuthUrl()\n const url = new URL(\"/core/auth/signin\", authUrl)\n if (callbackUrl) {\n url.searchParams.set(\"callbackUrl\", callbackUrl)\n }\n return url\n}\n\n/**\n * Build a sign-out redirect URL\n * Uses AUTH_URL environment variable internally.\n * \n * @param callbackUrl - Optional callback URL to return to after sign-out\n */\nexport function buildSignOutUrl(callbackUrl?: string): URL {\n const authUrl = getAuthUrl()\n const url = new URL(\"/core/auth/signout\", authUrl)\n if (callbackUrl) {\n url.searchParams.set(\"callbackUrl\", callbackUrl)\n }\n return url\n}\n\n","/**\n * Kernel Bridge Next.js Proxy\n * Middleware/proxy utilities for Next.js\n */\n\nimport { NextResponse } from \"next/server\"\nimport type { NextRequest } from \"next/server\"\nimport {\n decodeSessionToken,\n extractSessionCookie,\n fetchUserRoles,\n hasAnyRole,\n} from \"../index\"\nimport {\n findMatchingProtectedRoute,\n} from \"../routes\"\nimport type { ProxyOptions, DynamicRoutePattern, DynamicRouteMatch } from \"./types\"\n\n/**\n * Parse a dynamic route pattern and match against pathname\n *\n * @example\n * matchDynamicRoute(\"/abc123/admin\", \"/[organization_id]/admin\")\n * // Returns: { matched: true, params: { organization_id: \"abc123\" } }\n */\nfunction matchDynamicRoute(\n pathname: string,\n pattern: string,\n basePath: string\n): Omit<DynamicRouteMatch, \"requiredRoles\"> {\n // Remove basePath from pathname\n const relativePath = pathname.replace(basePath, \"\") || \"/\"\n\n // Convert pattern to regex\n // e.g., \"/[organization_id]/admin\" -> /^\\/([^/]+)\\/admin/\n const paramNames: string[] = []\n const regexPattern = pattern.replace(/\\[([^\\]]+)\\]/g, (_, paramName) => {\n paramNames.push(paramName)\n return \"([^/]+)\"\n })\n\n const regex = new RegExp(`^${regexPattern}(?:/.*)?$`)\n const match = relativePath.match(regex)\n\n if (!match) {\n return { matched: false, params: {} }\n }\n\n // Extract params from match groups\n const params: Record<string, string> = {}\n paramNames.forEach((name, index) => {\n const value = match[index + 1]\n if (value !== undefined) {\n params[name] = value\n }\n })\n\n return { matched: true, params }\n}\n\n/**\n * Find matching dynamic route pattern\n */\nfunction findDynamicRouteMatch(\n pathname: string,\n patterns: DynamicRoutePattern[],\n basePath: string\n): DynamicRouteMatch | null {\n for (const pattern of patterns) {\n const result = matchDynamicRoute(pathname, pattern.pattern, basePath)\n if (result.matched) {\n return { ...result, requiredRoles: pattern.requiredRoles }\n }\n }\n return null\n}\n\n/**\n * Create a proxy/middleware handler function for Next.js\n *\n * This creates a fully configured middleware handler that:\n * - Validates authentication for protected routes\n * - Handles dynamic routes with organization IDs\n * - Redirects unauthenticated users to sign in\n * - Adds user info to request headers for downstream use\n *\n * Note: The `config` export must be defined statically in your proxy.ts file\n * for Next.js to analyze it at compile time.\n *\n * @param options - Proxy configuration\n * @returns A Next.js proxy handler function\n *\n * @example\n * ```typescript\n * // src/proxy.ts\n * import { createProxyHandler, type ProxyOptions } from \"@logickernel/bridge/next\"\n *\n * const proxyConfig: ProxyOptions = {\n * basePath: \"/app\",\n * protectedRoutes: { \"/dashboard\": [] },\n * dynamicRoutes: [\n * { pattern: \"/[organization_id]/admin\", requiredRoles: [\"organization.owner\"] },\n * ],\n * }\n *\n * export const proxy = createProxyHandler(proxyConfig)\n *\n * // Config must be static for Next.js\n * export const config = {\n * matcher: [\"/((?!_next/static|_next/image|favicon.ico|public).*)\"],\n * }\n * ```\n */\nexport function createProxyHandler(\n options: ProxyOptions\n): (request: NextRequest) => Promise<Response> {\n const {\n basePath,\n protectedRoutes,\n dynamicRoutes = [],\n errorPath,\n } = options\n\n async function proxy(request: NextRequest): Promise<Response> {\n const { pathname } = request.nextUrl\n\n // Check dynamic route patterns first\n const dynamicMatch = findDynamicRouteMatch(\n pathname,\n dynamicRoutes,\n basePath\n )\n\n // Check if route matches protected routes\n const staticProtectedMatch = findMatchingProtectedRoute(pathname, protectedRoutes)\n\n // If route is not in protectedRoutes or dynamicRoutes, it's public (no auth needed)\n const isProtected = staticProtectedMatch !== undefined || dynamicMatch?.matched === true\n\n if (!isProtected) {\n return NextResponse.next()\n }\n\n // Protected routes - validate session\n const cookie = extractSessionCookie(request.cookies)\n\n if (!cookie) {\n // Use simple absolute-relative path since all apps share the same hostname\n const signInUrl = new URL(\"/core/auth/signin\", request.nextUrl.origin)\n signInUrl.searchParams.set(\"callbackUrl\", request.url)\n return NextResponse.redirect(signInUrl)\n }\n\n const secret = process.env.AUTH_SECRET\n if (!secret) {\n console.error(\"[bridge] AUTH_SECRET is not configured\")\n const errorUrl = errorPath\n ? new URL(errorPath, request.url)\n : new URL(\"/error?code=config\", request.url)\n return NextResponse.redirect(errorUrl)\n }\n\n const result = await decodeSessionToken(cookie.value, secret, cookie.isSecure)\n\n if (!result.success) {\n // Use simple absolute-relative path since all apps share the same hostname\n const signInUrl = new URL(\"/core/auth/signin\", request.nextUrl.origin)\n return NextResponse.redirect(signInUrl)\n }\n\n // Check roles if required\n const requiredRoles = dynamicMatch?.requiredRoles || staticProtectedMatch?.requiredRoles || []\n \n if (requiredRoles.length > 0) {\n // Need organization ID for role checking\n const organizationId = dynamicMatch?.params.organization_id || \n request.headers.get(\"x-organization-id\")\n \n if (!organizationId) {\n // Can't check roles without organization ID\n const signInUrl = new URL(\"/core/auth/signin\", request.nextUrl.origin)\n signInUrl.searchParams.set(\"callbackUrl\", request.url)\n return NextResponse.redirect(signInUrl)\n }\n\n // Fetch user roles\n const rolesResult = await fetchUserRoles({\n organizationId,\n sessionToken: cookie.value,\n isSecure: cookie.isSecure,\n })\n\n if (!rolesResult.success) {\n console.error(\"[bridge] Failed to fetch roles:\", rolesResult.error)\n const signInUrl = new URL(\"/core/auth/signin\", request.nextUrl.origin)\n signInUrl.searchParams.set(\"callbackUrl\", request.url)\n return NextResponse.redirect(signInUrl)\n }\n\n // Check if user has any of the required roles\n if (!hasAnyRole(rolesResult.roles, requiredRoles)) {\n // User doesn't have required role\n const signInUrl = new URL(\"/core/auth/signin\", request.nextUrl.origin)\n signInUrl.searchParams.set(\"callbackUrl\", request.url)\n return NextResponse.redirect(signInUrl)\n }\n }\n\n // Add user info to headers for downstream use\n const requestHeaders = new Headers(request.headers)\n requestHeaders.set(\"x-user-id\", result.session.user.id)\n requestHeaders.set(\"x-user-email\", result.session.user.email)\n\n // Add organization ID if from dynamic route\n if (dynamicMatch?.matched && dynamicMatch.params.organization_id) {\n requestHeaders.set(\"x-organization-id\", dynamicMatch.params.organization_id)\n }\n\n // Add any other dynamic params\n if (dynamicMatch?.matched) {\n for (const [key, value] of Object.entries(dynamicMatch.params)) {\n requestHeaders.set(`x-param-${key}`, value)\n }\n }\n\n return NextResponse.next({\n request: {\n headers: requestHeaders,\n },\n })\n }\n\n return proxy\n}\n\n/**\n * Default matcher config for proxy\n * Can be used if you don't want to define your own config\n */\nexport const defaultProxyConfig = {\n matcher: [\n /*\n * Match all request paths except:\n * - _next/static (static files)\n * - _next/image (image optimization files)\n * - favicon.ico (favicon file)\n * - public files (public folder)\n */\n \"/((?!_next/static|_next/image|favicon.ico|public).*)\",\n ],\n}\n\n"]}
|
package/dist/next/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { b as Session, S as SessionCookie, M as MicroFrontendConfig } from '../types-
|
|
2
|
-
export {
|
|
1
|
+
import { b as Session, S as SessionCookie, M as MicroFrontendConfig } from '../types-BndSEjVw.cjs';
|
|
2
|
+
export { c as SessionUser } from '../types-BndSEjVw.cjs';
|
|
3
3
|
import { NextRequest } from 'next/server';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -318,7 +318,7 @@ declare function getSessionFromRequest(request: NextRequest): Promise<Session |
|
|
|
318
318
|
* import { createProxyHandler, type ProxyOptions } from "@logickernel/bridge/next"
|
|
319
319
|
*
|
|
320
320
|
* const proxyConfig: ProxyOptions = {
|
|
321
|
-
* basePath: "/
|
|
321
|
+
* basePath: "/app",
|
|
322
322
|
* protectedRoutes: { "/dashboard": [] },
|
|
323
323
|
* dynamicRoutes: [
|
|
324
324
|
* { pattern: "/[organization_id]/admin", requiredRoles: ["organization.owner"] },
|
package/dist/next/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { b as Session, S as SessionCookie, M as MicroFrontendConfig } from '../types-
|
|
2
|
-
export {
|
|
1
|
+
import { b as Session, S as SessionCookie, M as MicroFrontendConfig } from '../types-BndSEjVw.js';
|
|
2
|
+
export { c as SessionUser } from '../types-BndSEjVw.js';
|
|
3
3
|
import { NextRequest } from 'next/server';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -318,7 +318,7 @@ declare function getSessionFromRequest(request: NextRequest): Promise<Session |
|
|
|
318
318
|
* import { createProxyHandler, type ProxyOptions } from "@logickernel/bridge/next"
|
|
319
319
|
*
|
|
320
320
|
* const proxyConfig: ProxyOptions = {
|
|
321
|
-
* basePath: "/
|
|
321
|
+
* basePath: "/app",
|
|
322
322
|
* protectedRoutes: { "/dashboard": [] },
|
|
323
323
|
* dynamicRoutes: [
|
|
324
324
|
* { pattern: "/[organization_id]/admin", requiredRoles: ["organization.owner"] },
|
package/dist/next/index.js
CHANGED
|
@@ -115,24 +115,6 @@ function hasAnyRole(userRoles, requiredRoles) {
|
|
|
115
115
|
return requiredRoles.some((role) => userRoles.includes(role));
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
// src/routes.ts
|
|
119
|
-
function findMatchingProtectedRoute(pathname, protectedRoutes) {
|
|
120
|
-
for (const [route, roles] of Object.entries(protectedRoutes)) {
|
|
121
|
-
if (pathname.startsWith(route)) {
|
|
122
|
-
return { route, requiredRoles: roles };
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
return void 0;
|
|
126
|
-
}
|
|
127
|
-
function buildSignInUrl(callbackUrl) {
|
|
128
|
-
const authUrl = getAuthUrl();
|
|
129
|
-
const url = new URL("/core/auth/signin", authUrl);
|
|
130
|
-
if (callbackUrl) {
|
|
131
|
-
url.searchParams.set("callbackUrl", callbackUrl);
|
|
132
|
-
}
|
|
133
|
-
return url;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
118
|
// src/next/session.ts
|
|
137
119
|
async function getSessionCookie() {
|
|
138
120
|
const cookieStore = await cookies();
|
|
@@ -307,6 +289,18 @@ async function getSessionFromRequest(request) {
|
|
|
307
289
|
const validation = await validateSession(request);
|
|
308
290
|
return validation.success ? validation.session : null;
|
|
309
291
|
}
|
|
292
|
+
|
|
293
|
+
// src/routes.ts
|
|
294
|
+
function findMatchingProtectedRoute(pathname, protectedRoutes) {
|
|
295
|
+
for (const [route, roles] of Object.entries(protectedRoutes)) {
|
|
296
|
+
if (pathname.startsWith(route)) {
|
|
297
|
+
return { route, requiredRoles: roles };
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return void 0;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// src/next/proxy.ts
|
|
310
304
|
function matchDynamicRoute(pathname, pattern, basePath) {
|
|
311
305
|
const relativePath = pathname.replace(basePath, "") || "/";
|
|
312
306
|
const paramNames = [];
|
|
@@ -358,7 +352,8 @@ function createProxyHandler(options) {
|
|
|
358
352
|
}
|
|
359
353
|
const cookie = extractSessionCookie(request.cookies);
|
|
360
354
|
if (!cookie) {
|
|
361
|
-
const signInUrl =
|
|
355
|
+
const signInUrl = new URL("/core/auth/signin", request.nextUrl.origin);
|
|
356
|
+
signInUrl.searchParams.set("callbackUrl", request.url);
|
|
362
357
|
return NextResponse.redirect(signInUrl);
|
|
363
358
|
}
|
|
364
359
|
const secret = process.env.AUTH_SECRET;
|
|
@@ -369,9 +364,34 @@ function createProxyHandler(options) {
|
|
|
369
364
|
}
|
|
370
365
|
const result = await decodeSessionToken(cookie.value, secret, cookie.isSecure);
|
|
371
366
|
if (!result.success) {
|
|
372
|
-
const signInUrl =
|
|
367
|
+
const signInUrl = new URL("/core/auth/signin", request.nextUrl.origin);
|
|
373
368
|
return NextResponse.redirect(signInUrl);
|
|
374
369
|
}
|
|
370
|
+
const requiredRoles = dynamicMatch?.requiredRoles || staticProtectedMatch?.requiredRoles || [];
|
|
371
|
+
if (requiredRoles.length > 0) {
|
|
372
|
+
const organizationId = dynamicMatch?.params.organization_id || request.headers.get("x-organization-id");
|
|
373
|
+
if (!organizationId) {
|
|
374
|
+
const signInUrl = new URL("/core/auth/signin", request.nextUrl.origin);
|
|
375
|
+
signInUrl.searchParams.set("callbackUrl", request.url);
|
|
376
|
+
return NextResponse.redirect(signInUrl);
|
|
377
|
+
}
|
|
378
|
+
const rolesResult = await fetchUserRoles({
|
|
379
|
+
organizationId,
|
|
380
|
+
sessionToken: cookie.value,
|
|
381
|
+
isSecure: cookie.isSecure
|
|
382
|
+
});
|
|
383
|
+
if (!rolesResult.success) {
|
|
384
|
+
console.error("[bridge] Failed to fetch roles:", rolesResult.error);
|
|
385
|
+
const signInUrl = new URL("/core/auth/signin", request.nextUrl.origin);
|
|
386
|
+
signInUrl.searchParams.set("callbackUrl", request.url);
|
|
387
|
+
return NextResponse.redirect(signInUrl);
|
|
388
|
+
}
|
|
389
|
+
if (!hasAnyRole(rolesResult.roles, requiredRoles)) {
|
|
390
|
+
const signInUrl = new URL("/core/auth/signin", request.nextUrl.origin);
|
|
391
|
+
signInUrl.searchParams.set("callbackUrl", request.url);
|
|
392
|
+
return NextResponse.redirect(signInUrl);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
375
395
|
const requestHeaders = new Headers(request.headers);
|
|
376
396
|
requestHeaders.set("x-user-id", result.session.user.id);
|
|
377
397
|
requestHeaders.set("x-user-email", result.session.user.email);
|