# How to Set Up OAuth Providers for Claude Code, Codex, and Antigravity in OmniRoute

> Easily set up OAuth providers like Claude Code, Codex, and Antigravity in OmniRoute. Configure authentication flows and modules using environment variables for seamless integration.

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

---

**OmniRoute provides a unified OAuth framework in `src/lib/oauth/providers/` that supports multiple authentication flows—including Authorization-Code PKCE for Claude Code and Codex, Import-Token for Cursor, and standard Authorization-Code for Antigravity—allowing you to configure these providers through environment variables and standardized provider modules.**

Setting up OAuth providers in the diegosouzapw/OmniRoute repository involves configuring environment variables and leveraging provider-specific modules that handle distinct authentication flows. Each provider resides in `src/lib/oauth/providers/` and implements a consistent contract including `buildAuthUrl`, `exchangeToken`, and `mapTokens` functions. This guide covers the exact implementation details for Claude Code, Codex, Cursor, and Antigravity, including configuration constants from [`src/lib/oauth/constants/oauth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/constants/oauth.ts) and runnable code examples.

## OmniRoute OAuth Architecture Overview

Every provider in OmniRoute follows a strict contract defined in [`src/lib/oauth/constants/oauth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/constants/oauth.ts). Each implementation exports a `config` object containing URLs, scopes, and client credentials, plus five core functions:

- **`buildAuthUrl`** – Generates the authorization URL with state and PKCE parameters where applicable.
- **`exchangeToken`** – Swaps the authorization code for access tokens via HTTP POST.
- **`postExchange`** – Optional hook for fetching additional data (user info, bootstrap data) after token exchange.
- **`mapTokens`** – Normalizes the raw OAuth response into OmniRoute’s internal shape (`accessToken`, `refreshToken`, `expiresIn`, `email`, `providerSpecificData`).

The `PROVIDERS` enum in [`constants/oauth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/constants/oauth.ts) registers each provider, while environment variables like `CLAUDE_OAUTH_CLIENT_ID` and `ANTIGRAVITY_OAUTH_CLIENT_SECRET` inject credentials securely.

## Claude Code OAuth Configuration

**Claude Code** (Anthropic) uses the **Authorization-Code PKCE** flow defined in [`src/lib/oauth/providers/claude.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/providers/claude.ts). This implementation requires a code verifier and challenge to secure the token exchange.

### Key Configuration Constants

```typescript
// src/lib/oauth/constants/oauth.ts
export const CLAUDE_CONFIG = {
  clientId: resolvePublicCred("claude_id", "CLAUDE_OAUTH_CLIENT_ID"),
  authorizeUrl: "https://claude.ai/oauth/authorize",
  tokenUrl: "https://api.anthropic.com/v1/oauth/token",
  redirectUri: process.env.CLAUDE_CODE_REDIRECT_URI || "https://platform.claude.com/oauth/code/callback",
  scopes: [
    "org:create_api_key",
    "user:profile",
    "user:inference",
    "user:sessions:claude_code",
    "user:mcp_servers",
  ],
  codeChallengeMethod: "S256",
};

```

### Implementation Flow

1. **Generate PKCE pair** using `generatePkcePair()` from `@omniroute/open-sse/utils/pkce.ts`.
2. **Build auth URL** via `claude.buildAuthUrl(config, redirectUri, state, codeChallenge)`.
3. **Exchange code** after user redirect using `claude.exchangeToken(config, code, redirectUri, codeVerifier, state)`.
4. **Bootstrap account data** via `claude.postExchange(rawTokens)`, which fetches from `https://api.anthropic.com/api/claude_cli/bootstrap`.
5. **Map tokens** to OmniRoute format using `claude.mapTokens(rawTokens, extra)`, which injects a random `cliUserID`.

### Code Example

```typescript
import { claude } from "./src/lib/oauth/providers/claude.ts";
import { CLAUDE_CONFIG } from "./src/lib/oauth/constants/oauth.ts";
import { generatePkcePair } from "@omniroute/open-sse/utils/pkce.ts";

async function loginClaude() {
  const { verifier, challenge } = await generatePkcePair();
  const state = crypto.randomUUID();
  const authUrl = claude.buildAuthUrl(
    CLAUDE_CONFIG,
    CLAUDE_CONFIG.redirectUri,
    state,
    challenge,
  );
  console.log("Open this URL:", authUrl);

  // After user authorization, capture the code from redirect
  const code = await waitForCodeFromRedirect();

  const rawTokens = await claude.exchangeToken(
    CLAUDE_CONFIG,
    code,
    CLAUDE_CONFIG.redirectUri,
    verifier,
    state,
  );

  const extra = await claude.postExchange(rawTokens);
  const omniTokens = claude.mapTokens(rawTokens, extra);
  console.log("OmniRoute token:", omniTokens);
}

```

## Codex OAuth Configuration

**Codex** (OpenAI) also uses **Authorization-Code PKCE** but adds query parameters to force fresh authentication, enabling multi-account login support. The implementation lives in [`src/lib/oauth/providers/codex.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/providers/codex.ts).

### Key Configuration Constants

```typescript
// src/lib/oauth/constants/oauth.ts
export const CODEX_CONFIG = {
  clientId: resolvePublicCred("codex_id", "CODEX_OAUTH_CLIENT_ID"),
  authorizeUrl: "https://auth.openai.com/oauth/authorize",
  tokenUrl: "https://auth.openai.com/oauth/token",
  scope: "openid profile email offline_access",
  codeChallengeMethod: "S256",
  extraParams: {
    id_token_add_organizations: "true",
    codex_cli_simplified_flow: "true",
    originator: "codex_cli_rs",
    prompt: "login", // Forces login screen for account switching
  },
};

```

### Implementation Differences

Unlike Claude, **Codex does not use `postExchange`**. The `mapTokens` function parses the `id_token` JWT directly to extract workspace data. The `extraParams` are automatically appended to the authorization URL by `buildAuthUrl`.

### Code Example

```typescript
import { codex } from "./src/lib/oauth/providers/codex.ts";
import { CODEX_CONFIG } from "./src/lib/oauth/constants/oauth.ts";
import { generatePkcePair } from "@omniroute/open-sse/utils/pkce.ts";

async function loginCodex() {
  const { verifier, challenge } = await generatePkcePair();
  const state = crypto.randomUUID();

  // extraParams injected automatically
  const authUrl = codex.buildAuthUrl(
    CODEX_CONFIG,
    CODEX_CONFIG.redirectUri ?? "",
    state,
    challenge,
  );
  console.log("Visit:", authUrl);

  const code = await waitForCodeFromRedirect();

  const rawTokens = await codex.exchangeToken(
    CODEX_CONFIG,
    code,
    CODEX_CONFIG.redirectUri ?? "",
    verifier,
    state,
  );

  // No postExchange needed
  const omniTokens = codex.mapTokens(rawTokens, {});
  console.log(omniTokens);
}

```

## Cursor Import-Token Configuration

**Cursor** uses a unique **Import-Token** flow rather than web-based OAuth. The provider reads credentials directly from the Cursor IDE's local SQLite database. Implementation: [`src/lib/oauth/providers/cursor.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/providers/cursor.ts).

### Key Configuration Constants

```typescript
// src/lib/oauth/constants/oauth.ts
export const CURSOR_CONFIG = {
  apiEndpoint: "https://api2.cursor.sh",
  clientVersion: "3.2.14",
  clientType: "ide",
  tokenStoragePaths: {
    linux: "~/.config/Cursor/User/globalStorage/state.vscdb",
    macos: "/Users/<user>/Library/Application Support/Cursor/User/globalStorage/state.vscdb",
    windows: "%APPDATA%\\Cursor\\User\\globalStorage\\state.vscdb",
  },
  dbKeys: {
    accessToken: "cursorAuth/accessToken",
    machineId: "storage.serviceMachineId",
  },
};

```

### Database Extraction Flow

1. **Locate SQLite DB** at OS-specific path (e.g., `~/.config/Cursor/User/globalStorage/state.vscdb` on Linux).
2. **Query keys** `cursorAuth/accessToken` and `storage.serviceMachineId`.
3. **Map tokens** via `cursor.mapTokens()` with a default 24-hour expiration.

### Code Example

```typescript
import { cursor } from "./src/lib/oauth/providers/cursor.ts";
import { CURSOR_CONFIG } from "./src/lib/oauth/constants/oauth.ts";
import sqlite3 from "better-sqlite3";
import os from "node:os";

function getCursorDbPath(): string {
  const platform = os.platform();
  const base = CURSOR_CONFIG.tokenStoragePaths[
    platform === "win32" ? "windows" : platform === "darwin" ? "macos" : "linux"
  ];
  return base.replace("<user>", os.userInfo().username);
}

function importCursorToken() {
  const dbPath = getCursorDbPath();
  const db = sqlite3(dbPath, { readonly: true });
  
  const token = db
    .prepare(`SELECT value FROM ItemTable WHERE key = ?`)
    .get(CURSOR_CONFIG.dbKeys.accessToken);
  const machine = db
    .prepare(`SELECT value FROM ItemTable WHERE key = ?`)
    .get(CURSOR_CONFIG.dbKeys.machineId);

  const omniTokens = cursor.mapTokens({
    accessToken: token?.value,
    machineId: machine?.value,
    expiresIn: 86400, // ~24 hours
  });

  console.log("Cursor token:", omniTokens);
}

```

## Antigravity OAuth Configuration

**Antigravity** uses **Authorization-Code** flow without PKCE (Google native flow). The implementation in [`src/lib/oauth/providers/antigravity.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/providers/antigravity.ts) includes a post-exchange step that fetches user information and Code-Assist endpoints.

### Key Configuration Constants

```typescript
// src/lib/oauth/constants/oauth.ts
export const ANTIGRAVITY_CONFIG = {
  clientId: resolvePublicCred("antigravity_id", "ANTIGRAVITY_OAUTH_CLIENT_ID"),
  clientSecret: resolvePublicCred("antigravity_alt", "ANTIGRAVITY_OAUTH_CLIENT_SECRET"),
  authorizeUrl: "https://accounts.google.com/o/oauth2/v2/auth",
  tokenUrl: "https://oauth2.googleapis.com/token",
  userInfoUrl: "https://www.googleapis.com/oauth2/v1/userinfo",
  scopes: [
    "https://www.googleapis.com/auth/cloud-platform",
    "https://www.googleapis.com/auth/userinfo.email",
    "https://www.googleapis.com/auth/userinfo.profile",
    "https://www.googleapis.com/auth/cclog",
  ],
};

```

### Implementation Flow

1. **Build auth URL** without required PKCE (optional challenge ignored by service).
2. **Exchange token** using `client_secret` in the POST body.
3. **Post-exchange** fetches `userInfo` and calls `loadCodeAssistEndpoints()` to retrieve additional metadata.
4. **Map tokens** extracts `projectId`, `tier`, and `email` from the extra data.

### Code Example

```typescript
import { antigravity } from "./src/lib/oauth/providers/antigravity.ts";
import { ANTIGRAVITY_CONFIG } from "./src/lib/oauth/constants/oauth.ts";

async function loginAntigravity() {
  const state = crypto.randomUUID();
  
  // PKCE optional for Antigravity
  const authUrl = antigravity.buildAuthUrl(
    ANTIGRAVITY_CONFIG,
    "http://localhost:3000/oauth/callback",
    state,
  );

  console.log("Open:", authUrl);
  const code = await waitForCodeFromRedirect();

  const rawTokens = await antigravity.exchangeToken(
    ANTIGRAVITY_CONFIG,
    code,
    "http://localhost:3000/oauth/callback",
  );

  const extra = await antigravity.postExchange(rawTokens);
  const omniTokens = antigravity.mapTokens(rawTokens, extra);
  console.log("Antigravity token:", omniTokens);
}

```

## Using the Generic OAuth API

OmniRoute exposes a Next.js route that abstracts provider-specific implementations:

```

POST /api/oauth/[provider]/[action]

```

- **`provider`** – Enum value from `PROVIDERS` (`claude`, `codex`, `cursor`, `antigravity`).
- **`action`** – `authorize` (returns URL), `exchange` (trades code for tokens), or `import` (for Cursor).

Example request for Claude:

```bash
curl -X POST https://your-app.com/api/oauth/claude/authorize \
  -H "Content-Type: application/json" \
  -d '{"redirectUri":"https://myapp.local/callback"}'

```

Response:

```json
{
  "authUrl": "https://claude.ai/oauth/authorize?...&code_challenge=..."
}

```

## Summary

- **Claude Code** requires the PKCE flow with `codeChallengeMethod: "S256"` and a post-exchange bootstrap call to `https://api.anthropic.com/api/claude_cli/bootstrap`.
- **Codex** uses PKCE with `extraParams.prompt=login` to enable multi-account authentication and skips post-exchange processing by parsing the JWT directly.
- **Cursor** bypasses web OAuth entirely, reading the `accessToken` and `machineId` from the local SQLite database at `state.vscdb`.
- **Antigravity** uses standard Authorization-Code without PKCE, requiring `client_secret` and implementing `postExchange` to fetch Google user info and Code-Assist endpoints.
- All providers normalize data through `mapTokens()` and store configuration in [`src/lib/oauth/constants/oauth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/constants/oauth.ts).

## Frequently Asked Questions

### What is the difference between PKCE and Import-Token flows in OmniRoute?

**PKCE (Proof Key for Code Exchange)** is used by Claude Code and Codex to secure public clients by verifying the code challenge during token exchange, preventing authorization code interception attacks. **Import-Token**, used exclusively by Cursor, reads existing credentials from the local filesystem rather than initiating a web flow, as the token is already stored in the Cursor IDE's SQLite database.

### How do I handle environment variables for multiple OAuth providers?

Each provider uses a specific environment variable pattern defined in `resolvePublicCred()` calls within [`src/lib/oauth/constants/oauth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/constants/oauth.ts). For Claude, set `CLAUDE_OAUTH_CLIENT_ID`; for Codex, `CODEX_OAUTH_CLIENT_ID`; for Antigravity, both `ANTIGRAVITY_OAUTH_CLIENT_ID` and `ANTIGRAVITY_OAUTH_CLIENT_SECRET`. Cursor requires no environment variables since it imports tokens from the local database.

### Why does Antigravity not require PKCE while Claude and Codex do?

Antigravity uses Google's OAuth 2.0 implementation which supports confidential clients with `client_secret` authentication, making PKCE optional. Claude and Codex implement OAuth for public clients (CLI tools) where the client cannot securely store a secret, making PKCE mandatory to protect the authorization code during the exchange process.

### Can I customize the redirect URI for these OAuth providers?

Yes, all providers support custom redirect URIs through the `redirectUri` parameter in `buildAuthUrl()` or the `redirectUri` field in the provider config objects. For Claude, you can also set the `CLAUDE_CODE_REDIRECT_URI` environment variable to override the default `https://platform.claude.com/oauth/code/callback`.