NIN Connect Docs
Platform (Self-Host)Security

Input Validation

Zod-based request validation, error sanitization, and redirect URL validation across all API routes.

Overview

Every API endpoint validates its input using Zod schemas before any business logic executes. Invalid requests are rejected immediately with a descriptive error in development and a generic error in production.

Validation Pattern

All route handlers follow the same pattern:

import { z } from "zod";

const schema = z.object({
  name: z.string().min(1).max(100),
  slug: z.string().regex(/^[a-z0-9-]+$/),
  baseUrl: z.string().url(),
});

export const POST = async (req: NextRequest) => {
  const body = await req.json();
  const parsed = schema.safeParse(body);

  if (!parsed.success) {
    return NextResponse.json(
      { error: "Invalid input", details: parsed.error.flatten() },
      { status: 400 }
    );
  }

  // Business logic uses parsed.data (typed and validated)
};

By using safeParse instead of parse, routes return structured errors instead of throwing exceptions.

Validated Endpoints

EndpointValidated Fields
POST /api/dashboard/pluginsname, slug, baseUrl, description, logoUrl, category, toolSource
PUT /api/dashboard/plugins/[pluginId]name, slug, baseUrl, description, logoUrl, category, toolSource
POST /api/dashboard/api-keysname
DELETE /api/dashboard/api-keyskeyId
PUT /api/dashboard/plugins/[pluginId]/auth-methodslabel, type, config, isDefault, authMethodId
DELETE /api/dashboard/plugins/[pluginId]/auth-methodsauthMethodId
PUT /api/dashboard/plugins/[pluginId]/toolsname, description, method, path, parameters, toolId
DELETE /api/dashboard/plugins/[pluginId]/toolstoolId
POST /api/connect/submitdeveloperId, userId, pluginSlug, authMethodId, credentials, connectToken
POST /api/connect/oauth-startpluginSlug, authMethodId, connectToken, redirectUrl aliases, mcpClientSecret
POST /api/v1/connections/initiatepluginSlug, userId, connectionType, authMethodId, redirectUrl aliases
DELETE /api/v1/connectionsuserId, plugin, connectionId
DELETE /api/dashboard/connectionsconnectionId

Error Sanitization

In production, error responses are sanitized to prevent leaking internal details:

export const safeErrorResponse = (
  error: unknown,
  fallbackMessage: string = "Internal server error",
  status: number = 500
): NextResponse => {
  if (process.env.NODE_ENV === "production") {
    return NextResponse.json({ error: fallbackMessage }, { status });
  }

  // In development, include full error details for debugging
  const message = error instanceof Error ? error.message : String(error);
  return NextResponse.json({ error: message }, { status });
};

This prevents stack traces, SQL queries, and internal error messages from reaching clients in production.

Redirect URL Validation

All endpoints that accept a final return URL validate it before use. The preferred field is redirectUrl; returnUrl, return_url, callbackUrl, and callback_url are accepted as aliases. If the final return URL is missing, NIN can fall back to X-NIN-Redirect-URL, X-Return-URL, Referer, then Origin.

Strict Mode (Dashboard Flows)

Used for dashboard-initiated connections where the redirect must go back to the same origin:

  • Protocol must be http: or https:
  • Origin must match NEXT_PUBLIC_APP_URL

Relaxed Mode (SDK Flows)

Used for SDK-initiated connections where the developer controls the redirect destination:

  • Protocol must be http: or https:
  • In production, protocol must be https: (no plaintext redirects)
  • In development, http://localhost is allowed
  • If a request accidentally sends the NIN app origin as the final return URL and a valid requester URL is present, the requester URL is used instead

OAuth Callback Redirects

The OAuth callback endpoint (/api/v1/oauth/callback) validates the final return URL before issuing a redirect. Invalid URLs return a 400 response instead of following the potentially malicious URL.

On this page