# How OmniRoute's Authorization Pipeline Classifies and Enforces PUBLIC, CLIENT_API, and MANAGEMENT Routes

> Discover how OmniRoute's authorization pipeline classifies and enforces PUBLIC, CLIENT_API, and MANAGEMENT routes. Learn about its centralized system and dedicated policy handlers for robust security.

- Repository: [Diego Rodrigues de Sa e Souza/OmniRoute](https://github.com/diegosouzapw/OmniRoute)
- Tags: how-to-guide
- Published: 2026-07-25

---

**OmniRoute's authorization pipeline uses a centralized classification system in [`src/server/authz/classify.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/server/authz/classify.ts) to categorize every incoming request as PUBLIC, CLIENT_API, or MANAGEMENT, then dispatches the request to dedicated policy handlers in `src/server/authz/policies/` that enforce specific authentication requirements based on the route classification.**

The diegosouzapw/OmniRoute repository implements a robust security layer that separates route intent from authentication logic. Understanding how the authorization pipeline classifies and enforces route security is essential for developers deploying or extending this OpenAI-compatible API gateway. This article examines the complete flow from path normalization to policy enforcement, referencing actual source files and implementation details.

## Route Classification Logic

The classification engine lives in [`src/server/authz/classify.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/server/authz/classify.ts) and exports the `classifyRoute` function. This module normalizes incoming paths and maps them to one of three security classes using a priority-ordered decision tree.

### Path Normalization and Priority Rules

The `classifyRoute` function first normalizes the request path by stripping trailing slashes, adding leading slashes, and expanding legacy aliases such as `/v1`, `/v1beta`, and `/codex`. This guarantees a canonical pathname for consistent matching.

The classification logic then evaluates the following rules in order:

- **Root path (`/`)** → Redirects to dashboard, classified as **MANAGEMENT** (`reason: "root_redirect"`)
- **Dashboard pages (`/dashboard…`)** → All UI routes are **MANAGEMENT** (`reason: "dashboard_prefix"`)
- **Onboarding wizard (`/dashboard/onboarding`)** → **PUBLIC** (`reason: "setup_wizard"`)
- **Connect pages (`/connect…`)** → Public device-flow login links, **PUBLIC** (`reason: "public_connect_page"`)
- **Client-API versioned prefixes (`/api/v1…`, `/api/v1beta…`)** → **CLIENT_API** (`reason: "client_api_v1"` or alias reason)
- **Other `/api/…` routes** → Calls `isPublicApiRoute` from [`src/shared/constants/publicApiRoutes.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/shared/constants/publicApiRoutes.ts) to check against `PUBLIC_READONLY_API_ROUTE_PREFIXES` and `PUBLIC_READONLY_METHODS`. Matches return **PUBLIC**; otherwise **MANAGEMENT**
- **Fallback** → Everything else defaults to **MANAGEMENT** (`reason: "fallback_management"`)

The function returns a `RouteClassification` object:

```ts
{
  routeClass: "PUBLIC" | "CLIENT_API" | "MANAGEMENT",
  reason: string,
  normalizedPath: string,
}

```

The classification reason enables debugging and allows downstream policies to make context-aware decisions.

## Policy Dispatch and Enforcement

After classification, the pipeline in [`src/server/authz/pipeline.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/server/authz/pipeline.ts) loads the corresponding policy from `src/server/authz/policies/` based on the `routeClass` value. Each policy implements the `RoutePolicy` interface and returns an `AuthOutcome` using helper functions `allow()` and `reject()` from [`src/server/authz/context.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/server/authz/context.ts).

### PUBLIC Policy (public.ts)

The [`public.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/public.ts) policy requires no authentication. It allows optional JWT validation for CSRF protection on dashboard actions but imposes no mandatory identity checks.

### CLIENT_API Policy (clientApi.ts)

The [`clientApi.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/clientApi.ts) policy handles OpenAI-compatible endpoints with multiple authentication vectors:

1. Extracts bearer tokens from the `Authorization` header
2. Checks for `x-api-key` headers
3. Searches for URL-embedded API keys via `extractApiKey`
4. Falls back to WebSocket handshake detection for anonymous metadata
5. Validates dashboard sessions via `isDashboardSessionAuthenticated` from [`src/shared/utils/apiAuth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/shared/utils/apiAuth.ts)

If no credentials are present and the `REQUIRE_API_KEY` flag (from [`src/shared/utils/featureFlags.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/shared/utils/featureFlags.ts)) is enabled, the policy returns a `401 AUTH_002` error. When disabled, anonymous traffic proceeds. Validated keys yield an identity of type `client_api_key` via `validateApiKey` from [`src/lib/db/apiKeys.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/apiKeys.ts).

### MANAGEMENT Policy (management.ts)

The strictest policy in [`management.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/management.ts) requires either a valid API key or an active dashboard session. It also respects the `OMNIROUTE_MGMT_TOKEN` header for internal service authentication.

## The Authorization Pipeline Orchestration

The main entry point in [`src/server/authz/pipeline.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/server/authz/pipeline.ts) orchestrates the complete flow through three high-level steps:

1. **Generate a request ID** for distributed tracing and audit logging
2. **Classify the route** using `classifyRoute`
3. **Execute the selected policy** (`clientApiPolicy`, `publicPolicy`, or `managementPolicy`)

If the policy returns `allow`, the request proceeds to the route handler. If it returns `reject`, the pipeline returns the prescribed HTTP status and error code.

The pipeline also enforces auxiliary guards:

- **Body-size limits** for payload protection
- **CSRF validation** for dashboard POST/PUT/PATCH/DELETE requests
- **Peer-IP header sanitization** by stripping `PEER_IP_HEADER` before downstream handling

## Practical Implementation Examples

### Manual Route Classification

```ts
import { classifyRoute } from "@/server/authz/classify";

const info = classifyRoute("/v1/models", "GET");
console.log(info);
// → { routeClass: "CLIENT_API", reason: "client_api_alias", normalizedPath: "/api/v1/models" }

```

### Integrating the Pipeline in Next.js Routes

```ts
import { runAuthzPipeline } from "@/server/authz/pipeline";

export async function GET(req: Request) {
  const { outcome, context } = await runAuthzPipeline(req);
  if (outcome.kind === "reject") {
    return new Response(outcome.body, { status: outcome.status });
  }

  // Authenticated – proceed to handler
  return handleChatCompletions(req, context);
}

```

### Accessing Authenticated Identity

```ts
if (context.auth.kind === "client_api_key") {
  console.log("API key ID:", context.auth.id); // e.g., "key_abcd"
}

```

## Summary

- **Classification occurs in [`src/server/authz/classify.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/server/authz/classify.ts)**, which normalizes paths and assigns one of three security classes based on prefix matching and method checks against `PUBLIC_READONLY_API_ROUTE_PREFIXES`.
- **Policy enforcement is modular**, with separate handlers in `src/server/authz/policies/` for PUBLIC (open access), CLIENT_API (flexible key-based auth), and MANAGEMENT (strict session or key required) routes.
- **The central pipeline** in [`src/server/authz/pipeline.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/server/authz/pipeline.ts) generates trace IDs, runs classification, executes policies, and applies auxiliary security guards like CSRF validation and body-size limits.
- **Authentication outcomes** are standardized through `allow()` and `reject()` helpers in [`src/server/authz/context.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/server/authz/context.ts), ensuring consistent error codes and HTTP responses across all route types.

## Frequently Asked Questions

### How does OmniRoute handle anonymous requests to CLIENT_API endpoints?

When the `REQUIRE_API_KEY` feature flag is disabled in [`src/shared/utils/featureFlags.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/shared/utils/featureFlags.ts), the [`clientApi.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/clientApi.ts) policy allows anonymous traffic to proceed without authentication. If the flag is enabled and no valid bearer token, API key, or dashboard session is present, the policy rejects the request with a `401 AUTH_002` error code.

### What distinguishes MANAGEMENT routes from CLIENT_API routes in the classification logic?

MANAGEMENT routes include the root path (`/`), all `/dashboard` prefixes (except the onboarding wizard), and fallback unmatched paths, requiring strict authentication via API keys, dashboard sessions, or the `OMNIROUTE_MGMT_TOKEN` header. CLIENT_API routes are specifically identified by `/api/v1…` or `/api/v1beta…` prefixes and support multiple authentication methods including bearer tokens, x-api-key headers, and URL-embedded keys.

### Where does the pipeline check for public read-only API routes?

The classification logic delegates public API detection to `isPublicApiRoute` in [`src/shared/constants/publicApiRoutes.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/shared/constants/publicApiRoutes.ts), which validates requests against `PUBLIC_READONLY_API_ROUTE_PREFIXES` and `PUBLIC_READONLY_METHODS`. Routes matching these constants receive the PUBLIC classification, allowing access without authentication credentials.

### Can I extend the authorization pipeline with custom policies?

Yes, the architecture supports extension by implementing the `RoutePolicy` interface and adding your policy to the dispatch logic in [`src/server/authz/pipeline.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/server/authz/pipeline.ts). New route classifications can be added to `classifyRoute` in [`src/server/authz/classify.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/server/authz/classify.ts), and corresponding policy files should be placed in `src/server/authz/policies/` following the existing pattern of returning `AuthOutcome` via `allow()` and `reject()` helpers from [`src/server/authz/context.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/server/authz/context.ts).