Avnology ID
SDKsTypeScript SDKGuides

Next.js Integration

Integrate Avnology ID with Next.js App Router using server components, middleware, and route handlers.

Next.js Integration

This guide covers Next.js 15 App Router integration with server-side session validation, middleware-based auth, and API route protection.

Setup

npm install @avnology/sdk-typescript

Server-side client

// lib/avnology-server.ts
import { AvnologyId } from "@avnology/sdk-typescript";

export function createServerClient() {
  return new AvnologyId({
    baseUrl: process.env.AVNOLOGY_BASE_URL!,
    clientId: process.env.AVNOLOGY_CLIENT_ID!,
    clientSecret: process.env.AVNOLOGY_CLIENT_SECRET!,
  });
}

Browser client

// lib/avnology-browser.ts
"use client";
import { AvnologyId } from "@avnology/sdk-typescript";

export const avnology = new AvnologyId({
  baseUrl: process.env.NEXT_PUBLIC_AVNOLOGY_BASE_URL!,
  clientId: process.env.NEXT_PUBLIC_AVNOLOGY_CLIENT_ID!,
  credentials: "include",
});

Middleware authentication

Protect routes at the edge with Next.js middleware.

// middleware.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";

const protectedPaths = ["/dashboard", "/settings", "/admin"];
const publicPaths = ["/login", "/register", "/callback"];

export async function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl;

  if (publicPaths.some((p) => pathname.startsWith(p))) {
    return NextResponse.next();
  }

  if (protectedPaths.some((p) => pathname.startsWith(p))) {
    const sessionCookie = request.cookies.get("avnology_session");
    if (!sessionCookie) {
      return NextResponse.redirect(new URL("/login", request.url));
    }
  }

  return NextResponse.next();
}

export const config = {
  matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
};

Server components

// app/dashboard/page.tsx
import { createServerClient } from "@/lib/avnology-server";
import { cookies } from "next/headers";
import { redirect } from "next/navigation";

export default async function DashboardPage() {
  const client = createServerClient();

  // Pass session cookie to the server client
  const cookieStore = await cookies();
  const sessionToken = cookieStore.get("avnology_session")?.value;

  if (!sessionToken) {
    redirect("/login");
  }

  const session = await client.getSession();
  if (!session) {
    redirect("/login");
  }

  return (
    <div>
      <h1>Dashboard</h1>
      <p>Welcome, {session.identity.name?.first}</p>
    </div>
  );
}

OAuth callback route

// app/callback/route.ts
import { createServerClient } from "@/lib/avnology-server";
import { cookies } from "next/headers";
import { redirect } from "next/navigation";

export async function GET(request: Request) {
  const url = new URL(request.url);
  const code = url.searchParams.get("code");
  const state = url.searchParams.get("state");

  if (!code) {
    redirect("/login?error=missing_code");
  }

  const client = createServerClient();

  const cookieStore = await cookies();
  const codeVerifier = cookieStore.get("pkce_code_verifier")?.value;

  const tokens = await client.oauth.exchangeCode({
    code,
    codeVerifier: codeVerifier!,
    redirectUri: `${url.origin}/callback`,
  });

  // Set session cookie (httpOnly, secure)
  cookieStore.set("avnology_session", tokens.accessToken, {
    httpOnly: true,
    secure: true,
    sameSite: "lax",
    maxAge: tokens.expiresIn,
    path: "/",
  });

  redirect("/dashboard");
}

API route protection

// app/api/users/route.ts
import { createServerClient } from "@/lib/avnology-server";
import { NextResponse } from "next/server";

export async function GET(request: Request) {
  const authHeader = request.headers.get("authorization");
  if (!authHeader?.startsWith("Bearer ")) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  const client = createServerClient();
  const result = await client.oauth.introspectToken({
    token: authHeader.slice(7),
  });

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

  const users = await client.admin.listUsers({ pageSize: 25 });
  return NextResponse.json(users);
}

See also

On this page