# How to Configure OAuth for Claude Code, Codex, Cursor and Antigravity in OmniRoute

> Learn to configure OAuth for Claude Code, Codex, Cursor, and Antigravity in OmniRoute. Master environment variables and OAuth functions for seamless integration.

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

---

**To configure OAuth for Claude Code, Codex, Cursor, and Antigravity in OmniRoute, set the provider-specific environment variables for each service, then invoke the `buildAuthUrl`, `exchangeToken`, and `mapTokens` functions from their respective modules in `src/lib/oauth/providers/` to handle PKCE challenges, token exchanges, and response normalization.**

OmniRoute unifies authentication for multiple AI coding assistants through a standard provider interface. Each integration lives in `src/lib/oauth/providers/` and follows a consistent contract defined in [`src/lib/oauth/constants/oauth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/constants/oauth.ts), supporting `authorization_code_pkce`, `authorization_code`, and `import_token` flows.

## OmniRoute OAuth Provider Architecture

Every provider implements a standard interface with these core components:

- **`config`** – Provider constants (client IDs, URLs, scopes) imported from [`src/lib/oauth/constants/oauth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/constants/oauth.ts)
- **`flowType`** – The OAuth grant type (`authorization_code_pkce`, `authorization_code`, or `import_token`)
- **`buildAuthUrl`** – Constructs the initial authentication URL with required query parameters
- **`exchangeToken`** – Performs the HTTP POST to swap authorization codes for access tokens
- **`postExchange`** *(optional)* – Executes additional API calls after token reception (e.g., fetching user metadata)
- **`mapTokens`** – Normalizes raw API responses into OmniRoute’s internal token shape (`accessToken`, `refreshToken`, `expiresIn`, `email`)

To enable any provider, export the corresponding configuration object and invoke these methods in sequence according to the provider's `flowType`.

## Configuring Claude Code OAuth

The Claude provider ([`src/lib/oauth/providers/claude.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/providers/claude.ts)) implements the **Authorization Code with PKCE** flow against Anthropic’s OAuth endpoints.

### Claude Configuration Constants

In [`src/lib/oauth/constants/oauth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/constants/oauth.ts), the `CLAUDE_CONFIG` object defines the endpoints and scopes:

```typescript
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",
};

```

### Implementing the PKCE Flow

1. Generate a PKCE code verifier and challenge using `generatePkcePair`
2. Call `claude.buildAuthUrl(config, redirectUri, state, codeChallenge)` to create the login URL
3. After user authentication, extract the `code` from the redirect URL callback
4. Exchange the code via `claude.exchangeToken(config, code, redirectUri, codeVerifier, state)`
5. Execute `claude.postExchange` to fetch account bootstrap data from `https://api.anthropic.com/api/claude_cli/bootstrap`
6. Normalize the result with `claude.mapTokens`, which extracts plan data and generates a random `cliUserID`

### Claude OAuth 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("Navigate to:", authUrl);

  // Capture code from redirect callback
  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("Normalized tokens:", omniTokens);
}

```

## Configuring Codex (OpenAI) OAuth

The Codex provider ([`src/lib/oauth/providers/codex.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/providers/codex.ts)) uses **Authorization Code with PKCE**, but includes additional query parameters to force fresh authentication and enable multi-account support.

### Codex Configuration and Multi-Account Support

The `CODEX_CONFIG` in [`src/lib/oauth/constants/oauth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/constants/oauth.ts) specifies:

```typescript
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 multi-account switching
  },
};

```

### Token Exchange Process

The `codex.buildAuthUrl` method automatically appends `extraParams` to the authorization URL. Unlike Claude, Codex does not require a `postExchange` step; the `id_token` JWT in the initial response contains workspace metadata that `codex.mapTokens` parses directly.

### Codex OAuth 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 are automatically injected
  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 for Codex
  const omniTokens = codex.mapTokens(rawTokens, {});
  console.log(omniTokens);
}

```

## Configuring Cursor OAuth (Import Token Flow)

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

### Accessing Cursor's SQLite Storage

The `CURSOR_CONFIG` defines platform-specific paths to the `state.vscdb` file:

- **Linux**: `~/.config/Cursor/User/globalStorage/state.vscdb`
- **macOS**: `~/Library/Application Support/Cursor/User/globalStorage/state.vscdb`
- **Windows**: `%APPDATA%\Cursor\User\globalStorage\state.vscdb`

The database keys are `cursorAuth/accessToken` and `storage.serviceMachineId`.

### Token Mapping Implementation

```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 pathTemplate = CURSOR_CONFIG.tokenStoragePaths[
    platform === "win32" ? "windows" : platform === "darwin" ? "macos" : "linux"
  ];
  return pathTemplate.replace("<user>", os.userInfo().username);
}

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

  const omniTokens = cursor.mapTokens({
    accessToken: tokenRow?.value,
    machineId: machineRow?.value,
    expiresIn: 86400, // Cursor tokens expire in ~24 hours
  });
  
  return omniTokens;
}

```

## Configuring Antigravity OAuth

The Antigravity provider ([`src/lib/oauth/providers/antigravity.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/providers/antigravity.ts)) implements the standard **Authorization Code** flow without PKCE, using Google's OAuth infrastructure.

### Google Authorization Code Configuration

The `ANTIGRAVITY_CONFIG` requires both a client ID and client secret:

```typescript
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",
    "https://www.googleapis.com/auth/experimentsandconfigs",
  ],
};

```

### Post-Exchange Data Loading

After token exchange, `antigravity.postExchange` performs three operations:

1. Fetches user information from the Google UserInfo endpoint
2. Loads Code-Assist configuration via `loadCodeAssistEndpoints`
3. Executes background onboarding (non-blocking)

The `mapTokens` function then extracts `projectId`, `tier`, and `email` from the aggregated data.

### Antigravity OAuth 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 challenge is 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 record:", omniTokens);
}

```

## Using the Generic OAuth API Route

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

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

```

Supported actions include:

- **`authorize`** – Returns the authentication URL and PKCE verifier
- **`exchange`** – Swaps the authorization code for tokens
- **`import`** – Handles import-token flows for Cursor

Example request for Claude authorization:

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

```

The route delegates to the specific provider module, ensuring consistent JSON payloads across all four services.

## Summary

- **Claude Code** and **Codex** use PKCE flows via [`src/lib/oauth/providers/claude.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/providers/claude.ts) and [`src/lib/oauth/providers/codex.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/providers/codex.ts), requiring `CLAUDE_OAUTH_CLIENT_ID` or `CODEX_OAUTH_CLIENT_ID` environment variables and the `generatePkcePair` utility.
- **Cursor** uses an import-token flow reading from local SQLite storage at [`src/lib/oauth/providers/cursor.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/providers/cursor.ts), requiring filesystem access to the Cursor IDE's database.
- **Antigravity** uses a standard authorization-code flow against Google OAuth endpoints via [`src/lib/oauth/providers/antigravity.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/providers/antigravity.ts), requiring both `ANTIGRAVITY_OAUTH_CLIENT_ID` and `ANTIGRAVITY_OAUTH_CLIENT_SECRET`.
- All providers normalize output through `mapTokens` and support the generic `/api/oauth/[provider]/[action]` endpoint for consistent integration.

## Frequently Asked Questions

### What environment variables are required to configure OAuth for Claude Code, Codex, Cursor and Antigravity?

For **Claude**, set `CLAUDE_OAUTH_CLIENT_ID` and optionally `CLAUDE_CODE_REDIRECT_URI`. For **Codex**, set `CODEX_OAUTH_CLIENT_ID`. For **Antigravity**, set both `ANTIGRAVITY_OAUTH_CLIENT_ID` and `ANTIGRAVITY_OAUTH_CLIENT_SECRET`. **Cursor** requires no environment variables for OAuth, as it reads tokens directly from the local IDE's SQLite database at `~/.config/Cursor/User/globalStorage/state.vscdb` (Linux) or equivalent paths per operating system.

### Does OmniRoute use PKCE for all OAuth providers?

No. **Claude Code** and **Codex** implement `authorization_code_pkce` with S256 code challenges. **Antigravity** uses the standard `authorization_code` flow where PKCE is optional and not required by the Google OAuth implementation. **Cursor** does not use a web-based OAuth flow at all; instead, it uses the `import_token` grant type to read existing credentials from local storage.

### How does the Cursor provider handle authentication without a browser redirect?

The Cursor provider ([`src/lib/oauth/providers/cursor.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/providers/cursor.ts)) executes an import-token flow that queries the Cursor IDE's internal SQLite database (`state.vscdb`). It extracts the `cursorAuth/accessToken` and `storage.serviceMachineId` keys using SQL queries, then passes these values to `cursor.mapTokens` to produce the normalized OmniRoute token format. This approach requires direct filesystem access but eliminates the need for interactive OAuth consent screens.

### Can I use the generic API route instead of importing provider modules directly?

Yes. The `/api/oauth/[provider]/[action]` endpoint delegates to the same underlying functions found in `src/lib/oauth/providers/`. You can invoke `POST /api/oauth/claude/authorize` to obtain a login URL, then `POST /api/oauth/claude/exchange` with the authorization code to complete the flow. This approach is useful for client-side applications or when you want to avoid direct Node.js dependencies on the provider modules.