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
The API key is used to derive two keys via HKDF: one for AES-256-GCM session encryption and one for HMAC-SHA256 OAuth state signing. A single key, two domain-separated derived keys.
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.
If you see [AuthGate] ✗ Callback URL not registered in your console, go to Dashboard → Callback URLs and add {YOUR_APP_URL}/api/auth/callback.
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.
| URL | Method | Action |
|---|---|---|
/api/auth/{provider}/login | GET | Redirect to OAuth provider |
/api/auth/callback | GET | Handle OAuth callback |
/api/auth/email/signup | POST | Email registration |
/api/auth/email/signin | POST | Email sign-in |
/api/auth/email/forgot-password | POST | Request password reset |
/api/auth/email/reset-password | POST | Confirm password reset |
/api/auth/email/verify-code | POST | Verify email with OTP code |
/api/auth/sms/send-code | POST | Send SMS verification code |
/api/auth/sms/verify-code | POST | Verify SMS code and sign in |
/api/auth/me | GET | Get current session |
/api/auth/logout | POST | Clear session |
/api/auth/mfa/verify | POST | Verify MFA challenge |
/api/auth/refresh | POST | Refresh 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
nullif the path doesn't match or the user is authenticated (lets your chain continue) - Returns a redirect
NextResponseif 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*"],
};
Next.js v16 is transitioning from middleware to proxy. The layout-level check works regardless of this transition. Middleware/proxy is an optional optimization layer you can add or remove without affecting auth correctness.
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:
| Field | Type | Description |
|---|---|---|
id | string | Unique user ID |
email | string | null | User's email |
phone | string | null | User's phone number (E.164) |
name | string | null | Display name |
picture | string | null | Avatar URL |
provider | string | Auth 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,securein 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>.