# How to Implement Custom OAuth Providers with OmniRoute's Public Credential Embedding

> Learn how to implement custom OAuth providers with OmniRoute's public credential embedding. Follow our guide to create and register your own OAuth modules seamlessly.

- Repository: [Diego Rodrigues de Sa e Souza/OmniRoute](https://github.com/diegosouzapw/OmniRoute)
- Tags: how-to-guide
- Published: 2026-08-20

---

**Implement custom OAuth providers in OmniRoute by creating a provider module in `src/lib/oauth/providers/` that uses `resolvePublicCred` from `@omniroute/open-sse/utils/publicCreds.ts`, then register it in [`open-sse/config/providerRegistry.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/config/providerRegistry.ts).**

OmniRoute centralizes every LLM and provider interaction through a **provider registry**, making it straightforward to add new OAuth-based providers while keeping credentials secure. This guide walks through the complete implementation using OmniRoute's public credential embedding system, which satisfies Hard Rule #11—no literal secrets in source code.

## Creating a New OAuth Provider Module

Every custom OAuth provider starts with a dedicated file under `src/lib/oauth/providers/`. The key requirement is using the `resolvePublicCred` helper to handle credentials safely.

### The `resolvePublicCred` Pattern

In [`open-sse/utils/publicCreds.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/utils/publicCreds.ts), the `resolvePublicCred` function reads a default credential bundled in the repository while allowing an environment variable to override it at runtime. This is the core mechanism for public credential embedding.

```typescript
// src/lib/oauth/providers/my-provider.ts
import { resolvePublicCred } from "@omniroute/open-sse/utils/publicCreds.ts";

export const CONFIG = {
  // Resolves from embedded default OR environment variable MYPROV_OAUTH_CLIENT_ID
  clientIdDefault: resolvePublicCred("myprov_id", "MYPROV_OAUTH_CLIENT_ID"),
  // Resolves from embedded default OR environment variable MYPROV_OAUTH_CLIENT_SECRET
  clientSecretDefault: resolvePublicCred("myprov_secret", "MYPROV_OAUTH_CLIENT_SECRET"),
  tokenUrl: "https://auth.myprovider.com/oauth/token",
  authUrl: "https://auth.myprovider.com/oauth/authorize",
  scopes: ["openid", "email", "profile"],
};

```

The first argument (`"myprov_id"`) references an embedded default value. The second argument (`"MYPROV_OAUTH_CLIENT_ID"`) names the environment variable that can override it.

## Registering Your Provider in the Registry

After creating the provider module, expose it through [`open-sse/config/providerRegistry.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/config/providerRegistry.ts). The registry ties together the provider's **type**, **executor**, and supported models.

```typescript
// open-sse/config/providerRegistry.ts (excerpt)
import { CONFIG as MYPROV_CONFIG } from "@/lib/oauth/providers/my-provider.ts";

export const REGISTRY = {
  // …existing providers
  myprovider: {
    type: "oauth",
    config: MYPROV_CONFIG,
    // Models fetched lazily—static list or remote JSON
    modelsUrl: "https://api.myprovider.com/v1/models",
    // Reuse "default" executor for OpenAI-compatible APIs, or create custom
    executor: "default",
  },
} as const;

```

The **type** must be `"oauth"` for all OAuth-based providers. The **executor** field typically uses `"default"` for providers with standard OpenAI-compatible API surfaces—only specify a custom executor for non-standard implementations.

## Handling Non-Standard OAuth Flows

Most providers reuse the generic OAuth flow located in [`src/lib/oauth/services/default.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/services/default.ts). If your provider has quirks (non-standard token response, custom refresh logic, or unusual scope handling), create a service file implementing the `OAuthService` interface:

1. Add the service under [`src/lib/oauth/services/myprovider.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/services/myprovider.ts)
2. Export it from [`src/lib/oauth/providers/index.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/providers/index.ts)
3. Reference it in your registry entry with `executor: "myprovider"`

For the majority of providers, the default flow works unchanged and requires no additional service code.

## Configuring Model Discovery

OmniRoute's **auto-combo router** automatically discovers and exposes models on the `/v1/models` endpoint. You have two options for model configuration:

- **Static list**: Point `modelsUrl` to a JSON file in your repository
- **Dynamic fetch**: Point `modelsUrl` to your provider's remote model endpoint

If your provider doesn't support model listing, omit `modelsUrl` and the system will use fallback behavior.

## Complete Working Example

Here's a full implementation for a hypothetical "ExampleProvider":

```typescript
// src/lib/oauth/providers/example.ts
import { resolvePublicCred } from "@omniroute/open-sse/utils/publicCreds.ts";

export const CONFIG = {
  clientIdDefault: resolvePublicCred("example_id", "EXAMPLE_OAUTH_CLIENT_ID"),
  clientSecretDefault: resolvePublicCred("example_secret", "EXAMPLE_OAUTH_CLIENT_SECRET"),
  authUrl: "https://login.example.com/oauth/authorize",
  tokenUrl: "https://login.example.com/oauth/token",
  scopes: ["openid", "email"],
};

```

```typescript
// open-sse/config/providerRegistry.ts
import { CONFIG as EXAMPLE_CONFIG } from "@/lib/oauth/providers/example.ts";

export const REGISTRY = {
  example: {
    type: "oauth",
    config: EXAMPLE_CONFIG,
    modelsUrl: "https://api.example.com/v1/models",
    executor: "default",
  },
} as const;

```

Clients select your custom provider using the `provider` field in API requests:

```typescript
const response = await fetch("/v1/chat/completions", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    model: "example-gpt-4",
    messages: [{ role: "user", content: "Hello" }],
    provider: "example",
  }),
});

```

## Required Unit Tests

Every new OAuth provider must include tests under `tests/unit/` that verify:

- `resolvePublicCred` is used (no hard-coded literals)
- The provider entry exists in `REGISTRY`
- Model list loads correctly or falls back gracefully

```typescript
// tests/unit/myprovider.test.ts
import { REGISTRY } from "../../open-sse/config/providerRegistry.ts";

test("myprovider is registered", () => {
  const entry = REGISTRY.myprovider;
  assert.ok(entry, "myprovider must be defined");
});

test("clientId resolves via resolvePublicCred", async () => {
  const { resolvePublicCred } = await import("@omniroute/open-sse/utils/publicCreds.ts");
  const id = resolvePublicCred("myprov_id", "MYPROV_OAUTH_CLIENT_ID");
  assert.match(id, /^[a-z0-9_-]+$/i);
});

```

## Key Files Reference

| File | Purpose |
|------|---------|
| [`open-sse/utils/publicCreds.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/utils/publicCreds.ts) | Core helper for credential resolution with environment overrides |
| [`open-sse/config/providerRegistry.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/config/providerRegistry.ts) | Central registry mapping provider keys to configuration |
| [`src/lib/oauth/services/default.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/oauth/services/default.ts) | Generic OAuth flow implementation |
| `src/lib/oauth/providers/` | Directory for new provider definitions |

## Summary

- Use **`resolvePublicCred`** from `@omniroute/open-sse/utils/publicCreds.ts` for all OAuth credentials—never hard-code secrets
- Place provider definitions in **`src/lib/oauth/providers/`** and export a `CONFIG` object
- Register providers in **[`open-sse/config/providerRegistry.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/config/providerRegistry.ts)** with type `"oauth"`, proper config reference, and executor selection
- Reuse the **default executor** for OpenAI-compatible APIs; create custom services only for non-standard behaviors
- Write **unit tests** verifying credential resolution and registry registration

## Frequently Asked Questions

### What is public credential embedding in OmniRoute?

Public credential embedding is OmniRoute's security pattern where default OAuth credentials are bundled in the repository but can be overridden via environment variables at runtime. The `resolvePublicCred` function in [`open-sse/utils/publicCreds.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/utils/publicCreds.ts) implements this pattern, ensuring no literal secrets appear in source code while maintaining deploy-time flexibility.

### When do I need a custom OAuth executor versus using "default"?

Use **`executor: "default"`** when your provider follows standard OAuth 2.0 flows and OpenAI-compatible API responses. Create a custom executor in `src/lib/oauth/services/` only when your provider has non-standard behaviors—such as custom token response formats, unusual refresh token handling, or proprietary authentication extensions.

### How does `resolvePublicCred` prevent secrets from leaking?

`resolvePublicCred` takes two arguments: a key for an embedded default value and an environment variable name. The embedded default can be a placeholder or public value, while the actual secret is injected at runtime via environment variables. This satisfies OmniRoute Hard Rule #11 by ensuring production secrets never appear in git history or source code.

### Can I use environment variables exclusively without embedded defaults?

Yes. While the pattern shows embedded defaults, you can structure your `resolvePublicCred` calls so that the embedded value is a non-functional placeholder (empty string or "REPLACE_ME"), forcing runtime configuration through environment variables only. The function will always prefer the environment variable when present.