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

> Understand how Pi-Web configures models, handles provider authentication, and manages dual-auth providers using SDK metadata. Learn how credentials determine API Key or OAuth placement.

- Repository: [Alex Yang/pi-web](https://github.com/agegr/pi-web)
- Tags: deep-dive
- Published: 2026-08-16

---

**The Pi‑Web codebase stores model definitions in [`models.json`](https://github.com/agegr/pi-web/blob/main/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`](https://github.com/agegr/pi-web/blob/main/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`](https://github.com/agegr/pi-web/blob/main/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`](https://github.com/agegr/pi-web/blob/main/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`](https://github.com/agegr/pi-web/blob/main/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`](https://github.com/agegr/pi-web/blob/main/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.

```typescript
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.

```typescript
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`](https://github.com/agegr/pi-web/blob/main/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`](https://github.com/agegr/pi-web/blob/main/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:

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

```typescript
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`](https://github.com/agegr/pi-web/blob/main/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`](https://github.com/agegr/pi-web/blob/main/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`](https://github.com/agegr/pi-web/blob/main/lib/provider-listing.ts) construct deduplicated, flag‑annotated provider objects
- **Dual‑auth flags** (`supportsOAuth`, `supportsApiKey`) enable UI indication of alternative methods
- **[`ModelsConfig.tsx`](https://github.com/agegr/pi-web/blob/main/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`](https://github.com/agegr/pi-web/blob/main/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`](https://github.com/agegr/pi-web/blob/main/auth.json).

### Why are custom providers from [`models.json`](https://github.com/agegr/pi-web/blob/main/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.