SDK

The @auth-gate/nextjs SDK reduces auth integration from hundreds of lines of boilerplate down to a handful. It handles encrypted sessions, OAuth state management, token verification, and route protection.

Installation

npm install @auth-gate/nextjs

Environment variables

.env.local

# Server-only
AUTHGATE_API_KEY=your_api_key

# AuthGate configuration (server-side only)
AUTHGATE_URL=https://your-authgate-instance.com

# Public
NEXT_PUBLIC_APP_URL=http://localhost:3000

Configuration validation

The SDK validates your configuration at two levels:

Instant checks (throws ConfigurationError) — If apiKey, baseUrl, or appUrl is missing, empty, or malformed, the SDK throws immediately with a message telling you exactly what's wrong and how to fix it.

Background server check (logs to console) — After initialization, the SDK pings AuthGate to verify your API key is valid and your callback URL is registered. If either check fails, you'll see a clear console.error with a link to the relevant dashboard page. This runs in the background and never blocks your app from starting.

Setup

1. Initialize the SDK

Create a single file that initializes the SDK and exports everything your app needs.

src/lib/auth.ts

import { createAuthGate, createAuthGateMiddleware } from "@auth-gate/nextjs";

const appUrl = process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000";

function getAuth() {
  return createAuthGate({
    apiKey: process.env.AUTHGATE_API_KEY!,
    baseUrl: process.env.AUTHGATE_URL!,
    appUrl,
  });
}

// Lazy singleton — avoids build-time env var errors
let _auth: ReturnType<typeof getAuth> | null = null;
function auth() {
  if (!_auth) _auth = getAuth();
  return _auth;
}

// Route handlers (for catch-all route)
export const handlers = {
  GET: (...args: Parameters<ReturnType<typeof getAuth>["handlers"]["GET"]>) =>
    auth().handlers.GET(...args),
  POST: (...args: Parameters<ReturnType<typeof getAuth>["handlers"]["POST"]>) =>
    auth().handlers.POST(...args),
};

// Session helper (for server components & API routes)
export async function getSession() {
  return auth().session.getSession();
}

// Composable middleware (optional — see "Route protection" below)
export const authMiddleware = createAuthGateMiddleware(
  () => auth().client,
  { matcher: ["/dashboard/:path*"], loginPath: "/login" }
);

2. Create the catch-all route

This single route handles all auth endpoints: OAuth login redirects, callbacks, email sign-in/up, logout, and session queries.

src/app/api/auth/[...authgate]/route.ts

import { handlers } from "@/lib/auth";

export const { GET, POST } = handlers;

The catch-all maps URL segments to handlers automatically. The SDK uses a single callback path (/api/auth/callback) for all providers — make sure to register {YOUR_APP_URL}/api/auth/callback in the Callbacks tab of your project dashboard.

URLMethodAction
/api/auth/{provider}/loginGETRedirect to OAuth provider
/api/auth/callbackGETHandle OAuth callback
/api/auth/email/signupPOSTEmail registration
/api/auth/email/signinPOSTEmail sign-in
/api/auth/email/forgot-passwordPOSTRequest password reset
/api/auth/email/reset-passwordPOSTConfirm password reset
/api/auth/email/verify-codePOSTVerify email with OTP code
/api/auth/sms/send-codePOSTSend SMS verification code
/api/auth/sms/verify-codePOSTVerify SMS code and sign in
/api/auth/meGETGet current session
/api/auth/logoutPOSTClear session
/api/auth/mfa/verifyPOSTVerify MFA challenge
/api/auth/refreshPOSTRefresh session token

Route protection

There are two layers of protection, each serving a different purpose. You can use either or both.

Layout-level check (recommended)

This is the primary auth gate. A server-side layout reads the encrypted session cookie and redirects if there's no valid session. It runs during page rendering and is framework-stable — it won't change with Next.js middleware/proxy evolution.

src/app/(protected)/layout.tsx

import { redirect } from "next/navigation";
import { getSession } from "@/lib/auth";

export default async function ProtectedLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  const user = await getSession();
  if (!user) {
    redirect("/login");
  }
  return <>{children}</>;
}

Middleware (optional, composable)

Middleware intercepts requests before rendering starts. This is a performance optimization: it avoids server-rendering a protected page only to redirect away. It's also useful if you have additional middleware concerns (i18n, logging, rate limiting).

createAuthGateMiddleware returns a function that:

  • Returns null if the path doesn't match or the user is authenticated (lets your chain continue)
  • Returns a redirect NextResponse if the user is unauthenticated on a matched path

src/middleware.ts

import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { authMiddleware } from "@/lib/auth";

export default async function middleware(request: NextRequest) {
  // Auth — redirects unauthenticated users, returns null otherwise
  const authResponse = await authMiddleware(request);
  if (authResponse) return authResponse;

  // Chain other middleware here (i18n, logging, etc.)

  return NextResponse.next();
}

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

Composing with other middleware

Since createAuthGateMiddleware returns null for non-matching paths and authenticated users, you can chain it with any other middleware:

src/middleware.ts

import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { authMiddleware } from "@/lib/auth";

export default async function middleware(request: NextRequest) {
  // 1. Auth check
  const authResponse = await authMiddleware(request);
  if (authResponse) return authResponse;

  // 2. Internationalization
  const i18nResponse = handleI18n(request);
  if (i18nResponse) return i18nResponse;

  // 3. Custom headers, logging, etc.
  const response = NextResponse.next();
  response.headers.set("x-request-id", crypto.randomUUID());
  return response;
}

export const config = {
  matcher: ["/dashboard/:path*", "/(en|fr|de)/:path*"],
};

Reading the session

Use getSession() in any server component or API route:

src/app/(protected)/dashboard/page.tsx

import { getSession } from "@/lib/auth";

export default async function DashboardPage() {
  const user = await getSession();

  return <h1>Welcome, {user?.name}</h1>;
}

The session user object contains:

FieldTypeDescription
idstringUnique user ID
emailstring | nullUser's email
phonestring | nullUser's phone number (E.164)
namestring | nullDisplay name
picturestring | nullAvatar URL
providerstringAuth provider used

How sessions work

Sessions are AES-256-GCM encrypted cookies. No JWTs, no external session stores.

  • The API key derives two keys via HKDF (domain-separated)
  • One key encrypts sessions (AES-256-GCM with random 12-byte IV per session)
  • One key signs OAuth state (HMAC-SHA256 with timing-safe comparison)
  • Expiry is embedded inside the encryption envelope (tamper-proof)
  • Default TTL: 7 days
  • Cookie: httpOnly, secure in production, sameSite: lax

API reference

createAuthGate(config)

Creates the SDK instance with route handlers and session helpers.

  • Name
    apiKey
    Type
    string
    Description

    Your AuthGate API key (server-only).

  • Name
    baseUrl
    Type
    string
    Description

    URL of your AuthGate instance.

  • Name
    appUrl
    Type
    string
    Description

    Your application's URL (used to build callback URLs).

Throws ConfigurationError if apiKey, baseUrl, or appUrl is missing, empty, or not a valid URL.

Returns { client, handlers, session }.

createAuthGateMiddleware(client, options?)

Creates a composable middleware function. Accepts either an AuthGateClient instance or a getter function () => AuthGateClient for lazy initialization.

  • Name
    matcher
    Type
    string[]
    Description

    Path patterns to protect. Uses Next.js matcher syntax.

  • Name
    loginPath
    Type
    string
    Description

    Where to redirect unauthenticated users.

Returns (request: NextRequest) => Promise<NextResponse | null>.

Was this page helpful?