Creator
Authentication

Auth Guards

How to protect API routes and pages with authentication and authorization guards.

API guards

requireApiAuth()

Require authentication for an API route. Returns { session, userId } on success, or { error } (401 response) on failure.

File: lib/auth/api-guard.ts

import { requireApiAuth } from "@/lib/auth/api-guard";

export async function GET(request: NextRequest) {
  const { userId, error } = await requireApiAuth();
  if (error) return error;

  // userId is available here
}

requireApiAdmin()

Require admin role. Returns 401 if not authenticated, 403 if not admin.

import { requireApiAdmin } from "@/lib/auth/api-guard";

export async function GET(request: NextRequest) {
  const { session, userId, error } = await requireApiAdmin();
  if (error) return error;

  // Only admins reach here
}

Subscription guards

File: lib/auth/subscription-guard.ts

requireSubscription()

Gate an API route to users with an active subscription.

import { requireSubscription } from "@/lib/auth/subscription-guard";

export async function GET(request: NextRequest) {
  const { authorized, response, userId } = await requireSubscription();
  if (!authorized) return response;

  // User has an active subscription
}

requirePlan(planId)

Require a specific plan.

const { authorized, response, currentPlan } = await requirePlan("starter");
if (!authorized) return response;

requireLimit(key, currentUsage)

Check a plan's limit against current usage. The limit value comes from billing.config.ts — use -1 for unlimited.

const { authorized, response } = await requireLimit("requestsPerDay", currentCount);
if (!authorized) return response;

getSubscriptionStatus()

Non-blocking check — returns subscription info without blocking the request.

const { isAuthenticated, hasActiveSubscription, plan, status, userId } =
  await getSubscriptionStatus();

Page guards

File: lib/auth/utils.ts

getCurrentUser()

Get the current user on the server side. Returns null if not logged in.

import { getCurrentUser } from "@/lib/auth/utils";

export default async function Page() {
  const user = await getCurrentUser();
  if (!user) redirect("/auth/login");
  // ...
}

requireAuth()

Redirects to /auth/login if not authenticated. Returns the session.

import { requireAuth } from "@/lib/auth/utils";

export default async function ProtectedPage() {
  const session = await requireAuth();
  // session.user is guaranteed to exist
}

requireAdmin()

Redirects to /auth/login if not authenticated, then redirects to / if not admin.

import { requireAdmin } from "@/lib/auth/utils";

export default async function AdminPage() {
  const session = await requireAdmin();
  // session.user.role === "admin"
}

How route protection works

The primary layer of protection is proxy.ts (Next.js middleware). Every route is protected by default — only routes in the publicRoutes array (config/public-routes.config.ts) are accessible without authentication. Unauthenticated users are redirected to /auth/login.

The protected layout at app/(protected)/layout.tsx adds a second layer by fetching the user for the sidebar UI. API routes use requireApiAuth() and requireApiAdmin() as their own protection layer, returning error responses instead of redirecting.

On this page

We use cookies to ensure you get the best experience on our website. For more information on how we use cookies, please see our cookie policy.