How Model Configuration Works with Provider Authentication and Dual‑Auth Provider Handling in Pi‑Web

The Pi‑Web codebase stores model definitions in models.json and dynamically builds provider lists by querying SDK authentication metadata, ensuring dual‑auth providers appear exactly once—either under API Key or OAuth based on the stored credential type.

Pi‑Web decouples model configuration from hard‑coded provider logic. Instead, it leverages runtime detection of provider capabilities to render clean, deduplicated authentication options. This article explains the complete flow from credential storage to UI rendering, referencing the actual implementation in the agegr/pi-web repository.


Understanding the Credential Type System

Pi‑Web records every authentication entry in auth.json with an explicit type discriminator. The ProviderCredentialType union distinguishes between api_key and oauth credentials at line 15 of the auth store. This type tag drives all downstream provider listing logic.

When the application initializes, the SDK exposes provider metadata including hasApiKeyLogin and hasOAuth booleans. Rather than maintaining a static list of which providers support both methods, Pi‑Web interrogates these runtime flags to detect dual‑auth capability.


Detecting Dual‑Auth Providers at Runtime

The provider-listing.ts module consumes raw provider data from the SDK's auth object to determine authentication flexibility. Providers such as Anthropic, GitHub Copilot, Kimi‑Coding, OpenRouter, Radius, and XAI expose both API‑key and OAuth endpoints.

The detection logic resides in lib/provider-listing.ts and evaluates the hasApiKeyLogin and hasOAuth properties without hard‑coding provider IDs. This ensures new dual‑auth providers automatically surface both options without code changes.


Building the API Key Provider List

The buildApiKeyProviderList function filters and transforms raw provider data into UI‑ready objects. Located at lines 73–99 of lib/provider-listing.ts, this function applies three critical filters:

  • API‑key capability: Only providers with hasApiKeyLogin === true are retained
  • Exclusion of custom sources: Providers defined solely in models.json via CUSTOM_PROVIDER_SOURCES are filtered out
  • OAuth deduplication: Providers already authenticated via OAuth are omitted from this list entirely

The resulting ApiKeyProviderListing objects include a supportsOAuth boolean flag. This allows the UI to indicate when a provider offers an alternative authentication method even while displaying it in the API Key tab.

import { buildApiKeyProviderList } from '@/lib/provider-listing';

// rawProviders: SDK response with auth metadata
const apiKeyProviders = buildApiKeyProviderList(rawProviders);

// Each entry knows if OAuth is also available
interface ApiKeyProviderListing {
  id: string;
  displayName: string;
  configured: boolean;
  supportsOAuth: boolean;  // true for dual-auth providers
}

Building the OAuth Provider List

Complementing the API‑key list, buildOAuthProviderList (lines 102–117) constructs the OAuth tab contents. This function:

  1. Retains providers where hasOAuth === true
  2. Creates OAuthProviderListing objects with parallel structure
  3. Attaches a supportsApiKey flag for dual‑auth awareness

The symmetry between these two builder functions ensures consistent behavior: a dual‑auth provider authenticated via OAuth appears exclusively in the OAuth list, with UI cues indicating the API‑key alternative.

import { buildOAuthProviderList } from '@/lib/provider-listing';

const oauthProviders = buildOAuthProviderList(rawProviders);

interface OAuthProviderListing {
  id: string;
  displayName: string;
  configured: boolean;
  supportsApiKey: boolean;  // true for dual-auth providers
}

Runtime Adapter Layer

The lib/provider-listing-runtime.ts module bridges static provider definitions with live SDK state. It transforms ModelRuntime authentication status into the format expected by the listing builders. This separation allows the core logic to remain pure and testable while the runtime layer handles SDK‑specific quirks.


UI Rendering in ModelsConfig.tsx

The components/ModelsConfig.tsx component consumes both provider lists and renders them as "API Key" and "OAuth" tabs. The deduplication guarantees each provider appears exactly once—under the tab matching its stored credential type.

For dual‑auth providers, the supportsOAuth and supportsApiKey flags drive conditional UI elements:

// Inside ModelsConfig component
{apiKeyProviders.map(provider => (
  <ProviderRow
    key={provider.id}
    name={provider.displayName}
    configured={provider.configured}
  >
    {provider.supportsOAuth && (
      <SwitchButton 
        label="Use OAuth instead"
        onClick={() => switchAuth(provider.id, true)}
      />
    )}
  </ProviderRow>
))}

Switching Authentication Methods

Users may migrate between authentication types for dual‑auth providers. The switch operation triggers respective API endpoints and refreshes both provider lists:

async function switchAuth(providerId: string, toOAuth: boolean): Promise<void> {
  const endpoint = toOAuth 
    ? `/api/auth/oauth/${providerId}/login`
    : `/api/auth/api-key/${providerId}/login`;
    
  await fetch(endpoint, { method: 'POST' });
  
  // Re-fetch both lists to reflect credential change
  await refreshProviderLists();
}

The entry automatically migrates between tabs upon successful authentication, maintaining the invariant that each provider appears only once.


API Endpoint Integration

The app/api/auth/all-providers/route.ts endpoint aggregates both provider lists for frontend consumption. This centralizes authentication state and ensures the UI receives consistent, deduplicated data in a single request.


Summary

  • Credential typing in auth.json distinguishes api_key from oauth using ProviderCredentialType
  • Runtime detection of hasApiKeyLogin and hasOAuth properties eliminates hard‑coded dual‑auth lists
  • buildApiKeyProviderList and buildOAuthProviderList in lib/provider-listing.ts construct deduplicated, flag‑annotated provider objects
  • Dual‑auth flags (supportsOAuth, supportsApiKey) enable UI indication of alternative methods
  • ModelsConfig.tsx renders clean tabbed interfaces with one entry per provider regardless of dual‑auth capability
  • Authentication switching updates credential type and triggers list refresh, moving the entry between tabs

Frequently Asked Questions

How does Pi‑Web prevent duplicate provider entries when both API key and OAuth are supported?

Pi‑Web filters providers by their stored credential type rather than their capabilities. A provider authenticated via OAuth is excluded from the API‑key list entirely, appearing only in its matching tab. The alternative capability is surfaced via boolean flags without creating additional rows.

Where is the logic that determines if a provider supports both authentication methods?

The determination occurs in lib/provider-listing.ts by evaluating the SDK's hasApiKeyLogin and hasOAuth booleans. No hard‑coded provider list exists; the code dynamically responds to whatever authentication options the SDK exposes.

What happens when a user switches from API key to OAuth authentication?

The switchAuth function posts to the appropriate endpoint (/api/auth/oauth/{id}/login or /api/auth/api-key/{id}/login), then refreshes both provider lists. The provider entry migrates to the corresponding tab based on the new credential type stored in auth.json.

Why are custom providers from models.json excluded from the built‑in provider lists?

The buildApiKeyProviderList function filters against CUSTOM_PROVIDER_SOURCES to prevent collision between user‑defined model configurations and SDK‑managed provider authentication. Custom providers require separate handling outside the standard dual‑auth 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 →