# Setting Up OAuth Providers (Claude Code, Codex, Cursor, Cline) with OmniRoute

> Easily set up OAuth providers like Claude Code, Codex, Cursor, and Cline with OmniRoute. Unify authentication and streamline your application's access to multiple services.

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

---

**OmniRoute unifies Claude Code, Codex, Cursor, and Cline behind a single OAuth 2.0 architecture that stores access tokens in a local connections database and routes requests through provider-specific executors and translators.**

OmniRoute treats every upstream model as a *provider* that can authenticate via OAuth 2.0 or API keys. Setting up OAuth providers for Claude Code, Codex, Cursor, and Cline involves registering provider metadata, configuring environment variables, and initiating the authorization flow through generic API endpoints that handle token exchange and storage automatically.

## Provider Registration and OAuth Configuration

Each provider starts with a registration entry in the provider catalog. OmniRoute defines compatibility prefixes in [`src/shared/constants/providers.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/shared/constants/providers.ts) (such as `CLAUDE_CODE_COMPATIBLE_PREFIX`) and maintains concrete registry entries under `open-sse/config/providers/registry/`.

The OAuth-specific metadata for each provider lives in dedicated modules under `src/lib/oauth/providers/`:

- **[`src/lib/oauth/providers/claude.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/providers/claude.ts)** – Defines `clientId`, `clientSecret`, `authorizeUrl`, `tokenUrl`, scopes, and the User-Agent header (`claude-cli/${CLAUDE_CODE_VERSION}`) that Claude Code requires
- **[`src/lib/oauth/providers/cursor.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/providers/cursor.ts)** – Configures Cursor-specific endpoints and token handling
- **[`src/lib/oauth/providers/codex.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/providers/codex.ts)** – Manages Codex OAuth metadata and scope requirements
- **[`src/lib/oauth/providers/cline.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/providers/cline.ts)** – Handles Cline provider registration and authentication headers

These modules export a consistent interface used by the generic OAuth router to build authorization URLs and exchange codes for tokens.

## Environment Variable Setup

Before initiating flows, configure the OAuth credentials in your environment. Create or update your `.env` file with the client ID and secret pairs for each provider:

```bash

# Claude Code OAuth credentials

CLAUDE_CODE_CLIENT_ID=your-claude-code-client-id
CLAUDE_CODE_CLIENT_SECRET=your-claude-code-secret

# Codex OAuth credentials

CODEX_CLIENT_ID=your-codex-client-id
CODEX_CLIENT_SECRET=your-codex-secret

# Cursor OAuth credentials

CURSOR_CLIENT_ID=your-cursor-client-id
CURSOR_CLIENT_SECRET=your-cursor-secret

# Cline OAuth credentials

CLINE_CLIENT_ID=your-cline-client-id
CLINE_CLIENT_SECRET=your-cline-secret

```

Default redirect URIs and optional overrides are centralized in [`src/lib/oauth/constants/oauth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/constants/oauth.ts). After updating environment variables, restart OmniRoute (`npm run dev` or `npm run start`) to load the new configuration into the provider modules.

## Initiating the OAuth Flow

All OAuth actions—**start**, **callback**, **poll**, and **revoke**—are handled by the dynamic route in `src/app/api/oauth/[provider]/[action]/route.ts`. This route delegates to provider-specific helpers (`generateAuthData`, `getProvider`) to construct the proper authorization URL and exchange codes for access tokens.

### Starting Authorization

Initiate the OAuth flow by calling the start endpoint for your chosen provider:

```bash

# Claude Code

curl -X GET "http://localhost:3000/api/oauth/claude/start?redirect_uri=http://localhost:3000/callback"

# Cursor

curl -X GET "http://localhost:3000/api/oauth/cursor/start?redirect_uri=http://localhost:3000/callback"

```

OmniRoute returns a **302** redirect to the provider's consent page. After you grant permission, the provider redirects back to your specified callback URI with an authorization `code`.

### Handling the Callback

The provider redirects to `/api/oauth/<provider>/callback` with the authorization code. OmniRoute automatically exchanges this code for an access token using the provider's `tokenUrl` and stores the result:

```bash
curl -X POST "http://localhost:3000/api/oauth/claude/callback" \
  -H "Content-Type: application/json" \
  -d '{
    "code": "AUTH_CODE_FROM_REDIRECT",
    "redirect_uri": "http://localhost:3000/callback"
  }'

```

On success, the response includes a `connectionId` referencing the newly created OAuth connection in the database.

## Token Storage and Request Execution

Once obtained, access tokens are persisted in the `connections` table with `authType: "oauth"`. When you reference a connection in subsequent API calls, OmniRoute retrieves the token and routes the request through the appropriate executor.

### Executors and Translators

Request execution depends on the provider type:

- **DefaultExecutor** ([`open-sse/executors/default.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/executors/default.ts)) – Handles most providers by attaching `Authorization: Bearer <token>` headers to OpenAI-compatible endpoints
- **ClaudeExecutor** ([`open-sse/executors/claudeIdentity.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/executors/claudeIdentity.ts)) – Special handler for Claude Code that injects the `anthropic-beta` header alongside bearer tokens

The translation layer converts OpenAI-style requests to provider-native formats:

- **[`open-sse/translator/claude.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/claude.ts)** – Translates between OpenAI schema and Claude's native messages format
- **[`open-sse/translator/cursor.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/translator/cursor.ts)** – Adapts requests for Cursor's specific API structure

### Making Authenticated Requests

Use the stored connection to route chat completions through your OAuth provider:

```bash
curl -X POST "http://localhost:3000/api/v1/chat/completions" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-opus-4",
    "messages": [{"role": "user", "content": "Hello!"}],
    "connectionId": "your-connection-id-from-callback"
  }'

```

OmniRoute automatically attaches the OAuth token via the provider's executor and translates the response back to OpenAI-compatible JSON.

## Usage Monitoring and CLI Integration

Each OAuth provider implements quota retrieval through usage fetchers. For example, [`src/lib/oauth/providers/cursor.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/providers/cursor.ts) exports a `fetchUsage` function that calls `GET /api/oauth/usage` to check remaining credits.

Check usage programmatically:

```bash
curl -X GET "http://localhost:3000/api/oauth/usage?provider=cursor&connectionId=your-connection-id"

```

### CLI Shortcuts

OmniRoute includes a test CLI ([`src/cli/omni.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/cli/omni.ts)) that simplifies OAuth flows:

```bash

# Start Claude Code OAuth flow

npm run cli -- oauth claude start

# Exchange code after browser redirect

npm run cli -- oauth claude callback <AUTH_CODE>

```

## Complete Integration Example

Below is a TypeScript implementation demonstrating the full OAuth lifecycle:

```typescript
import fetch from "node-fetch";

// Step 1: Initiate OAuth flow
async function startOAuth(provider: string) {
  const res = await fetch(
    `http://localhost:3000/api/oauth/${provider}/start?redirect_uri=http://localhost:3000/callback`,
    { redirect: "manual" }
  );
  const location = res.headers.get("location");
  console.log(`Authorize ${provider} at:`, location);
}

// Step 2: Exchange authorization code for token
async function exchangeCode(provider: string, code: string) {
  const res = await fetch(
    `http://localhost:3000/api/oauth/${provider}/callback`,
    {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ 
        code, 
        redirect_uri: "http://localhost:3000/callback" 
      }),
    }
  );
  const body = await res.json();
  console.log(`Connection created:`, body.connectionId);
  return body.connectionId;
}

// Step 3: Make authenticated request
async function chatWithProvider(connectionId: string) {
  const res = await fetch("http://localhost:3000/api/v1/chat/completions", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      model: "claude-opus-4",
      messages: [{ role: "user", content: "Explain OAuth flows" }],
      connectionId,
    }),
  });
  const reply = await res.json();
  console.log("Response:", reply.choices?.[0]?.message?.content);
}

// Usage: await startOAuth("claude"); 
// Then: await exchangeCode("claude", "CODE_FROM_QUERY");
// Finally: await chatWithProvider("CONNECTION_ID");

```

## Summary

- **Provider registration** happens in [`src/shared/constants/providers.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/shared/constants/providers.ts) and `open-sse/config/providers/registry/` files, defining base URLs and compatibility prefixes
- **OAuth metadata** (client IDs, secrets, endpoints) lives in [`src/lib/oauth/providers/claude.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/providers/claude.ts), [`cursor.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/cursor.ts), [`codex.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/codex.ts), and [`cline.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/cline.ts)
- **Environment variables** must be set for each provider's `CLIENT_ID` and `CLIENT_SECRET`, loaded via [`src/lib/oauth/constants/oauth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/constants/oauth.ts)
- **Flow endpoints** are handled generically by `src/app/api/oauth/[provider]/[action]/route.ts`, which delegates to provider-specific code exchange logic
- **Token storage** uses the `connections` table with `authType: "oauth"`, referenced by `connectionId` in subsequent requests
- **Request execution** routes through [`open-sse/executors/default.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/executors/default.ts) (Cursor, Codex, Cline) or [`open-sse/executors/claudeIdentity.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/executors/claudeIdentity.ts) (Claude Code), with translation layers converting between OpenAI and native formats

## Frequently Asked Questions

### How does OmniRoute store OAuth tokens securely?

OmniRoute stores OAuth access tokens in the local `connections` table (managed via [`src/lib/db/connections.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/connections.ts)) with `authType` set to `"oauth"`. Tokens are retrieved by `connectionId` during request execution and injected into the `Authorization: Bearer` header by the appropriate executor. The system does not log or expose tokens in API responses after the initial callback exchange.

### Can I use the same OAuth application for multiple users?

Yes. Each authorization flow creates a distinct row in the connections table with a unique `connectionId`. Multiple users can authorize the same provider application (using the same `CLIENT_ID` and `CLIENT_SECRET`), and OmniRoute maintains separate tokens for each user through distinct connection records. When making requests, specify the appropriate `connectionId` to route through the correct user's OAuth context.

### Why does Claude Code require a special executor while Cursor uses the default?

Claude Code requires specific headers—including `anthropic-beta` and a custom User-Agent (`claude-cli/${CLAUDE_CODE_VERSION}`)—that differ from standard OpenAI-compatible implementations. The `ClaudeExecutor` in [`open-sse/executors/claudeIdentity.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/executors/claudeIdentity.ts) handles these requirements, while Cursor, Codex, and Cline expose OpenAI-compatible endpoints that work with the `DefaultExecutor` in [`open-sse/executors/default.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/executors/default.ts) using standard bearer token authentication.

### How do I revoke an OAuth connection?

Send a request to the revoke endpoint for the specific provider and connection:

```bash
curl -X POST "http://localhost:3000/api/oauth/claude/revoke" \
  -H "Content-Type: application/json" \
  -d '{"connectionId": "your-connection-id"}'

```

This removes the token from the connections table and calls the provider's revocation endpoint if configured in the provider's OAuth module (such as [`src/lib/oauth/providers/claude.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/providers/claude.ts)).