Skip to main content
CoreSDK
Guides

Next.js Integration

Add CoreSDK to a Next.js App Router project — middleware, protected API routes, and server components with user context.

Phase note. The TypeScript/Next.js SDK ships in Phase 2. The API shown is the planned surface.

Phase 2 — TypeScript SDK. The @coresdk/sdk package is not yet available. It ships in Phase 2. The code below reflects the planned API.

Next.js Integration

This guide covers integrating CoreSDK into a Next.js project using the App Router. You will configure middleware.ts to authenticate every request, protect API routes, and read user context from server components.

1. Install

npm install @coresdk/sdk
# Phase 2 — not yet published

2. Initialize the SDK

Create a shared SDK instance that is reused across your server-side code.

// lib/sdk.ts
import { CoreSDK } from "@coresdk/sdk";

export const sdk = new CoreSDK({ tenant: "acme-corp" })  // Phase 2 planned API
  .tenant(process.env.CORESDK_TENANT!)
  .jwksUrl(process.env.CORESDK_JWKS_URL!)
  .jwtAudience(process.env.CORESDK_AUDIENCE!)
  .jwtIssuer(process.env.CORESDK_ISSUER!)
  .build();

Add the required environment variables to .env.local:

CORESDK_TENANT=acme
CORESDK_JWKS_URL=https://your-idp.com/.well-known/jwks.json
CORESDK_AUDIENCE=https://api.acme.com
CORESDK_ISSUER=https://your-idp.com/

3. Middleware

middleware.ts runs on every matched request before it reaches your route handlers or server components. Verify the bearer token here and forward the resolved user identity as a request header so downstream code does not need to re-verify.

// middleware.ts
import { NextRequest, NextResponse } from "next/server";
import { sdk } from "@/lib/sdk";

export async function middleware(req: NextRequest) {
  const authHeader = req.headers.get("authorization") ?? "";
  const token = authHeader.startsWith("Bearer ")
    ? authHeader.slice(7)
    : req.cookies.get("access_token")?.value;

  if (!token) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  const result = await sdk.verifyToken(token);

  if (!result.ok) {
    return NextResponse.json(
      { error: result.error.title, trace_id: result.error.traceId },
      { status: result.error.status }
    );
  }

  // Forward verified user identity to route handlers and server components
  const requestHeaders = new Headers(req.headers);
  requestHeaders.set("x-user-id", result.user.id);
  requestHeaders.set("x-user-email", result.user.email ?? "");
  requestHeaders.set("x-user-role", result.user.role ?? "");

  return NextResponse.next({ request: { headers: requestHeaders } });
}

export const config = {
  matcher: ["/api/:path*", "/dashboard/:path*"],
};

4. Protecting API Routes

Read the forwarded headers set by middleware. Because the token was already verified in middleware, no second verification is needed.

// app/api/documents/route.ts
import { NextRequest, NextResponse } from "next/server";
import { sdk } from "@/lib/sdk";

export async function GET(req: NextRequest) {
  const userId = req.headers.get("x-user-id")!;
  const userRole = req.headers.get("x-user-role")!;

  // Check a policy before proceeding
  const allowed = await sdk.policy.check({
    principal: { id: userId, role: userRole },
    action: "documents:read",
    resource: { type: "document", tenantId: req.headers.get("x-tenant-id")! },
  });

  if (!allowed) {
    return NextResponse.json({ error: "Forbidden" }, { status: 403 });
  }

  const documents = await fetchDocuments(userId);
  return NextResponse.json({ documents });
}

export async function POST(req: NextRequest) {
  const userId = req.headers.get("x-user-id")!;
  const body = await req.json();

  const allowed = await sdk.policy.check({
    principal: { id: userId },
    action: "documents:create",
    resource: { type: "document" },
  });

  if (!allowed) {
    return NextResponse.json({ error: "Forbidden" }, { status: 403 });
  }

  const doc = await createDocument({ ...body, ownerId: userId });
  return NextResponse.json({ document: doc }, { status: 201 });
}

5. Protecting Pages (Server Components)

In server components, read user context from the incoming request headers via next/headers.

// app/dashboard/page.tsx
import { headers } from "next/headers";
import { redirect } from "next/navigation";

export default async function DashboardPage() {
  const headerStore = await headers();
  const userId = headerStore.get("x-user-id");

  // Middleware guarantees this is set for /dashboard/* routes,
  // but guard defensively in case the matcher is changed.
  if (!userId) {
    redirect("/login");
  }

  const userEmail = headerStore.get("x-user-email") ?? "";
  const userRole = headerStore.get("x-user-role") ?? "viewer";

  return (
    <main>
      <h1>Dashboard</h1>
      <p>Signed in as {userEmail}</p>
      {userRole === "admin" && <AdminPanel />}
    </main>
  );
}

6. TypeScript Types

CoreSDK exports types for the verified user and policy check inputs.

import type { VerifiedUser, PolicyCheckInput, PolicyCheckResult } from "@coresdk/sdk";

// Extend the verified user with your own custom claims
interface AppUser extends VerifiedUser {
  orgId: string;
  plan: "free" | "pro" | "enterprise";
}

// Helper to read user from request headers in API routes
export function getUserFromHeaders(req: NextRequest): VerifiedUser {
  return {
    id: req.headers.get("x-user-id")!,
    email: req.headers.get("x-user-email") ?? undefined,
    role: req.headers.get("x-user-role") ?? undefined,
  };
}

Summary

LayerWhat CoreSDK does
middleware.tsVerifies JWT once per request, forwards identity as headers
API routeReads identity from headers, runs policy checks
Server componentReads identity from headers, renders conditional UI
TypeScriptVerifiedUser, PolicyCheckInput, PolicyCheckResult

On this page