@logickernel/bridge 0.13.3 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -9
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/next/index.cjs +32 -34
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.d.cts +7 -34
- package/dist/next/index.d.ts +7 -34
- package/dist/next/index.js +32 -34
- package/dist/next/index.js.map +1 -1
- package/dist/styles.css +1 -0
- package/dist/{types-BndSEjVw.d.cts → types-CyxNdDrk.d.cts} +5 -3
- package/dist/{types-BndSEjVw.d.ts → types-CyxNdDrk.d.ts} +5 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -258,16 +258,11 @@ import { createProxyHandler, type ProxyOptions } from "@logickernel/bridge/next"
|
|
|
258
258
|
|
|
259
259
|
const config: ProxyOptions = {
|
|
260
260
|
basePath: "/app",
|
|
261
|
-
|
|
262
|
-
"/
|
|
263
|
-
"/
|
|
261
|
+
routes: {
|
|
262
|
+
"/dashboard": [], // Any authenticated user (relative to basePath)
|
|
263
|
+
"/settings": [],
|
|
264
|
+
"/[organization_id]/admin": ["organization.owner"], // Dynamic route with role requirement
|
|
264
265
|
},
|
|
265
|
-
dynamicRoutes: [
|
|
266
|
-
{
|
|
267
|
-
pattern: "/app/[organization_id]/admin",
|
|
268
|
-
requiredRoles: ["organization.owner"],
|
|
269
|
-
},
|
|
270
|
-
],
|
|
271
266
|
}
|
|
272
267
|
|
|
273
268
|
export const proxy = createProxyHandler(config)
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { D as DecodeResult, S as SessionCookie, a as DecodedToken, b as Session } from './types-
|
|
2
|
-
export { c as SessionUser } from './types-
|
|
1
|
+
import { D as DecodeResult, S as SessionCookie, a as DecodedToken, b as Session } from './types-CyxNdDrk.cjs';
|
|
2
|
+
export { c as SessionUser } from './types-CyxNdDrk.cjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Kernel Bridge JWT
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { D as DecodeResult, S as SessionCookie, a as DecodedToken, b as Session } from './types-
|
|
2
|
-
export { c as SessionUser } from './types-
|
|
1
|
+
import { D as DecodeResult, S as SessionCookie, a as DecodedToken, b as Session } from './types-CyxNdDrk.js';
|
|
2
|
+
export { c as SessionUser } from './types-CyxNdDrk.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Kernel Bridge JWT
|
package/dist/next/index.cjs
CHANGED
|
@@ -293,18 +293,10 @@ async function getSessionFromRequest(request) {
|
|
|
293
293
|
}
|
|
294
294
|
|
|
295
295
|
// src/routes.ts
|
|
296
|
-
function
|
|
297
|
-
|
|
298
|
-
if (pathname.startsWith(route)) {
|
|
299
|
-
return { route, requiredRoles: roles };
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
return void 0;
|
|
296
|
+
function isDynamicRoute(route) {
|
|
297
|
+
return route.includes("[") && route.includes("]");
|
|
303
298
|
}
|
|
304
|
-
|
|
305
|
-
// src/next/proxy.ts
|
|
306
|
-
function matchDynamicRoute(pathname, pattern, basePath) {
|
|
307
|
-
const relativePath = pathname.replace(basePath, "") || "/";
|
|
299
|
+
function matchDynamicRoutePattern(relativePath, pattern) {
|
|
308
300
|
const paramNames = [];
|
|
309
301
|
const regexPattern = pattern.replace(/\[([^\]]+)\]/g, (_, paramName) => {
|
|
310
302
|
paramNames.push(paramName);
|
|
@@ -313,7 +305,7 @@ function matchDynamicRoute(pathname, pattern, basePath) {
|
|
|
313
305
|
const regex = new RegExp(`^${regexPattern}(?:/.*)?$`);
|
|
314
306
|
const match = relativePath.match(regex);
|
|
315
307
|
if (!match) {
|
|
316
|
-
return
|
|
308
|
+
return null;
|
|
317
309
|
}
|
|
318
310
|
const params = {};
|
|
319
311
|
paramNames.forEach((name, index) => {
|
|
@@ -322,33 +314,39 @@ function matchDynamicRoute(pathname, pattern, basePath) {
|
|
|
322
314
|
params[name] = value;
|
|
323
315
|
}
|
|
324
316
|
});
|
|
325
|
-
return {
|
|
317
|
+
return { params };
|
|
326
318
|
}
|
|
327
|
-
function
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
319
|
+
function findMatchingRoute(pathname, routes, basePath) {
|
|
320
|
+
if (!pathname.startsWith(basePath)) {
|
|
321
|
+
return void 0;
|
|
322
|
+
}
|
|
323
|
+
const relativePath = pathname.replace(basePath, "") || "/";
|
|
324
|
+
for (const [route, requiredRoles] of Object.entries(routes)) {
|
|
325
|
+
if (isDynamicRoute(route)) {
|
|
326
|
+
const match = matchDynamicRoutePattern(relativePath, route);
|
|
327
|
+
if (match) {
|
|
328
|
+
return { route, requiredRoles, params: match.params };
|
|
329
|
+
}
|
|
330
|
+
} else {
|
|
331
|
+
if (relativePath.startsWith(route)) {
|
|
332
|
+
return { route, requiredRoles };
|
|
333
|
+
}
|
|
332
334
|
}
|
|
333
335
|
}
|
|
334
|
-
return
|
|
336
|
+
return void 0;
|
|
335
337
|
}
|
|
338
|
+
|
|
339
|
+
// src/next/proxy.ts
|
|
336
340
|
function createProxyHandler(options) {
|
|
337
341
|
const {
|
|
338
342
|
basePath,
|
|
339
|
-
|
|
340
|
-
dynamicRoutes = [],
|
|
343
|
+
routes,
|
|
341
344
|
errorPath
|
|
342
345
|
} = options;
|
|
343
346
|
async function proxy(request) {
|
|
344
347
|
const { pathname } = request.nextUrl;
|
|
345
|
-
const
|
|
346
|
-
|
|
347
|
-
dynamicRoutes,
|
|
348
|
-
basePath
|
|
349
|
-
);
|
|
350
|
-
const staticProtectedMatch = findMatchingProtectedRoute(pathname, protectedRoutes);
|
|
351
|
-
const isProtected = staticProtectedMatch !== void 0 || dynamicMatch?.matched === true;
|
|
348
|
+
const routeMatch = findMatchingRoute(pathname, routes, basePath);
|
|
349
|
+
const isProtected = routeMatch !== void 0;
|
|
352
350
|
if (!isProtected) {
|
|
353
351
|
return server.NextResponse.next();
|
|
354
352
|
}
|
|
@@ -369,9 +367,9 @@ function createProxyHandler(options) {
|
|
|
369
367
|
const signInUrl = new URL("/core/auth/signin", request.nextUrl.origin);
|
|
370
368
|
return server.NextResponse.redirect(signInUrl);
|
|
371
369
|
}
|
|
372
|
-
const requiredRoles =
|
|
370
|
+
const requiredRoles = routeMatch?.requiredRoles || [];
|
|
373
371
|
if (requiredRoles.length > 0) {
|
|
374
|
-
const organizationId =
|
|
372
|
+
const organizationId = routeMatch?.params?.organization_id || request.headers.get("x-organization-id");
|
|
375
373
|
if (!organizationId) {
|
|
376
374
|
const signInUrl = new URL("/core/auth/signin", request.nextUrl.origin);
|
|
377
375
|
signInUrl.searchParams.set("callbackUrl", request.url);
|
|
@@ -397,11 +395,11 @@ function createProxyHandler(options) {
|
|
|
397
395
|
const requestHeaders = new Headers(request.headers);
|
|
398
396
|
requestHeaders.set("x-user-id", result.session.user.id);
|
|
399
397
|
requestHeaders.set("x-user-email", result.session.user.email);
|
|
400
|
-
if (
|
|
401
|
-
requestHeaders.set("x-organization-id",
|
|
398
|
+
if (routeMatch?.params?.organization_id) {
|
|
399
|
+
requestHeaders.set("x-organization-id", routeMatch.params.organization_id);
|
|
402
400
|
}
|
|
403
|
-
if (
|
|
404
|
-
for (const [key, value] of Object.entries(
|
|
401
|
+
if (routeMatch?.params) {
|
|
402
|
+
for (const [key, value] of Object.entries(routeMatch.params)) {
|
|
405
403
|
requestHeaders.set(`x-param-${key}`, value);
|
|
406
404
|
}
|
|
407
405
|
}
|
package/dist/next/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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"]}
|
|
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 * @deprecated Use findMatchingRoute instead, which supports basePath-relative routes\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 * Check if a route pattern is dynamic (contains [param] syntax)\n */\nfunction isDynamicRoute(route: string): boolean {\n return route.includes(\"[\") && route.includes(\"]\")\n}\n\n/**\n * Match a dynamic route pattern against a relative pathname\n * @param relativePath - Path relative to basePath (e.g., \"/abc123/admin\")\n * @param pattern - Route pattern with [param] syntax (e.g., \"/[organization_id]/admin\")\n * @returns Match result with params if matched, null otherwise\n */\nfunction matchDynamicRoutePattern(\n relativePath: string,\n pattern: string\n): { params: Record<string, string> } | null {\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 null\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 { params }\n}\n\n/**\n * Find the first matching route for a pathname (supports both static and dynamic routes)\n * Routes are relative to basePath, and basePath is prepended during matching\n * \n * @param pathname - Full pathname (e.g., \"/app/dashboard\")\n * @param routes - Route configuration (routes are relative to basePath)\n * @param basePath - Base path for the micro-frontend (e.g., \"/app\")\n * @returns Match result with route, requiredRoles, and params (if dynamic), or undefined\n */\nexport function findMatchingRoute(\n pathname: string,\n routes: RouteConfig,\n basePath: string\n): { route: string; requiredRoles: string[]; params?: Record<string, string> } | undefined {\n // Check if pathname starts with basePath\n if (!pathname.startsWith(basePath)) {\n return undefined\n }\n\n // Extract relative path (remove basePath)\n const relativePath = pathname.replace(basePath, \"\") || \"/\"\n\n // Check each route\n for (const [route, requiredRoles] of Object.entries(routes)) {\n if (isDynamicRoute(route)) {\n // Dynamic route: match pattern\n const match = matchDynamicRoutePattern(relativePath, route)\n if (match) {\n return { route, requiredRoles, params: match.params }\n }\n } else {\n // Static route: prefix matching\n if (relativePath.startsWith(route)) {\n return { route, requiredRoles }\n }\n }\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(\"/app/dashboard\", {\n * basePath: \"/app\",\n * publicRoutes: [\"/\", \"/api/health\"],\n * routes: { \"/dashboard\": [], \"/admin\": [\"organization.owner\"] },\n * })\n * // Returns: { type: \"protected\", requiredRoles: [] }\n * ```\n */\nexport function matchRoute(\n pathname: string,\n config: Pick<MicroFrontendConfig, \"basePath\" | \"publicRoutes\" | \"routes\">\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 routes (relative to basePath)\n const match = findMatchingRoute(pathname, config.routes, config.basePath)\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 routes: options.routes || {\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 findMatchingRoute,\n} from \"../routes\"\nimport type { ProxyOptions } from \"./types\"\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 * routes: {\n * \"/dashboard\": [],\n * \"/[organization_id]/admin\": [\"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 routes,\n errorPath,\n } = options\n\n async function proxy(request: NextRequest): Promise<Response> {\n const { pathname } = request.nextUrl\n\n // Check if route matches any configured route (static or dynamic)\n const routeMatch = findMatchingRoute(pathname, routes, basePath)\n\n // If route is not in routes, it's public (no auth needed)\n const isProtected = routeMatch !== undefined\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 = routeMatch?.requiredRoles || []\n \n if (requiredRoles.length > 0) {\n // Need organization ID for role checking\n const organizationId = routeMatch?.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 route params\n if (routeMatch?.params?.organization_id) {\n requestHeaders.set(\"x-organization-id\", routeMatch.params.organization_id)\n }\n\n // Add any other route params\n if (routeMatch?.params) {\n for (const [key, value] of Object.entries(routeMatch.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 { c as SessionUser } from '../types-
|
|
1
|
+
import { b as Session, S as SessionCookie, M as MicroFrontendConfig } from '../types-CyxNdDrk.cjs';
|
|
2
|
+
export { c as SessionUser } from '../types-CyxNdDrk.cjs';
|
|
3
3
|
import { NextRequest } from 'next/server';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -79,38 +79,11 @@ declare function getUserRoles(organizationId: string): Promise<string[]>;
|
|
|
79
79
|
* Options for the proxy factory
|
|
80
80
|
*/
|
|
81
81
|
interface ProxyOptions extends MicroFrontendConfig {
|
|
82
|
-
/**
|
|
83
|
-
* Dynamic route patterns that contain organization IDs
|
|
84
|
-
* e.g., "/[organization_id]/admin" matches "/abc123/admin"
|
|
85
|
-
*/
|
|
86
|
-
dynamicRoutes?: DynamicRoutePattern[];
|
|
87
82
|
/**
|
|
88
83
|
* Custom error page path (defaults to showing error in redirect)
|
|
89
84
|
*/
|
|
90
85
|
errorPath?: string;
|
|
91
86
|
}
|
|
92
|
-
/**
|
|
93
|
-
* Dynamic route pattern configuration
|
|
94
|
-
*/
|
|
95
|
-
interface DynamicRoutePattern {
|
|
96
|
-
/**
|
|
97
|
-
* Pattern to match (e.g., "/[organization_id]/admin")
|
|
98
|
-
* Use [param] syntax for dynamic segments
|
|
99
|
-
*/
|
|
100
|
-
pattern: string;
|
|
101
|
-
/**
|
|
102
|
-
* Required roles for this route (empty = any authenticated user)
|
|
103
|
-
*/
|
|
104
|
-
requiredRoles: string[];
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Result from parsing a dynamic route
|
|
108
|
-
*/
|
|
109
|
-
interface DynamicRouteMatch {
|
|
110
|
-
matched: boolean;
|
|
111
|
-
params: Record<string, string>;
|
|
112
|
-
requiredRoles: string[];
|
|
113
|
-
}
|
|
114
87
|
/**
|
|
115
88
|
* API route handler with auth context
|
|
116
89
|
*/
|
|
@@ -319,10 +292,10 @@ declare function getSessionFromRequest(request: NextRequest): Promise<Session |
|
|
|
319
292
|
*
|
|
320
293
|
* const proxyConfig: ProxyOptions = {
|
|
321
294
|
* basePath: "/app",
|
|
322
|
-
*
|
|
323
|
-
*
|
|
324
|
-
*
|
|
325
|
-
*
|
|
295
|
+
* routes: {
|
|
296
|
+
* "/dashboard": [],
|
|
297
|
+
* "/[organization_id]/admin": ["organization.owner"],
|
|
298
|
+
* },
|
|
326
299
|
* }
|
|
327
300
|
*
|
|
328
301
|
* export const proxy = createProxyHandler(proxyConfig)
|
|
@@ -342,4 +315,4 @@ declare const defaultProxyConfig: {
|
|
|
342
315
|
matcher: string[];
|
|
343
316
|
};
|
|
344
317
|
|
|
345
|
-
export { type AuthContext, type AuthenticatedHandler, type
|
|
318
|
+
export { type AuthContext, type AuthenticatedHandler, type PageAuthOptions, type PageAuthResult, type ProxyOptions, Session, SessionCookie, type WithAuthOptions, checkPageAuth, createProxyHandler, defaultProxyConfig, getBaseUrl, getSession, getSessionCookie, getSessionFromRequest, getSessionToken, getUserRoles, hasRequiredRole, isSecureCookie, requireAuth, withAuth };
|
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 { c as SessionUser } from '../types-
|
|
1
|
+
import { b as Session, S as SessionCookie, M as MicroFrontendConfig } from '../types-CyxNdDrk.js';
|
|
2
|
+
export { c as SessionUser } from '../types-CyxNdDrk.js';
|
|
3
3
|
import { NextRequest } from 'next/server';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -79,38 +79,11 @@ declare function getUserRoles(organizationId: string): Promise<string[]>;
|
|
|
79
79
|
* Options for the proxy factory
|
|
80
80
|
*/
|
|
81
81
|
interface ProxyOptions extends MicroFrontendConfig {
|
|
82
|
-
/**
|
|
83
|
-
* Dynamic route patterns that contain organization IDs
|
|
84
|
-
* e.g., "/[organization_id]/admin" matches "/abc123/admin"
|
|
85
|
-
*/
|
|
86
|
-
dynamicRoutes?: DynamicRoutePattern[];
|
|
87
82
|
/**
|
|
88
83
|
* Custom error page path (defaults to showing error in redirect)
|
|
89
84
|
*/
|
|
90
85
|
errorPath?: string;
|
|
91
86
|
}
|
|
92
|
-
/**
|
|
93
|
-
* Dynamic route pattern configuration
|
|
94
|
-
*/
|
|
95
|
-
interface DynamicRoutePattern {
|
|
96
|
-
/**
|
|
97
|
-
* Pattern to match (e.g., "/[organization_id]/admin")
|
|
98
|
-
* Use [param] syntax for dynamic segments
|
|
99
|
-
*/
|
|
100
|
-
pattern: string;
|
|
101
|
-
/**
|
|
102
|
-
* Required roles for this route (empty = any authenticated user)
|
|
103
|
-
*/
|
|
104
|
-
requiredRoles: string[];
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Result from parsing a dynamic route
|
|
108
|
-
*/
|
|
109
|
-
interface DynamicRouteMatch {
|
|
110
|
-
matched: boolean;
|
|
111
|
-
params: Record<string, string>;
|
|
112
|
-
requiredRoles: string[];
|
|
113
|
-
}
|
|
114
87
|
/**
|
|
115
88
|
* API route handler with auth context
|
|
116
89
|
*/
|
|
@@ -319,10 +292,10 @@ declare function getSessionFromRequest(request: NextRequest): Promise<Session |
|
|
|
319
292
|
*
|
|
320
293
|
* const proxyConfig: ProxyOptions = {
|
|
321
294
|
* basePath: "/app",
|
|
322
|
-
*
|
|
323
|
-
*
|
|
324
|
-
*
|
|
325
|
-
*
|
|
295
|
+
* routes: {
|
|
296
|
+
* "/dashboard": [],
|
|
297
|
+
* "/[organization_id]/admin": ["organization.owner"],
|
|
298
|
+
* },
|
|
326
299
|
* }
|
|
327
300
|
*
|
|
328
301
|
* export const proxy = createProxyHandler(proxyConfig)
|
|
@@ -342,4 +315,4 @@ declare const defaultProxyConfig: {
|
|
|
342
315
|
matcher: string[];
|
|
343
316
|
};
|
|
344
317
|
|
|
345
|
-
export { type AuthContext, type AuthenticatedHandler, type
|
|
318
|
+
export { type AuthContext, type AuthenticatedHandler, type PageAuthOptions, type PageAuthResult, type ProxyOptions, Session, SessionCookie, type WithAuthOptions, checkPageAuth, createProxyHandler, defaultProxyConfig, getBaseUrl, getSession, getSessionCookie, getSessionFromRequest, getSessionToken, getUserRoles, hasRequiredRole, isSecureCookie, requireAuth, withAuth };
|
package/dist/next/index.js
CHANGED
|
@@ -291,18 +291,10 @@ async function getSessionFromRequest(request) {
|
|
|
291
291
|
}
|
|
292
292
|
|
|
293
293
|
// src/routes.ts
|
|
294
|
-
function
|
|
295
|
-
|
|
296
|
-
if (pathname.startsWith(route)) {
|
|
297
|
-
return { route, requiredRoles: roles };
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
return void 0;
|
|
294
|
+
function isDynamicRoute(route) {
|
|
295
|
+
return route.includes("[") && route.includes("]");
|
|
301
296
|
}
|
|
302
|
-
|
|
303
|
-
// src/next/proxy.ts
|
|
304
|
-
function matchDynamicRoute(pathname, pattern, basePath) {
|
|
305
|
-
const relativePath = pathname.replace(basePath, "") || "/";
|
|
297
|
+
function matchDynamicRoutePattern(relativePath, pattern) {
|
|
306
298
|
const paramNames = [];
|
|
307
299
|
const regexPattern = pattern.replace(/\[([^\]]+)\]/g, (_, paramName) => {
|
|
308
300
|
paramNames.push(paramName);
|
|
@@ -311,7 +303,7 @@ function matchDynamicRoute(pathname, pattern, basePath) {
|
|
|
311
303
|
const regex = new RegExp(`^${regexPattern}(?:/.*)?$`);
|
|
312
304
|
const match = relativePath.match(regex);
|
|
313
305
|
if (!match) {
|
|
314
|
-
return
|
|
306
|
+
return null;
|
|
315
307
|
}
|
|
316
308
|
const params = {};
|
|
317
309
|
paramNames.forEach((name, index) => {
|
|
@@ -320,33 +312,39 @@ function matchDynamicRoute(pathname, pattern, basePath) {
|
|
|
320
312
|
params[name] = value;
|
|
321
313
|
}
|
|
322
314
|
});
|
|
323
|
-
return {
|
|
315
|
+
return { params };
|
|
324
316
|
}
|
|
325
|
-
function
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
317
|
+
function findMatchingRoute(pathname, routes, basePath) {
|
|
318
|
+
if (!pathname.startsWith(basePath)) {
|
|
319
|
+
return void 0;
|
|
320
|
+
}
|
|
321
|
+
const relativePath = pathname.replace(basePath, "") || "/";
|
|
322
|
+
for (const [route, requiredRoles] of Object.entries(routes)) {
|
|
323
|
+
if (isDynamicRoute(route)) {
|
|
324
|
+
const match = matchDynamicRoutePattern(relativePath, route);
|
|
325
|
+
if (match) {
|
|
326
|
+
return { route, requiredRoles, params: match.params };
|
|
327
|
+
}
|
|
328
|
+
} else {
|
|
329
|
+
if (relativePath.startsWith(route)) {
|
|
330
|
+
return { route, requiredRoles };
|
|
331
|
+
}
|
|
330
332
|
}
|
|
331
333
|
}
|
|
332
|
-
return
|
|
334
|
+
return void 0;
|
|
333
335
|
}
|
|
336
|
+
|
|
337
|
+
// src/next/proxy.ts
|
|
334
338
|
function createProxyHandler(options) {
|
|
335
339
|
const {
|
|
336
340
|
basePath,
|
|
337
|
-
|
|
338
|
-
dynamicRoutes = [],
|
|
341
|
+
routes,
|
|
339
342
|
errorPath
|
|
340
343
|
} = options;
|
|
341
344
|
async function proxy(request) {
|
|
342
345
|
const { pathname } = request.nextUrl;
|
|
343
|
-
const
|
|
344
|
-
|
|
345
|
-
dynamicRoutes,
|
|
346
|
-
basePath
|
|
347
|
-
);
|
|
348
|
-
const staticProtectedMatch = findMatchingProtectedRoute(pathname, protectedRoutes);
|
|
349
|
-
const isProtected = staticProtectedMatch !== void 0 || dynamicMatch?.matched === true;
|
|
346
|
+
const routeMatch = findMatchingRoute(pathname, routes, basePath);
|
|
347
|
+
const isProtected = routeMatch !== void 0;
|
|
350
348
|
if (!isProtected) {
|
|
351
349
|
return NextResponse.next();
|
|
352
350
|
}
|
|
@@ -367,9 +365,9 @@ function createProxyHandler(options) {
|
|
|
367
365
|
const signInUrl = new URL("/core/auth/signin", request.nextUrl.origin);
|
|
368
366
|
return NextResponse.redirect(signInUrl);
|
|
369
367
|
}
|
|
370
|
-
const requiredRoles =
|
|
368
|
+
const requiredRoles = routeMatch?.requiredRoles || [];
|
|
371
369
|
if (requiredRoles.length > 0) {
|
|
372
|
-
const organizationId =
|
|
370
|
+
const organizationId = routeMatch?.params?.organization_id || request.headers.get("x-organization-id");
|
|
373
371
|
if (!organizationId) {
|
|
374
372
|
const signInUrl = new URL("/core/auth/signin", request.nextUrl.origin);
|
|
375
373
|
signInUrl.searchParams.set("callbackUrl", request.url);
|
|
@@ -395,11 +393,11 @@ function createProxyHandler(options) {
|
|
|
395
393
|
const requestHeaders = new Headers(request.headers);
|
|
396
394
|
requestHeaders.set("x-user-id", result.session.user.id);
|
|
397
395
|
requestHeaders.set("x-user-email", result.session.user.email);
|
|
398
|
-
if (
|
|
399
|
-
requestHeaders.set("x-organization-id",
|
|
396
|
+
if (routeMatch?.params?.organization_id) {
|
|
397
|
+
requestHeaders.set("x-organization-id", routeMatch.params.organization_id);
|
|
400
398
|
}
|
|
401
|
-
if (
|
|
402
|
-
for (const [key, value] of Object.entries(
|
|
399
|
+
if (routeMatch?.params) {
|
|
400
|
+
for (const [key, value] of Object.entries(routeMatch.params)) {
|
|
403
401
|
requestHeaders.set(`x-param-${key}`, value);
|
|
404
402
|
}
|
|
405
403
|
}
|
package/dist/next/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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.js","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"]}
|
|
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.js","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 * @deprecated Use findMatchingRoute instead, which supports basePath-relative routes\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 * Check if a route pattern is dynamic (contains [param] syntax)\n */\nfunction isDynamicRoute(route: string): boolean {\n return route.includes(\"[\") && route.includes(\"]\")\n}\n\n/**\n * Match a dynamic route pattern against a relative pathname\n * @param relativePath - Path relative to basePath (e.g., \"/abc123/admin\")\n * @param pattern - Route pattern with [param] syntax (e.g., \"/[organization_id]/admin\")\n * @returns Match result with params if matched, null otherwise\n */\nfunction matchDynamicRoutePattern(\n relativePath: string,\n pattern: string\n): { params: Record<string, string> } | null {\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 null\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 { params }\n}\n\n/**\n * Find the first matching route for a pathname (supports both static and dynamic routes)\n * Routes are relative to basePath, and basePath is prepended during matching\n * \n * @param pathname - Full pathname (e.g., \"/app/dashboard\")\n * @param routes - Route configuration (routes are relative to basePath)\n * @param basePath - Base path for the micro-frontend (e.g., \"/app\")\n * @returns Match result with route, requiredRoles, and params (if dynamic), or undefined\n */\nexport function findMatchingRoute(\n pathname: string,\n routes: RouteConfig,\n basePath: string\n): { route: string; requiredRoles: string[]; params?: Record<string, string> } | undefined {\n // Check if pathname starts with basePath\n if (!pathname.startsWith(basePath)) {\n return undefined\n }\n\n // Extract relative path (remove basePath)\n const relativePath = pathname.replace(basePath, \"\") || \"/\"\n\n // Check each route\n for (const [route, requiredRoles] of Object.entries(routes)) {\n if (isDynamicRoute(route)) {\n // Dynamic route: match pattern\n const match = matchDynamicRoutePattern(relativePath, route)\n if (match) {\n return { route, requiredRoles, params: match.params }\n }\n } else {\n // Static route: prefix matching\n if (relativePath.startsWith(route)) {\n return { route, requiredRoles }\n }\n }\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(\"/app/dashboard\", {\n * basePath: \"/app\",\n * publicRoutes: [\"/\", \"/api/health\"],\n * routes: { \"/dashboard\": [], \"/admin\": [\"organization.owner\"] },\n * })\n * // Returns: { type: \"protected\", requiredRoles: [] }\n * ```\n */\nexport function matchRoute(\n pathname: string,\n config: Pick<MicroFrontendConfig, \"basePath\" | \"publicRoutes\" | \"routes\">\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 routes (relative to basePath)\n const match = findMatchingRoute(pathname, config.routes, config.basePath)\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 routes: options.routes || {\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 findMatchingRoute,\n} from \"../routes\"\nimport type { ProxyOptions } from \"./types\"\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 * routes: {\n * \"/dashboard\": [],\n * \"/[organization_id]/admin\": [\"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 routes,\n errorPath,\n } = options\n\n async function proxy(request: NextRequest): Promise<Response> {\n const { pathname } = request.nextUrl\n\n // Check if route matches any configured route (static or dynamic)\n const routeMatch = findMatchingRoute(pathname, routes, basePath)\n\n // If route is not in routes, it's public (no auth needed)\n const isProtected = routeMatch !== undefined\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 = routeMatch?.requiredRoles || []\n \n if (requiredRoles.length > 0) {\n // Need organization ID for role checking\n const organizationId = routeMatch?.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 route params\n if (routeMatch?.params?.organization_id) {\n requestHeaders.set(\"x-organization-id\", routeMatch.params.organization_id)\n }\n\n // Add any other route params\n if (routeMatch?.params) {\n for (const [key, value] of Object.entries(routeMatch.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/styles.css
CHANGED
|
@@ -63,12 +63,14 @@ interface MicroFrontendConfig {
|
|
|
63
63
|
*/
|
|
64
64
|
basePath: string;
|
|
65
65
|
/**
|
|
66
|
-
*
|
|
66
|
+
* Routes and their required roles (relative to basePath)
|
|
67
|
+
* Routes can be static (e.g., "/dashboard") or dynamic (e.g., "/[organization_id]/admin")
|
|
68
|
+
* Empty array = any authenticated user, non-empty = requires at least one of the roles
|
|
67
69
|
*/
|
|
68
|
-
|
|
70
|
+
routes: RouteConfig;
|
|
69
71
|
/**
|
|
70
72
|
* Public routes that don't require authentication
|
|
71
|
-
* @deprecated Routes not in
|
|
73
|
+
* @deprecated Routes not in routes are now automatically public.
|
|
72
74
|
* This field is kept for backward compatibility but is no longer used.
|
|
73
75
|
*/
|
|
74
76
|
publicRoutes?: string[];
|
|
@@ -63,12 +63,14 @@ interface MicroFrontendConfig {
|
|
|
63
63
|
*/
|
|
64
64
|
basePath: string;
|
|
65
65
|
/**
|
|
66
|
-
*
|
|
66
|
+
* Routes and their required roles (relative to basePath)
|
|
67
|
+
* Routes can be static (e.g., "/dashboard") or dynamic (e.g., "/[organization_id]/admin")
|
|
68
|
+
* Empty array = any authenticated user, non-empty = requires at least one of the roles
|
|
67
69
|
*/
|
|
68
|
-
|
|
70
|
+
routes: RouteConfig;
|
|
69
71
|
/**
|
|
70
72
|
* Public routes that don't require authentication
|
|
71
|
-
* @deprecated Routes not in
|
|
73
|
+
* @deprecated Routes not in routes are now automatically public.
|
|
72
74
|
* This field is kept for backward compatibility but is no longer used.
|
|
73
75
|
*/
|
|
74
76
|
publicRoutes?: string[];
|