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

> Master OAuth authentication with Upstream Providers in OmniRoute. This guide details device-flow, browser-based auth, auto-refresh, and more for seamless LLM integration.

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

---

**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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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:

```bash

# 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`](https://github.com/diegosouzapw/OmniRoute/blob/main/tokenRefresh.ts) persists the returned tokens to the local database.

### 2. Configure Connection Routing

OmniRoute supports **multiple concurrent connections** per provider. The [`auth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/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:

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

```

### 3. Handle Token Refresh Automatically

The `getAccessToken` function in [`tokenRefresh.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/tokenRefresh.ts) encapsulates all refresh complexity. Example integration in a custom handler:

```typescript
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`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/sse/handlers/chat.ts) handler demonstrates proper session management:

```typescript
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:

```typescript
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`](https://github.com/diegosouzapw/OmniRoute/blob/main/tokenRefresh.ts) and [`auth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/auth.ts) enables transparent fallback:

```typescript
// 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`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/sse/services/tokenRefresh.ts) | Token refresh orchestration, proxy resolution, database persistence |
| [`src/sse/services/auth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/sse/services/auth.ts) | Connection selection, quota enforcement, session‑affinity handling |
| [`src/shared/constants/providers/oauth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/shared/constants/providers/oauth.ts) | OAuth provider registry, UI strings, provider‑specific metadata |
| [`src/sse/handlers/chat.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/sse/handlers/chat.ts) | Request handler demonstrating session‑occupancy locking |
| [`src/lib/zed-oauth/keychain-reader.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/tokenRefresh.ts)), connection selection ([`auth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/auth.ts)), and provider registry ([`oauth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/auth.ts) queues subsequent requests until the lock frees.

### Can I use OmniRoute OAuth with corporate HTTP proxies?

Yes. Both [`tokenRefresh.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/tokenRefresh.ts) and [`auth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/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`](https://github.com/diegosouzapw/OmniRoute/blob/main/tokenRefresh.ts) if the standard OAuth 2.0 flow requires adaptation. No changes to [`auth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/auth.ts) are typically needed unless the provider requires custom connection‑selection rules.