# OmniRoute OAuth Authentication Flow for Claude Code, Codex, Cursor, and 17+ Providers

> Learn how OmniRoute's unified OAuth flow simplifies authentication for Claude Code, Codex, Cursor, and 17+ providers. Handles token exchange, persistence, and refresh automatically.

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

---

**OmniRoute implements a unified PKCE-based OAuth 2.0 flow through a centralized Next.js route handler that treats Claude Code, Codex, Cursor, Antigravity, and 13 other providers as first-class credential types, automatically handling token exchange, encrypted persistence, refresh cycles, and injection into upstream API requests.**

OmniRoute supports OAuth authentication for Claude Code, Codex, Cursor, Antigravity, and 13 additional providers as first-class credential types alongside API keys. According to the diegosouzapw/OmniRoute source code (release v3.8.50), the platform uses a single generic route handler in `src/app/api/oauth/[provider]/[action]/route.ts` combined with provider-specific modules in `src/lib/oauth/providers/*.ts` to standardize the entire lifecycle from authorization to token refresh.

## The OmniRoute OAuth Lifecycle

The end-to-end OAuth flow follows eight distinct steps, utilizing PKCE (Proof Key for Code Exchange) for security and encrypted credential blobs for storage.

### 1. Authorization Request with PKCE

The frontend initiates authentication by calling `GET /api/oauth/<provider>/authorize` (or `start`). The generic route executes **`generateAuthData`** from [`src/lib/oauth/providers.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/providers.ts) (line 134), which constructs a cryptographically random `state` token, generates a `codeVerifier` for PKCE, and builds the provider-specific authorization URL.

### 2. Provider Redirect and User Consent

The client opens the returned URL in a browser, directing the user to the provider’s login page (e.g., Anthropic for Claude Code, Cursor's auth server). After successful authentication and consent, the provider redirects to OmniRoute’s callback endpoint.

### 3. Callback Capture and State Validation

The provider redirects to `<OmniRoute-base>/api/oauth/<provider>/callback?code=…&state=…`. The single generic route in `src/app/api/oauth/[provider]/[action]/route.ts` handles this redirect, validates the `state` parameter against the session, and temporarily stores the authorization `code`. OmniRoute does not expose separate callback handlers per provider; all redirect traffic flows through this unified handler.

### 4. Token Exchange

The client sends a `POST` request to `/api/oauth/<provider>/exchange` containing the `code` and original `codeVerifier`. The generic route forwards this to the provider-specific **`exchangeToken`** function—for example, [`src/lib/oauth/providers/antigravity.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/providers/antigravity.ts) (line 45) or equivalent functions in [`codex.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/codex.ts), [`cursor.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/cursor.ts), and [`claude.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/claude.ts). This server-to-server call exchanges the code for an access token and optional refresh token.

### 5. Encrypted Credential Persistence

The received tokens are wrapped in a credential blob prefixed with `CREDENTIAL_BLOB_PREFIX` and encrypted at rest using the utility in [`src/lib/db/encryption.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/encryption.ts). The **`writeCredentialBlob`** function in [`src/lib/oauth/credentialBlob.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/credentialBlob.ts) (line 112) persists this data to the connections table with `authType: "oauth"`, associating the credential with the user’s account.

### 6. Automatic Token Injection

When processing requests to protected models, the executor (`DefaultExecutor` in [`src/open-sse/executors/default.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/open-sse/executors/default.ts)) calls **`getProviderCredentials`** from [`src/lib/oauth/connectionPersistence.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/connectionPersistence.ts) (line 68). This retrieves and decrypts the stored blob, injecting the `Authorization: Bearer <token>` header into upstream API requests without exposing credentials to the client.

### 7. Background Token Refresh

If the access token is expired and a refresh token is available, OmniRoute automatically executes the refresh flow before forwarding the request. Provider-specific logic like **`refreshAccessToken`** in [`src/lib/oauth/providers/claude.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/providers/claude.ts) (line 78) handles the refresh request, updating the stored credential blob with new tokens.

### 8. Credential Revocation

Users can revoke access via `POST /api/oauth/<provider>/revoke`. The generic route clears the credential blob from the connections table, marks the connection as revoked, and prevents further upstream calls using that credential.

## Provider-Specific Implementation Quirks

While the core flow is standardized, individual providers require specialized handling:

- **Claude Code**: Uses standard OAuth 2.0 authorization code flow with PKCE. Configuration and refresh logic reside in [`src/lib/oauth/providers/claude.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/providers/claude.ts).

- **Codex**: Requires an extra **`workspace_id`** field for multi-tenant accounts. The implementation extracts this value from the JWT token after the initial exchange in [`src/lib/oauth/providers/codex.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/providers/codex.ts).

- **Cursor**: Supports both browser-based and **device-code flow**. Device-specific helpers are isolated in [`src/lib/oauth/services/cursor.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/services/cursor.ts).

- **Antigravity**: Mirrors the GitHub Copilot OAuth implementation and supports **client-credentials** grants for server-side authentication in addition to the standard user flow, defined in [`src/lib/oauth/providers/antigravity.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/providers/antigravity.ts).

All providers register their `authType: "oauth"` entry in [`src/shared/constants/providers/oauth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/shared/constants/providers/oauth.ts), which drives the dashboard UI, risk-notice rendering, and resilience settings.

## Implementation Examples

### Initiating the OAuth Flow

From the dashboard or client application, request an authorization URL:

```javascript
// Request login URL for Claude Code
const response = await fetch('/api/oauth/claude/authorize');
const { url } = await response.json();
// Returns: https://auth.anthropic.com/oauth/authorize?...PKCE...

// Open provider login in new window
window.open(url, '_blank');

```

### Handling the OAuth Callback

After provider authentication, capture the redirect parameters and exchange for tokens:

```javascript
// Extract parameters from provider redirect
const params = new URLSearchParams(window.location.search);
const code = params.get('code');
const state = params.get('state');

// Exchange code for access token
await fetch('/api/oauth/claude/exchange', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ code, state })
});

```

### Making Authenticated Requests

Use the stored OAuth credential without manual token handling:

```javascript
await fetch('/api/v1/chat/completions', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-omniroute-provider': 'claude',
    // Authorization header injected automatically by executor
  },
  body: JSON.stringify({
    model: 'claude-3-5-sonnet',
    messages: [{ role: 'user', content: 'Explain OAuth flows' }]
  })
});

```

### Revoking Credentials

Remove provider access and invalidate stored tokens:

```javascript
await fetch('/api/oauth/claude/revoke', { method: 'POST' });
// Clears credential blob and marks connection revoked

```

## Key Source Files Reference

| File | Responsibility |
|------|----------------|
| `src/app/api/oauth/[provider]/[action]/route.ts` | Centralized Next.js route implementing **authorize**, **callback**, **exchange**, and **revoke** actions for all 17+ providers. |
| [`src/lib/oauth/providers.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/providers.ts) | Shared `generateAuthData` helper for PKCE generation, state creation, and URL construction. |
| `src/lib/oauth/providers/*.ts` | Provider-specific implementations including [`antigravity.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/antigravity.ts), [`claude.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/claude.ts), [`codex.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/codex.ts), and [`cursor.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/cursor.ts) for token endpoints, scopes, and special fields. |
| [`src/lib/oauth/credentialBlob.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/credentialBlob.ts) | Serialization and encryption logic for storing OAuth tokens in the database. |
| [`src/lib/oauth/connectionPersistence.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/connectionPersistence.ts) | Retrieval and decryption of credentials during request execution. |
| [`src/shared/constants/providers/oauth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/shared/constants/providers/oauth.ts) | Canonical registry of OAuth providers with metadata for UI rendering and risk classification. |
| [`src/lib/resilience/settings.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/resilience/settings.ts) | Circuit-breaker and retry policies specific to the `oauth` auth category. |
| [`src/open-sse/executors/default.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/open-sse/executors/default.ts) | Request executor that retrieves credentials and injects `Authorization` headers. |

## Summary

- **Unified architecture**: All 17+ OAuth providers (Claude Code, Codex, Cursor, Antigravity, etc.) route through a single generic handler in `src/app/api/oauth/[provider]/[action]/route.ts`.
- **PKCE security**: Every flow uses PKCE with cryptographically random verifiers and state tokens generated in [`src/lib/oauth/providers.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/providers.ts).
- **Encrypted storage**: Tokens are serialized into encrypted blobs via [`src/lib/oauth/credentialBlob.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/credentialBlob.ts) and stored with `authType: "oauth"`.
- **Automatic lifecycle management**: OmniRoute handles token injection, background refresh via provider-specific functions like `refreshAccessToken`, and revocation through standardized endpoints.
- **Provider extensibility**: New OAuth providers are added by creating a file in `src/lib/oauth/providers/*.ts` and registering the `authType` in [`src/shared/constants/providers/oauth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/shared/constants/providers/oauth.ts).

## Frequently Asked Questions

### How does OmniRoute securely store OAuth tokens?

OmniRoute wraps access and refresh tokens in a credential blob using `writeCredentialBlob` in [`src/lib/oauth/credentialBlob.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/credentialBlob.ts), which applies at-rest encryption via [`src/lib/db/encryption.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/encryption.ts) before persisting to the connections database. The executor retrieves and decrypts these tokens server-side only when building upstream requests, ensuring credentials never reach the client browser.

### What is the difference between the callback and exchange endpoints?

The **callback** endpoint (`/api/oauth/<provider>/callback`) receives the redirect from the OAuth provider, validates the `state` parameter to prevent CSRF attacks, and temporarily stores the authorization code. The **exchange** endpoint (`/api/oauth/<provider>/exchange`) is subsequently called by the client to swap the code (along with the PKCE verifier) for actual access tokens via the provider-specific `exchangeToken` implementation.

### Does OmniRoute support automatic token refresh for all providers?

Yes, provided the OAuth provider issues refresh tokens. Before making an upstream request, the executor checks token expiration and automatically calls provider-specific refresh functions like `refreshAccessToken` in [`src/lib/oauth/providers/claude.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/providers/claude.ts) (line 78), updating the stored credential blob with new access tokens without user intervention.

### How can I add a new OAuth provider to OmniRoute?

Create a provider configuration file in `src/lib/oauth/providers/<provider>.ts` implementing `generateAuthData`, `exchangeToken`, and optional `refreshAccessToken` functions. Register the provider metadata in [`src/shared/constants/providers/oauth.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/shared/constants/providers/oauth.ts) with `authType: "oauth"`. The generic route handler in `src/app/api/oauth/[provider]/[action]/route.ts` will automatically recognize the new provider and enable the full authorization flow.