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.
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 (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_idfield for multi-tenant accounts. The implementation extracts this value from the JWT token after the initial exchange insrc/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.tsand stored withauthType: "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/*.tsand registering theauthTypeinsrc/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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →