How to Implement BYOK Authentication with External LLM Providers in the Copilot SDK
The Copilot SDK enables Bring-Your-Own-Key (BYOK) authentication by accepting a bearerTokenProvider callback during session creation, which the runtime invokes via RPC to fetch fresh bearer tokens for external LLM endpoints like OpenAI or Anthropic.
The GitHub Copilot SDK allows developers to integrate external large language model (LLM) providers using their own API keys rather than GitHub-hosted models. This BYOK authentication pattern lets you mix CAPI models with custom providers in the same session while maintaining full control over token acquisition through your identity system.
Configuring a BYOK Provider
To enable BYOK authentication, you declare a NamedProviderConfig that includes a bearerTokenProvider callback and registers it in the session options. The SDK expects this configuration alongside your model definitions to map specific model IDs to your custom provider.
The configuration requires four key fields:
- name: A unique identifier for your provider (e.g.,
"myprovider") used to qualify model selections - type: The provider family such as
"openai"or"anthropic"that matches runtime expectations - wireApi: The API shape expected (e.g.,
"completions") - bearerTokenProvider: An async callback function that returns a fresh token string
When you create a session, the SDK processes these configurations through extractBearerTokenProviders in src/client.ts (lines 89-107). This function strips the non-serializable callback from the wire payload and sets hasBearerTokenProvider: true to signal the runtime that it must request tokens dynamically.
Implementing the Token Provider Callback
The BearerTokenProvider callback receives a ProviderTokenArgs object defined in src/types.ts (lines 2674-2679) containing:
providerName: The name of the provider requiring authenticationsessionId: The unique session identifier for scoping or logging
Your implementation can integrate with any identity library such as Azure Identity, AWS STS, or a custom OAuth flow. The callback must return the raw token string without the "Bearer " prefix, as the runtime handles header construction.
import { BearerTokenProvider } from "@github/copilot-sdk";
const getMyProviderToken: BearerTokenProvider = async (args) => {
// args.providerName tells you which provider needs the token
// args.sessionId can be used for caching or audit logging
const token = await fetchTokenFromIdentityProvider();
return token; // Raw token string only
};
Runtime Token Request Flow
For every outbound request to a BYOK model, the runtime invokes the SDK-side callback through the providerToken.getToken RPC defined in src/generated/rpc.ts (lines 11677-11694). The session registers your callbacks via registerBearerTokenProviders in src/session.ts, creating a mapping that the RPC handler can invoke.
Critical behavior: The runtime does not cache tokens. Your bearerTokenProvider is called for every single request, ensuring fresh credentials but requiring your callback to handle any necessary caching or token refresh logic internally.
Once the callback returns the token, the runtime injects it as the Authorization: Bearer <token> header on the HTTP request to the external LLM endpoint.
Complete Implementation Example
This example demonstrates registering a custom OpenAI-compatible provider alongside GitHub-hosted models:
import {
CopilotClient,
BearerTokenProvider,
NamedProviderConfig,
ProviderModelConfig,
} from "@github/copilot-sdk";
// Define the token acquisition logic using your identity system
const getMyProviderToken: BearerTokenProvider = async (args) => {
const token = await fetchMyAzureToken("https://my-llm.example.com/.default");
return token; // No "Bearer " prefix
};
// Configure the BYOK provider
const myProvider: NamedProviderConfig = {
name: "myprovider",
type: "openai",
wireApi: "completions",
baseUrl: "https://my-llm.example.com/v1",
bearerTokenProvider: getMyProviderToken,
};
// Define a model that uses the BYOK provider
const myModel: ProviderModelConfig = {
id: "gpt-4o",
provider: "myprovider",
wireModel: "byok-gpt-4o",
};
// Create a session mixing CAPI and BYOK models
const client = new CopilotClient();
const session = await client.createSession({
providers: [myProvider], // Register BYOK provider
models: [myModel], // Register BYOK model
model: "myprovider/gpt-4o", // Select with provider-qualified ID
onPermissionRequest: approveAll,
});
// Execute a turn; the SDK calls getMyProviderToken before the LLM request
await session.sendAndWait({ prompt: "Explain quantum entanglement." });
Key Implementation Details
When implementing BYOK authentication in the Copilot SDK, keep these technical specifics in mind:
- Provider-qualified model IDs: Reference BYOK models using the format
"providerName/modelId"(e.g.,"myprovider/gpt-4o") as defined insrc/types.ts(lines 2250-2252) - Wire flag mechanism: The
hasBearerTokenProviderboolean flag travels across the wire while the actual callback remains SDK-side, ensuring the runtime knows to request tokens without serializing functions - No runtime caching: The
providerToken.getTokenRPC invokes your callback for every request; implement caching in your callback if your identity provider has rate limits - End-to-end testing: Reference
test/e2e/byok_bearer_token_provider.e2e.test.tsfor a complete working example including verification of theAuthorizationheader injection
Summary
- BYOK authentication lets you use external LLM providers with the Copilot SDK by supplying your own bearer tokens
- Configure providers using
NamedProviderConfigwith abearerTokenProvidercallback - The callback receives
ProviderTokenArgscontainingproviderNameandsessionIdviasrc/types.ts - The runtime requests tokens through the
providerToken.getTokenRPC before each LLM request with no caching - Reference models using provider-qualified IDs like
"myprovider/gpt-4o"to mix CAPI and BYOK endpoints
Frequently Asked Questions
How does the SDK handle the non-serializable callback during RPC?
The SDK invokes extractBearerTokenProviders in src/client.ts (lines 89-107) to remove the bearerTokenProvider function from the configuration object before serialization. It replaces the callback with hasBearerTokenProvider: true and stores the callback locally in a map keyed by provider name, allowing the runtime to request tokens via RPC without transmitting function code.
Can I reuse the same token across multiple requests in a single session?
No. The Copilot SDK runtime calls your bearerTokenProvider callback for every outbound request to the external LLM without implementing any token caching. If your identity provider issues long-lived tokens or you want to avoid excessive STS calls, implement caching logic inside your callback function using the sessionId or providerName arguments as cache keys.
What identity providers are compatible with BYOK authentication?
Any identity provider that can return a bearer token string works with the BYOK pattern. The SDK itself is provider-agnostic—you can use Azure Identity (DefaultAzureCredential), AWS STS, Google Cloud IAM, or a custom OAuth 2.0 flow. The only requirement is that your callback returns the raw token value (without the "Bearer " prefix) to ProviderTokenArgs as defined in src/types.ts.
Can I mix GitHub-hosted models and BYOK models in the same session?
Yes. You can register both CAPI models and BYOK models in the same session by including GitHub-hosted model IDs alongside your custom ProviderModelConfig entries. Use the provider-qualified selection ID (e.g., "myprovider/gpt-4o") to route specific turns to your external provider while using standard IDs like "gpt-4o" for GitHub-hosted models.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →