# How to Implement Custom Authentication in OmniRoute: A Complete Developer Guide

> Learn how to implement custom authentication in OmniRoute by extending extractApiKey and adding database validation. This developer guide shows you how.

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

---

**OmniRoute centralizes all authentication logic in [`src/sse/services/auth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/sse/services/auth.ts), allowing you to implement custom schemes by extending the `extractApiKey()` function and optionally adding database-backed validation in [`src/lib/db/apiKeys.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/apiKeys.ts).**

OmniRoute provides a unified authentication pipeline that handles API keys, OAuth tokens, and custom credentials through a single service layer. Understanding how to implement custom authentication in OmniRoute enables you to integrate proprietary token formats, legacy auth systems, or header-based schemes without modifying downstream executors or route handlers.

## Understanding OmniRoute's Authentication Architecture

### The Central Service Layer

All authentication flows in OmniRoute converge on the service layer located at [`src/sse/services/auth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/sse/services/auth.ts). This module exposes `extractApiKey()` (line 3316), which reads incoming requests and extracts credentials from multiple sources: the standard `Authorization` header, `x-api-key`, `x-goog-api-key`, and optional path-scoped tokens. When you implement custom authentication in OmniRoute, you extend this single function to recognize your proprietary headers or token formats.

### The Credential Validation Pipeline

After extraction, the system validates credentials through `isValidApiKey()` in [`src/lib/db/apiKeys.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/apiKeys.ts). This function queries the SQLite-backed `api_keys` table to verify the key exists and has not expired, returning the associated `connectionId`. Valid credentials are then materialized into a `ProviderCredentials` object by `getProviderCredentials()` (line 1233 in [`auth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/auth.ts)), which includes `authHeader`, `authType`, and OAuth session data. This object is consumed uniformly by all executors in `open-sse/executors/*`, API routes like [`src/sse/handlers/chat.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/sse/handlers/chat.ts), and the WebSocket server at [`src/server/ws/liveServer.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/server/ws/liveServer.ts).

## Implementing a Custom Authentication Scheme

### Step 1: Define the Extraction Rule

Create a new helper function in [`src/sse/services/auth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/sse/services/auth.ts) to parse your custom header format. For example, to support a Base64-encoded JSON payload in an `x-custom-token` header:

```typescript
// src/sse/services/auth.ts
export function extractCustomToken(request: AuthRequestLike): string | null {
  const hdr = readHeaderValue(request.headers, "x-custom-token");
  if (!hdr) return null;
  try {
    const decoded = Buffer.from(hdr.trim(), "base64").toString("utf8");
    const payload = JSON.parse(decoded);
    return payload?.token ?? null;
  } catch {
    return null;
  }
}

```

### Step 2: Integrate with the Main Flow

Modify the existing `extractApiKey()` function to include your custom extractor in the fallback chain. Maintain consistency with the existing order: Bearer → `x-api-key` → `x-goog-api-key` → custom:

```typescript
// src/sse/services/auth.ts – inside extractApiKey()
export function extractApiKey(request: AuthRequestLike): string | null {
  // ... existing built-in checks for Authorization, x-api-key, etc.
  
  const custom = extractCustomToken(request);
  if (custom) {
    return custom;  // custom scheme wins
  }
  
  return null;
}

```

Because `clientApiRouteAuth` in [`src/shared/utils/clientApiRouteAuth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/shared/utils/clientApiRouteAuth.ts) and the policy guard in [`src/server/authz/policies/clientApi.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/server/authz/policies/clientApi.ts) both call `extractApiKey()`, your new scheme automatically protects all API routes without additional configuration.

### Step 3: Persist Custom Tokens (Optional)

If your custom tokens require database persistence, extend [`src/lib/db/apiKeys.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/apiKeys.ts) to support a new table. Add a migration creating a `custom_tokens` table, then implement the lookup function:

```typescript
// src/lib/db/apiKeys.ts
export async function getCustomTokenInfo(token: string) {
  const db = getDbInstance();
  return db.get<{ connectionId: string }>(
    `SELECT connectionId FROM custom_tokens WHERE token = ?`, 
    token
  );
}

```

### Step 4: Validate Against the Database

Update the validation logic to branch for custom tokens. You can either extend `isValidApiKey()` or create a parallel `isValidCustomToken()` function that checks both the standard `api_keys` table and your new `custom_tokens` table:

```typescript
// src/sse/services/auth.ts
export async function isValidApiKey(key: string) {
  // Check standard API keys first
  const standard = await lookupStandardKey(key);
  if (standard) return standard;
  
  // Fallback to custom token validation
  return await getCustomTokenInfo(key);
}

```

## Testing Your Custom Implementation

### cURL Requests with Custom Headers

Test your implementation by sending a Base64-encoded JSON payload:

```bash
curl -H "x-custom-token: $(echo -n '{"token":"abc123"}' | base64)" \
     http://localhost:20128/v1/chat/completions

```

### JavaScript Client Implementation

For browser or Node.js clients, encode the payload before sending:

```javascript
const payload = JSON.stringify({ token: "abc123" });
const encoded = btoa(payload);

await fetch("http://localhost:20128/v1/chat/completions", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "x-custom-token": encoded,
  },
  body: JSON.stringify({ model: "gpt-4", messages: [...] })
});

```

### Server-Side Verification

When building custom handlers, use the same authentication functions to verify requests:

```typescript
import { extractApiKey, isValidApiKey } from "@/sse/services/auth";

export async function handler(request: Request) {
  const rawKey = extractApiKey(request);
  if (!rawKey) return new Response("Unauthorized", { status: 401 });

  const cred = await isValidApiKey(rawKey);
  if (!cred) return new Response("Invalid token", { status: 403 });

  // cred.connectionId can now be passed to the executor
  return new Response(JSON.stringify({ connectionId: cred.connectionId }));
}

```

## Key Files and Extension Points

Understanding these core files ensures your custom authentication integrates seamlessly with OmniRoute's security model:

- **[`src/sse/services/auth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/sse/services/auth.ts)** – Central credential extraction, validation, and materialization (contains `extractApiKey` at line 3316 and `getProviderCredentials` at line 1233)

- **[`src/lib/db/apiKeys.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/apiKeys.ts)** – SQLite-backed API key lookup used by `isValidApiKey()`; extend this to support custom token tables

- **[`src/sse/handlers/chat.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/sse/handlers/chat.ts)** – Example API route demonstrating how routes import `extractApiKey` and abort with 401 on failed authentication

- **[`src/server/authz/policies/clientApi.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/server/authz/policies/clientApi.ts)** – Middleware enforcing client-API authentication across all routes; automatically inherits changes to `extractApiKey`

- **[`src/server/ws/liveServer.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/server/ws/liveServer.ts)** – WebSocket server utilizing `loadAuthModule()` to authenticate handshake requests using the same service layer

- **[`src/shared/utils/clientApiRouteAuth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/shared/utils/clientApiRouteAuth.ts)** – Higher-level wrapper used by route handlers to standardize authentication checks

## Summary

- **OmniRoute uses a single service layer** ([`src/sse/services/auth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/sse/services/auth.ts)) for all authentication, making custom schemes manageable by modifying one file.

- **Extend `extractApiKey()`** to recognize custom headers or token formats, placing your logic after built-in checks for `Authorization`, `x-api-key`, and `x-goog-api-key`.

- **Validate custom tokens** by extending [`src/lib/db/apiKeys.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/apiKeys.ts) with new tables and lookup functions, then integrate these into `isValidApiKey()` or parallel validation functions.

- **Automatic propagation** occurs because all API routes, WebSocket handlers, and SDKs consume the same `extractApiKey()` function and `ProviderCredentials` object.

- **Zero downstream changes** are required; executors in `open-sse/executors/*` receive the normalized credential object regardless of the extraction method.

## Frequently Asked Questions

### Where is the authentication logic centralized in OmniRoute?

All authentication logic is centralized in [`src/sse/services/auth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/sse/services/auth.ts). This file contains `extractApiKey()` for credential extraction and `getProviderCredentials()` for materializing the `ProviderCredentials` object that downstream components consume. According to the OmniRoute source code, both API routes and WebSocket connections import from this single module, ensuring consistent authentication behavior across the entire application.

### Can I use custom authentication with WebSocket connections?

Yes. The WebSocket server at [`src/server/ws/liveServer.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/server/ws/liveServer.ts) loads the authentication module via `loadAuthModule()` and extracts keys during the initial handshake using the same `extractApiKey()` function. Because WebSocket authentication reuses the service layer in [`src/sse/services/auth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/sse/services/auth.ts), any custom extractor you add becomes immediately available to WebSocket clients without modifying the live server code.

### How do I store custom tokens in the database?

Add a migration to create a new table (e.g., `custom_tokens`) in the SQLite database, then implement a lookup function in [`src/lib/db/apiKeys.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/apiKeys.ts). Reference this function from your validation logic in [`src/sse/services/auth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/sse/services/auth.ts). The existing `api_keys` table schema provides a template for implementing expiration checks and connection ID mappings for your custom tokens.

### Is it possible to disable built-in authentication headers?

While you can modify `extractApiKey()` to skip built-in checks, OmniRoute does not provide a configuration flag to disable standard headers like `Authorization` or `x-api-key`. To prioritize your custom scheme, place your extraction logic at the beginning of the function's fallback chain. If you require conditional enabling based on settings, add a configuration entry in [`src/lib/resilience/settings.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/resilience/settings.ts) and check this setting inside `extractApiKey()` before processing standard headers.