How Dual-Auth Provider Handling Works in `provider-listing.ts`

Dual-auth providers in provider-listing.ts are discovered dynamically from SDK definitions and split into two separate lists—API-key and OAuth—with cross-flags (supportsOAuth/supportsApiKey) enabling UI toggles between authentication methods.

The provider-listing.ts module in the agegr/pi-web repository powers the Models panel's provider selection interface. It processes providers that support multiple authentication methods—such as Anthropic, GitHub Copilot, Kimi-coding, OpenRouter, Radius, and XAI—and ensures each appears correctly without duplicate entries.

How Dual-Auth Capability Is Discovered

A provider qualifies as dual-auth when it declares both hasApiKeyLogin and hasOAuth in its SDK definition. The module reads this directly from ProviderListingInput objects rather than inferring from hard-coded IDs.

"Which providers are dual-auth is a property of the SDK's provider definitions and changes between releases — read it from auth, never assume it from an id." — Source comment in [/lib/provider-listing.ts](https://github.com/agegr/pi-web/blob/main/lib/provider-listing.ts#L4-L11)

Each input contains:

  • hasApiKeyLogin – Boolean flag for API-key support
  • hasOAuth – Boolean flag for OAuth support
  • credentialType – Currently stored credential type ("api_key" | "oauth")
  • status.configured – Whether authentication is active
  • modelCount and display metadata

Deduplication of Provider Entries

The same provider may appear multiple times in raw SDK output. The dedupeById helper preserves first occurrence while removing duplicates:

function dedupeById(providers: readonly ProviderListingInput[]): ProviderListingInput[] {
  const seen = new Set<string>();
  const result: ProviderListingInput[] = [];
  for (const provider of providers) {
    if (seen.has(provider.id)) continue;
    seen.add(provider.id);
    result.push(provider);
  }
  return result;
}

Source: /lib/provider-listing.ts#L62-L70

Building the API-Key Provider List

The buildApiKeyProviderList function constructs entries for the API-key tab with specific dual-auth logic:

  • Skips providers without hasApiKeyLogin
  • Excludes custom models.json providers (handled separately)
  • Marks as unconfigured when credentialType === "oauth"—preventing OAuth-authenticated dual-auth providers from appearing active in both lists
  • Adds supportsOAuth flag when hasOAuth is true
if (!provider.hasApiKeyLogin) continue;
if (provider.status.source && CUSTOM_PROVIDER_SOURCES.has(provider.status.source)) continue;
const configured = provider.status.configured && provider.credentialType !== "oauth";
result.push({
  id: provider.id,
  displayName: provider.name,
  configured,
  ...(configured && provider.status.source ? { source: provider.status.source } : {}),
  modelCount: provider.modelCount,
  supportsOAuth: provider.hasOAuth,
});

Source: /lib/provider-listing.ts#L81-L99

Building the OAuth Provider List

The buildOAuthProviderList function handles the OAuth tab:

  • Includes only providers with hasOAuth
  • Uses friendly display names from OAUTH_DISPLAY_NAMES mapping or falls back to oauthName/name
  • Sets loggedIn when credentialType === "oauth"
  • Adds supportsApiKey flag for dual-auth detection
if (!provider.hasOAuth) continue;
result.push({
  id: provider.id,
  name: OAUTH_DISPLAY_NAMES[provider.id] ?? provider.oauthName ?? provider.name,
  usesCallbackServer: false,
  loggedIn: provider.credentialType === "oauth",
  supportsApiKey: provider.hasApiKeyLogin,
});

Source: /lib/provider-listing.ts#L102-L117

Complete Implementation Example

import {
  buildApiKeyProviderList,
  buildOAuthProviderList,
  ProviderListingInput,
} from "./provider-listing";

const rawProviders: ProviderListingInput[] = [
  {
    id: "anthropic",
    name: "Anthropic",
    hasApiKeyLogin: true,
    hasOAuth: true,
    oauthName: "Anthropic (Claude Pro/Max)",
    status: { configured: true },
    credentialType: "oauth",
    modelCount: 5,
  },
  // additional providers...
];

const apiKeyList = buildApiKeyProviderList(rawProviders);
const oauthList = buildOAuthProviderList(rawProviders);

console.log(apiKeyList);
// Anthropic appears with configured: false, supportsOAuth: true

console.log(oauthList);
// Anthropic appears with loggedIn: true, supportsApiKey: true
File Purpose
lib/provider-listing.ts Core dual-auth detection and list building
lib/provider-listing-runtime.ts SDK data fetching wrapper
lib/provider-credential-store.ts Credential storage and credentialType resolution
components/models-config-helpers.ts UI rendering with authentication toggles

Summary

  • Dual-auth providers are identified by SDK flags (hasApiKeyLogin + hasOAuth), never hard-coded IDs
  • dedupeById eliminates duplicate provider entries before list construction
  • supportsOAuth and supportsApiKey flags enable UI toggles between authentication methods
  • OAuth-active providers appear unconfigured in the API-key list to prevent duplication
  • The architecture supports future SDK providers automatically without code changes

Frequently Asked Questions

How does provider-listing.ts know if a provider supports both authentication methods?

The module reads hasApiKeyLogin and hasOAuth boolean flags directly from each ProviderListingInput object supplied by the SDK. These flags are defined in the provider's authentication schema, making detection dynamic rather than based on maintained ID lists.

What prevents a dual-auth provider from appearing twice in the UI?

The credentialType field determines active authentication. When credentialType === "oauth", the buildApiKeyProviderList function sets configured: false for that provider, so it only appears active in the OAuth list. The UI renders one row per provider with toggle capabilities instead of duplicate entries.

Can custom providers defined in models.json appear in these lists?

No. The CUSTOM_PROVIDER_SOURCES Set filters out providers with status.source matching custom entries. These are rendered separately in the UI through different components, keeping the built-in provider lists clean.

What happens when the SDK adds a new dual-auth provider?

No code changes are required. Since provider-listing.ts reads authentication capabilities from SDK-provided flags rather than hard-coded logic, new dual-auth providers automatically receive correct supportsOAuth/supportsApiKey flags and proper list placement based on their credentialType.

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 →