Skip to content

features provider authentication

Nik edited this page May 30, 2026 · 2 revisions

Provider authentication

Active contributors: Nik, Ran

Purpose

DroidProxy uses your existing provider subscriptions instead of API keys. Auth tokens live as JSON files in ~/.cli-proxy-api/. Most providers authenticate through a browser OAuth flow driven by the bundled cli-proxy-api binary; Cursor is API-key based. Multiple accounts per provider are supported, with round-robin load balancing and per-account enable/disable.

Providers

Provider ServiceType Login flag Auth mechanism
Claude Code claude -claude-login Browser OAuth → token JSON
Codex codex -codex-login Browser OAuth → token JSON
Antigravity antigravity -antigravity-login Browser OAuth (requires Google Antigravity installed) → token JSON
Kimi kimi -kimi-login Browser OAuth → token JSON
Cursor cursor none API key written to cursor.json (beta-gated)

ServiceType(authFileType:) in src/Sources/AuthStatus.swift also maps the legacy gemini / gemini-cli type strings onto antigravity.

Key abstractions

Type Role
AuthCommand Enum (claudeLogin, codexLogin, antigravityLogin, kimiLogin) with a loginFlag computed property passed to cli-proxy-api.
ServerManager.runAuthCommand Launches cli-proxy-api --config <bundled config> <loginFlag>, captures output, and posts authDirectoryChanged on clean exit.
AuthManager Scans ~/.cli-proxy-api/, parses each JSON into AuthAccount, and publishes serviceAccounts keyed by ServiceType.
AuthAccount One account: id (filename), email/login, type, expired, filePath, isDisabled.
AuthDirectoryMonitor Debounced DispatchSource watcher on ~/.cli-proxy-api that fires onChange when files are written, deleted, or renamed.
ServiceRow / AccountRowView SwiftUI rows for a provider and its accounts, with Add Account, Enable/Disable, and Remove controls.

How it works

Login flow

sequenceDiagram
    participant U as User
    participant SV as SettingsView
    participant SM as ServerManager
    participant CLI as cli-proxy-api
    participant B as Browser
    participant FS as ~/.cli-proxy-api
    participant M as AuthDirectoryMonitor
    participant AM as AuthManager

    U->>SV: Click Add Account
    SV->>SV: connectService(serviceType)
    SV->>SM: runAuthCommand(.claudeLogin / .codexLogin / ...)
    SM->>CLI: launch with -<provider>-login
    CLI->>B: open OAuth URL
    U->>B: complete login
    B->>CLI: redirect / callback
    CLI->>FS: write token JSON
    CLI-->>SM: exit 0
    SM->>M: post authDirectoryChanged
    FS-->>M: filesystem write event
    M->>AM: onChange (debounced)
    AM->>AM: checkAuthStatus()
    AM-->>SV: publish serviceAccounts (UI refresh)
Loading

connectService short-circuits for Cursor: it shows an alert and calls saveCursorApiKey, which writes cursor.json (with type: "cursor", the key, and disabled: false) and sets 0o600 permissions. For Codex, runAuthCommand writes a newline to the process after ~12s so the login keeps waiting on its callback instead of blocking on the manual prompt.

Multiple accounts, round-robin, and failover

Each provider may hold several account files. The backend config (src/Sources/Resources/config.yaml) sets routing.strategy: round-robin with session-affinity: true (TTL 2h), so a session pins to one upstream auth while new sessions spread across accounts. disable-cooling: true keeps a single transient failure from blacking out an auth.

Per-account enable/disable and removal

  • AuthManager.toggleAccountDisabled flips the disabled field in the account's JSON, but refuses to disable the last enabled account for a provider.
  • disconnectAccount (in SettingsView) stops the server, deletes the file via AuthManager.deleteAccount, then restarts the server if it was running.
  • The ServiceRow "connected accounts" summary shows "Round-robin w/ auto-failover" when more than one account is enabled, and expands to show per-account Enable/Disable and Remove controls.

Provider-level enable/disable

Separate from per-account flags, the provider toggle calls ServerManager.setProviderEnabled, which writes oauth-excluded-models into ~/.cli-proxy-api/merged-config.yaml. CLIProxyAPI hot-reloads, so no restart is needed. See Server manager.

Integration points

  • src/Sources/ServerManager.swift owns runAuthCommand, the provider-enable config writing, and the merged-config generation.
  • src/Sources/AuthDirectoryMonitor.swift is used by both AppDelegate and SettingsView to refresh on auth changes.
  • src/Sources/OAuthUsageTracker.swift reads Codex/Claude OAuth quota windows for the usage dashboard.

Entry points for modification

  • Add a provider: extend ServiceType and ServiceType(authFileType:) in src/Sources/AuthStatus.swift, add a case + loginFlag to AuthCommand in src/Sources/ServerManager.swift, map it in oauthProviderKeys, and wire a providerServiceRow in src/Sources/SettingsView.swift.
  • Change the auth directory: edit src/Sources/AuthPaths.swift (single source of truth).
  • Adjust login timing/quirks: see runAuthCommand in src/Sources/ServerManager.swift.

Key source files

File Role
src/Sources/ServerManager.swift runAuthCommand, AuthCommand, provider enable/disable, merged config.
src/Sources/AuthStatus.swift ServiceType, AuthAccount, AuthManager scanning and account mutations.
src/Sources/SettingsView.swift connectService, saveCursorApiKey, successMessage, ServiceRow, AccountRowView.
src/Sources/AuthDirectoryMonitor.swift Debounced filesystem watcher on ~/.cli-proxy-api.
src/Sources/AuthPaths.swift Auth directory location.

Related

Clone this wiki locally