npm package: @logickernel/bridge
Description: Framework-agnostic micro-frontend authentication library for AuthJS/NextAuth JWT tokens
Author: Victor
Latest version: 0.17.5

# @logickernel/bridge

Framework-agnostic micro-frontend authentication library for AuthJS/NextAuth JWT tokens with built-in Next.js components.

## Installation

```bash
npm install @logickernel/bridge
```

## Quick Start (Next.js)

### 1. Environment Variables

Add these to the `.env` file of your application:

```bash
AUTH_SECRET=your-nextauth-secret  # Required: Same secret used by your kernel/auth server
AUTH_URL=http://localhost:3000    # Required: Base URL of your kernel/auth server
BASE_URL=http://localhost:7001    # Optional: Your facade URL for user-facing redirects
```

### 2. Configure Next.js

In your `next.config.ts`, add the bridge package to `transpilePackages`:

```typescript
import type { NextConfig } from "next"

const nextConfig: NextConfig = {
  transpilePackages: ["@logickernel/bridge"],
  // ... other config
}

export default nextConfig
```

Use webpack instead of Turbopack (the default) in your `package.json` scripts:

```json
{
  "scripts": {
    "dev": "next dev --webpack",
    "build": "next build --webpack"
  }
}
```

### 3. Configure Tailwind CSS

Add the `@source` directive to your `src/app/globals.css` to ensure Tailwind scans classes from this package:

```css
@import "tailwindcss";
/* ... other imports ... */

@source "../../node_modules/@logickernel/bridge/dist/**/*.{js,cjs}";
```

**Note**: If using a local `file:../bridge` dependency, use `@source "../../../bridge/dist/**/*.{js,cjs}";` instead. Restart your dev server after adding this directive.

### 4. Use the Layout Component

Create a wrapper component to establish the client/server boundary:

```typescript
"use client"

/**
 * Client boundary wrapper for AppLayout.
 * 
 * This wrapper is required because "use client" directives are not preserved
 * in bundled library code. Next.js needs this directive in your source code
 * to properly handle the client/server boundary.
 */
import { AppLayout } from "@logickernel/bridge/next/components"

export function AppLayoutWrapper({
  children,
  organizationId,
  apiBaseUrl,
}: {
  children: React.ReactNode
  organizationId?: string
  apiBaseUrl?: string
}) {
  return (
    <AppLayout
      organizationId={organizationId}
      apiBaseUrl={apiBaseUrl}
    >
      {children}
    </AppLayout>
  )
}
```

Use in your layout:

```tsx
import { redirect } from "next/navigation"
import { auth } from "@/lib/next-auth" // or your auth setup
import { AppLayoutWrapper } from "@/components/app-layout-wrapper"

export default async function Layout({
  children,
}: {
  children: React.ReactNode
}) {
  const session = await auth()

  if (!session?.user) {
    redirect("/core/auth/signin?callbackUrl=/core/app")
  }

  return (
    <AppLayoutWrapper>
      {children}
    </AppLayoutWrapper>
  )
}
```

The layout automatically fetches user information from the navigation API endpoint.

### 5. Navigation API Endpoint

The layout automatically loads navigation items from `/core/api/navigation/[organization_id]`.

**Required Endpoint:** `GET /core/api/navigation/[organization_id]`

**Response Payload:**

```typescript
{
  items: NavigationItem[]
  organizationId: string
  organizations: NavigationOrganization[]
  user: {
    id: string
    name: string | null
    email: string
    image: string | null
  }
}

interface NavigationItem {
  title: string
  url?: string        // If missing, item is treated as a section label
  icon?: string       // Lucide icon name (e.g., "LayoutDashboard", "Users")
  isActive?: boolean  // Whether the item should be highlighted
  items?: {           // Sub-items for collapsible navigation
    title: string
    url: string
  }[]
}

interface NavigationOrganization {
  id: string
  name: string
  logo?: string       // Lucide icon name for the organization logo
  plan?: string       // Optional plan/badge text
}
```

The endpoint should be protected and only return data the authenticated user has access to. The `{organizationId}` placeholder in item URLs is automatically replaced with the current organization ID.

## Features

- Built-in sidebar navigation with collapsible sections
- Organization switcher
- User menu with profile and sign-out
- Responsive design (mobile and desktop)
- Automatic navigation loading from API
- Consistent UI across microfrontends

## Next.js Utilities

### Server Components / Pages

```tsx
import { getSession } from "@logickernel/bridge/next"
import { redirect } from "next/navigation"

export default async function DashboardPage() {
  const session = await getSession()
  
  if (!session) {
    redirect("/core/auth/signin")
  }

  return <div>Welcome, {session.user.email}!</div>
}
```

### Role-Based Page Protection

```tsx
import { checkPageAuth } from "@logickernel/bridge/next"
import { redirect, notFound } from "next/navigation"

export default async function AdminPage({
  params,
}: {
  params: Promise<{ organization_id: string }>
}) {
  const { organization_id } = await params

  const auth = await checkPageAuth({
    requiredRoles: ["organization.owner", "organization.editor"],
    organizationId: organization_id,
  })

  if (!auth.authenticated) {
    redirect(auth.redirectUrl)
  }

  if (!auth.hasRequiredRole) {
    notFound()
  }

  return <div>Admin Panel for {auth.session.user.email}</div>
}
```

### API Routes

```typescript
import { withAuth } from "@logickernel/bridge/next"
import { NextResponse } from "next/server"

// Any authenticated user
export const GET = withAuth(async (request, { session }) => {
  return NextResponse.json({
    userId: session.user.id,
    email: session.user.email,
  })
})

// With role requirements
export const DELETE = withAuth(
  async (request, { session }) => {
    // Only owners can delete
    return NextResponse.json({ deleted: true })
  },
  {
    requiredRoles: ["organization.owner"],
    organizationId: "org_id", // Extract from route params
  }
)
```

### Middleware / Proxy

```typescript
// middleware.ts or proxy.ts
import { createProxyHandler, type ProxyOptions } from "@logickernel/bridge/next"

const config: ProxyOptions = {
  basePath: "/app",
  routes: {
    "/dashboard": [],  // Any authenticated user (relative to basePath)
    "/settings": [],
    "/[organization_id]/admin": ["organization.owner"],  // Dynamic route with role requirement
  },
}

export const proxy = createProxyHandler(config)

// Static config for Next.js middleware
export const config = {
  matcher: ["/((?!_next/static|_next/image|favicon.ico|public).*)"],
}
```

## Import Paths

```typescript
// Core utilities (framework-agnostic)
import { decodeSessionToken, fetchUserRoles } from "@logickernel/bridge"

// Next.js utilities (server components, API routes, middleware)
import { getSession, withAuth, checkPageAuth } from "@logickernel/bridge/next"

// Layout components (client components)
import { AppLayout, type User } from "@logickernel/bridge/next/components"
```

## API Reference

### Core Exports (`@logickernel/bridge`)

| Export | Description |
|--------|-------------|
| `decodeSessionToken(token, secret, isSecure)` | Decode and validate a JWT session token |
| `extractSessionCookie(cookies)` | Extract session cookie from a cookies object |
| `fetchUserRoles(options)` | Fetch user roles from the kernel API |
| `hasAnyRole(userRoles, requiredRoles)` | Check if user has at least one required role |
| `hasAllRoles(userRoles, requiredRoles)` | Check if user has all required roles |
| `hasRole(userRoles, role)` | Check if user has a specific role |

### Next.js Exports (`@logickernel/bridge/next`)

| Export | Description |
|--------|-------------|
| `getSession()` | Get the current session from cookies |
| `getSessionToken()` | Get the raw session token value |
| `getUserRoles(orgId)` | Get user roles in an organization |
| `withAuth(handler, options?)` | Wrap an API route with authentication |
| `checkPageAuth(options?)` | Check authentication for a page |
| `requireAuth()` | Require authentication (throws if not authenticated) |
| `hasRequiredRole(orgId, roles)` | Check if user has required roles |
| `createProxyHandler(options)` | Create a middleware/proxy handler |

### Layout Components (`@logickernel/bridge/next/components`)

| Export | Description |
|--------|-------------|
| `AppLayout` | Main layout component with sidebar navigation (wrap in a local component with `"use client"` for server components) |
| `useNavigation` | Hook to fetch navigation items from API |
| `getIconComponent` | Utility to get icon component from icon name |

**AppLayout Props:**

```typescript
interface AppLayoutProps {
  user?: User                    // Optional: Auto-fetched if not provided
  organizationId?: string        // Optional: Current organization ID
  apiBaseUrl?: string            // Optional: Defaults to "/core/api"
  children: React.ReactNode
}
```

## TypeScript Types

```typescript
// Core types
import type {
  Session,
  SessionUser,
  DecodeResult,
} from "@logickernel/bridge"

// Next.js types
import type {
  ProxyOptions,
  AuthContext,
  AuthenticatedHandler,
  PageAuthOptions,
  PageAuthResult,
} from "@logickernel/bridge/next"

// Component types
import type {
  User,
  NavigationItem,
  NavigationOrganization,
} from "@logickernel/bridge/next/components"
```

## Architecture

```
bridge/
├── src/
│   ├── index.ts              # Core exports (framework-agnostic)
│   ├── jwt.ts                # JWT decoding utilities
│   ├── roles.ts              # Role checking utilities
│   └── next/
│       ├── index.ts          # Next.js adapter exports
│       ├── session.ts        # Session utilities
│       ├── api.ts            # API route utilities
│       ├── page.ts           # Page utilities
│       ├── proxy.ts          # Middleware/proxy utilities
│       └── components/
│           ├── app-layout.tsx     # Main layout component
│           ├── app-sidebar.tsx    # Sidebar component
│           ├── nav-main.tsx       # Navigation component
│           ├── nav-user.tsx       # User menu component
│           ├── team-switcher.tsx  # Organization switcher
│           ├── use-navigation.ts  # Navigation hook
│           └── ui/                # Internal UI components (shadcn)
```

## Development

```bash
# Install dependencies
npm install

# Build the library
npm run build

# Watch mode for development
npm run dev

# Type checking
npm run typecheck

# Clean build artifacts
npm run clean
```

## License

MIT