Setting Up OAuth Authentication with Upstream Providers in OmniRoute: A Complete Developer Guide

OmniRoute provides built‑in OAuth 2.0 device‑flow and browser‑based authentication for upstream LLM providers, with automatic token refresh, session‑occupancy locking, and proxy‑aware credential management.

Setting up OAuth authentication with upstream providers in OmniRoute lets you securely connect to services like Claude, Gemini, and enterprise SaaS APIs without hardcoding credentials. This guide walks through the architecture, implementation files, and practical code patterns used in production.

How OAuth Works in OmniRoute

The OAuth implementation spans three architectural layers: token lifecycle management, connection selection, and provider configuration. Understanding this flow ensures you can debug issues, extend support for new providers, or customize authentication behavior.

Token Refresh and Persistence

The src/sse/services/tokenRefresh.ts module centralizes all OAuth token operations. It handles:

  • Automatic refresh detection via TOKEN_EXPIRY_BUFFER_MS
  • Per‑connection proxy resolution through resolveProxyForConnection
  • Atomic database persistence using updateProviderConnection

When a stored token nears expiration, the refresh flow exchanges the refresh token for a new access token and updates the provider_connections table without race conditions.

Connection Selection and Session Management

The src/sse/services/auth.ts module selects the appropriate connection for each request:

  1. Checks session‑affinity pins to maintain consistent provider routing
  2. Validates quota‑policy limits and rate‑limit cooldowns
  3. Returns a credentials object with an attached releaseOAuthSession callback

If no OAuth connection is available for no‑auth providers, the module synthesizes a synthetic connection transparently.

Provider Registry

All OAuth‑first providers are declared in src/shared/constants/providers/oauth.ts. This file defines:

  • Human‑readable provider names for CLI and UI display
  • Provider‑specific quirks such as extra OAuth scopes or required client IDs
  • UI hints that drive the dashboard configuration experience

Step‑by‑Step OAuth Setup

1. Initiate Device‑Flow Authentication

Use the OmniRoute CLI to start OAuth login for any supported provider:


# Start OAuth device flow for Claude

omniroute login claude

# Expected output:

#   Verify at https://login.anthropic.com/device and enter code: ABCD-EFGH

Behind the scenes, the CLI invokes @omniroute/open-sse/services/oauthSessionOccupancy.ts to create a temporary session lock. After user authorization, updateProviderCredentials in tokenRefresh.ts persists the returned tokens to the local database.

2. Configure Connection Routing

OmniRoute supports multiple concurrent connections per provider. The auth.ts module selects connections based on:

  • Priority weight (configured per connection)
  • Quota availability (remaining token budget)
  • Error history (recent failures trigger temporary exclusion)

To pin a specific connection for testing or compliance:

omniroute config set-connection-priority claude-conn-prod 100

3. Handle Token Refresh Automatically

The getAccessToken function in tokenRefresh.ts encapsulates all refresh complexity. Example integration in a custom handler:

import { getAccessToken } from "@omniroute/open-sse/services/tokenRefresh.ts";

const { accessToken, updatedCredentials } = await getAccessToken(
  "gemini",
  {
    connectionId: "g2-production",
    refreshToken: process.env.GEMINI_REFRESH_TOKEN,
    providerSpecificData: { projectId: "my-gcp-project" }
  },
  async (result) => {
    // Persist updates atomically within the per-connection mutex
    await updateProviderCredentials("g2-production", result);
  }
);

The callback ensures credential updates are written to the database before the mutex releases, preventing token reuse conflicts.

Session‑Occupancy Locking for Concurrent Requests

OmniRoute uses pessimistic locking to prevent OAuth token exhaustion when multiple requests target the same provider connection.

Reserve and Release Pattern

The src/sse/handlers/chat.ts handler demonstrates proper session management:

import { wrapResponseWithOAuthSessionRelease } from "@omniroute/open-sse/services/oauthSessionOccupancy.ts";

async function handleChatCore(request, credentials) {
  const releaseOAuthSession = credentials.releaseOAuthSession ?? (() => {});
  
  try {
    const response = await executeUpstreamRequest(request, credentials);
    return wrapResponseWithOAuthSessionRelease(response, releaseOAuthSession);
  } catch (error) {
    releaseOAuthSession(); // Explicit release on error path
    throw error;
  }
}

The finally equivalent via wrapResponseWithOAuthSessionRelease guarantees session release even for streaming responses that throw mid‑stream.

Manual Refresh for Debugging

For troubleshooting or emergency token rotation, call refreshAccessToken directly:

import { refreshAccessToken } from "@omniroute/open-sse/services/tokenRefresh.ts";

const newCredentials = await refreshAccessToken(
  "anthropic",                          // provider slug from oauth.ts
  "refresh_token_abc123...",            // current refresh token
  { connectionId: "claude-prod-us" }    // metadata for logging
);

This helper resolves any per‑connection proxy settings and validates the response against provider‑specific schemas.

Proxy‑Aware OAuth Traffic

Corporate environments often require OAuth traffic to traverse authenticated proxies. Both tokenRefresh.ts and auth.ts integrate resolveProxyForConnection to:

  • Detect proxy configuration from environment variables
  • Apply per‑connection proxy overrides from the database
  • Tunnel HTTPS traffic without leaking credentials to intermediate proxies

No explicit configuration is required—proxy resolution occurs automatically during token refresh and connection selection.

Handling No‑Auth Providers

Not all upstream providers require OAuth. The NOAUTH_PROVIDERS registry in auth.ts enables transparent fallback:

// auth.ts synthesizes credentials for open-source model providers
if (NOAUTH_PROVIDERS.includes(request.provider)) {
  return {
    type: "no-auth",
    connectionId: `synthetic-${request.provider}`,
    releaseOAuthSession: () => {} // No-op for non-OAuth flows
  };
}

This abstraction lets downstream handlers treat OAuth and no‑auth providers identically.

Key Source Files Reference

File Responsibility
src/sse/services/tokenRefresh.ts Token refresh orchestration, proxy resolution, database persistence
src/sse/services/auth.ts Connection selection, quota enforcement, session‑affinity handling
src/shared/constants/providers/oauth.ts OAuth provider registry, UI strings, provider‑specific metadata
src/sse/handlers/chat.ts Request handler demonstrating session‑occupancy locking
src/lib/zed-oauth/keychain-reader.ts Local development credential extraction from Zed IDE keychain

Summary

  • OAuth authentication with upstream providers in OmniRoute uses a three‑layer architecture: token refresh (tokenRefresh.ts), connection selection (auth.ts), and provider registry (oauth.ts)
  • Device‑flow login is initiated via omniroute login <provider> with automatic credential persistence
  • Session‑occupancy locking prevents token exhaustion through reserveOAuthSession and wrapResponseWithOAuthSessionRelease
  • Automatic token refresh occurs via getAccessToken with configurable expiry buffers and atomic database updates
  • Proxy awareness is built into all OAuth traffic without manual configuration
  • No‑auth fallback enables unified handling of OAuth and open‑source providers

Frequently Asked Questions

How does OmniRoute prevent OAuth token conflicts during concurrent requests?

OmniRoute implements pessimistic session‑occupancy locking via oauthSessionOccupancy.ts. When a request acquires an OAuth connection, it receives a releaseOAuthSession callback that must be invoked after completion. The wrapResponseWithOAuthSessionRelease utility guarantees release even for streaming responses, while the underlying mutex in auth.ts queues subsequent requests until the lock frees.

Can I use OmniRoute OAuth with corporate HTTP proxies?

Yes. Both tokenRefresh.ts and auth.ts automatically resolve proxy settings through resolveProxyForConnection. Per‑connection proxy overrides can be stored in the database, and all OAuth traffic tunnels appropriately without credential leakage. No manual proxy configuration is required in application code.

What happens when an OAuth token expires mid‑request?

The getAccessToken function in tokenRefresh.ts checks TOKEN_EXPIRY_BUFFER_MS before returning credentials. If refresh is needed, it executes synchronously within the connection's mutex, updates the database via the provided callback, and returns fresh tokens. Mid‑request expiration is therefore transparent to calling code.

How do I add support for a new OAuth provider in OmniRoute?

Add the provider to src/shared/constants/providers/oauth.ts with its human‑readable name, required scopes, and any UI hints. Implement provider‑specific token refresh logic in tokenRefresh.ts if the standard OAuth 2.0 flow requires adaptation. No changes to auth.ts are typically needed unless the provider requires custom connection‑selection rules.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →