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

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 (line 134), which constructs a cryptographically random state token, generates a codeVerifier for PKCE, and builds the provider-specific authorization URL.

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 (line 45) or equivalent functions in codex.ts, cursor.ts, and 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. The writeCredentialBlob function in 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) calls getProviderCredentials from 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 (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.

  • 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.

  • Cursor: Supports both browser-based and device-code flow. Device-specific helpers are isolated in 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.

All providers register their authType: "oauth" entry in 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:

// 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:

// 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:

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:

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 Shared generateAuthData helper for PKCE generation, state creation, and URL construction.
src/lib/oauth/providers/*.ts Provider-specific implementations including antigravity.ts, claude.ts, codex.ts, and cursor.ts for token endpoints, scopes, and special fields.
src/lib/oauth/credentialBlob.ts Serialization and encryption logic for storing OAuth tokens in the database.
src/lib/oauth/connectionPersistence.ts Retrieval and decryption of credentials during request execution.
src/shared/constants/providers/oauth.ts Canonical registry of OAuth providers with metadata for UI rendering and risk classification.
src/lib/resilience/settings.ts Circuit-breaker and retry policies specific to the oauth auth category.
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.
  • Encrypted storage: Tokens are serialized into encrypted blobs via 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.

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, which applies at-rest encryption via 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 (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 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →