Skip to content

Commit bf30ae8

Browse files
Rename getBearerToken callback to bearerTokenProvider; fix precedence docs and CodeQL findings
Address post-merge review feedback from #1748 across all 6 SDKs: - Rename the BYOK token callback field getBearerToken/get_bearer_token/ GetBearerToken to bearerTokenProvider/bearer_token_provider/ BearerTokenProvider, and the callback type to BearerTokenProvider. The Provider suffix distinguishes the dynamic token source from the static bearerToken credential and aligns with the existing Rust trait and the SDK's *Provider value-producer precedent. In Java this also drops the double-get accessor (getGetBearerToken -> getBearerTokenProvider). - Fix docs that incorrectly described the callback and static apiKey/ bearerToken as mutually exclusive; the runtime applies precedence (the callback wins and the static credential is not sent). - Resolve 4 CodeQL findings: empty except in python; LINQ .Where filter, specific catch type, and HttpResponseMessage disposal in dotnet. Java was not build-verified locally (requires JDK 25); CI validates it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent c9626e6 commit bf30ae8

27 files changed

Lines changed: 219 additions & 200 deletions

dotnet/src/BearerTokenProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
namespace GitHub.Copilot;
88

99
/// <summary>
10-
/// Arguments passed to a bearer-token callback (the <c>GetBearerToken</c> property
10+
/// Arguments passed to a bearer-token callback (the <c>BearerTokenProvider</c> property
1111
/// on <see cref="ProviderConfig"/> / <see cref="NamedProviderConfig"/>) when the
1212
/// runtime needs a fresh bearer token for a BYOK provider.
1313
/// </summary>

dotnet/src/Client.cs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -671,26 +671,23 @@ private CopilotSession InitializeSession(
671671
private const string DefaultBearerTokenProviderName = "default";
672672

673673
/// <summary>
674-
/// Collects the per-provider <c>GetBearerToken</c> callbacks keyed by
674+
/// Collects the per-provider <c>BearerTokenProvider</c> callbacks keyed by
675675
/// provider name for session-side registration. The singular, whole-session
676676
/// <see cref="ProviderConfig"/> uses the implicit
677677
/// <see cref="DefaultBearerTokenProviderName"/>.
678678
/// </summary>
679679
private static Dictionary<string, Func<ProviderTokenArgs, Task<string>>> BuildBearerTokenCallbacks(SessionConfigBase config)
680680
{
681681
var callbacks = new Dictionary<string, Func<ProviderTokenArgs, Task<string>>>(StringComparer.Ordinal);
682-
if (config.Provider?.GetBearerToken is { } singular)
682+
if (config.Provider?.BearerTokenProvider is { } singular)
683683
{
684684
callbacks[DefaultBearerTokenProviderName] = singular;
685685
}
686686
if (config.Providers != null)
687687
{
688-
foreach (var provider in config.Providers)
688+
foreach (var provider in config.Providers.Where(provider => provider.BearerTokenProvider is not null))
689689
{
690-
if (provider.GetBearerToken is { } callback)
691-
{
692-
callbacks[provider.Name] = callback;
693-
}
690+
callbacks[provider.Name] = provider.BearerTokenProvider!;
694691
}
695692
}
696693
return callbacks;

dotnet/src/Session.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -871,7 +871,7 @@ internal void RegisterAutoModeSwitchHandler(Func<AutoModeSwitchRequest, AutoMode
871871
}
872872

873873
/// <summary>
874-
/// Registers per-provider <c>GetBearerToken</c> callbacks for BYOK
874+
/// Registers per-provider <c>BearerTokenProvider</c> callbacks for BYOK
875875
/// providers configured with managed-identity / on-demand bearer-token auth.
876876
/// </summary>
877877
/// <remarks>
@@ -899,7 +899,7 @@ internal void RegisterBearerTokenProviders(IReadOnlyDictionary<string, Func<Prov
899899

900900
/// <summary>
901901
/// Routes runtime <c>providerToken.getToken</c> requests to the matching
902-
/// per-provider <c>GetBearerToken</c> callback registered on the session.
902+
/// per-provider <c>BearerTokenProvider</c> callback registered on the session.
903903
/// </summary>
904904
private sealed class BearerTokenProviderHandler(CopilotSession session) : IProviderTokenHandler
905905
{

dotnet/src/Types.cs

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2044,26 +2044,27 @@ public sealed class ProviderConfig
20442044
public string? BearerToken { get; set; }
20452045

20462046
/// <summary>
2047-
/// Wire-only flag, emitted automatically when <see cref="GetBearerToken"/> is set, that tells
2047+
/// Wire-only flag, emitted automatically when <see cref="BearerTokenProvider"/> is set, that tells
20482048
/// the runtime to request a token over the session-scoped <c>providerToken.getToken</c> RPC
2049-
/// before each outbound request to this provider. Derived from <see cref="GetBearerToken"/>;
2049+
/// before each outbound request to this provider. Derived from <see cref="BearerTokenProvider"/>;
20502050
/// internal and never part of the public API.
20512051
/// </summary>
20522052
[JsonInclude]
20532053
[JsonPropertyName("hasBearerTokenProvider")]
2054-
internal bool? HasBearerTokenProvider => GetBearerToken is not null ? true : null;
2054+
internal bool? HasBearerTokenProvider => BearerTokenProvider is not null ? true : null;
20552055

20562056
/// <summary>
20572057
/// Per-request callback that resolves a bearer token on demand for this BYOK provider (for
20582058
/// example via Azure Managed Identity). The Copilot SDK takes no identity dependency: supply a
20592059
/// callback backed by your own identity library. Never serialized — setting it makes the SDK send
20602060
/// <c>hasBearerTokenProvider: true</c> on the wire and answer the runtime's
2061-
/// <c>providerToken.getToken</c> requests. Mutually exclusive with <see cref="ApiKey"/> and
2062-
/// <see cref="BearerToken"/>.
2061+
/// <c>providerToken.getToken</c> requests. When set alongside <see cref="ApiKey"/>/<see cref="BearerToken"/>, this callback takes precedence:
2062+
/// the runtime applies the token it returns as the Authorization: Bearer header for each request
2063+
/// and does not send the static credential.
20632064
/// </summary>
20642065
[JsonIgnore]
20652066
[Experimental(Diagnostics.Experimental)]
2066-
public Func<ProviderTokenArgs, Task<string>>? GetBearerToken { get; set; }
2067+
public Func<ProviderTokenArgs, Task<string>>? BearerTokenProvider { get; set; }
20672068

20682069
/// <summary>
20692070
/// Azure-specific configuration options.
@@ -2198,26 +2199,27 @@ public sealed class NamedProviderConfig
21982199
public string? BearerToken { get; set; }
21992200

22002201
/// <summary>
2201-
/// Wire-only flag, emitted automatically when <see cref="GetBearerToken"/> is set, that tells
2202+
/// Wire-only flag, emitted automatically when <see cref="BearerTokenProvider"/> is set, that tells
22022203
/// the runtime to request a token over the session-scoped <c>providerToken.getToken</c> RPC
2203-
/// before each outbound request to this provider. Derived from <see cref="GetBearerToken"/>;
2204+
/// before each outbound request to this provider. Derived from <see cref="BearerTokenProvider"/>;
22042205
/// internal and never part of the public API.
22052206
/// </summary>
22062207
[JsonInclude]
22072208
[JsonPropertyName("hasBearerTokenProvider")]
2208-
internal bool? HasBearerTokenProvider => GetBearerToken is not null ? true : null;
2209+
internal bool? HasBearerTokenProvider => BearerTokenProvider is not null ? true : null;
22092210

22102211
/// <summary>
22112212
/// Per-request callback that resolves a bearer token on demand for this BYOK provider (for
22122213
/// example via Azure Managed Identity). The Copilot SDK takes no identity dependency: supply a
22132214
/// callback backed by your own identity library. Never serialized — setting it makes the SDK send
22142215
/// <c>hasBearerTokenProvider: true</c> on the wire and answer the runtime's
2215-
/// <c>providerToken.getToken</c> requests. Mutually exclusive with <see cref="ApiKey"/> and
2216-
/// <see cref="BearerToken"/>.
2216+
/// <c>providerToken.getToken</c> requests. When set alongside <see cref="ApiKey"/>/<see cref="BearerToken"/>, this callback takes precedence:
2217+
/// the runtime applies the token it returns as the Authorization: Bearer header for each request
2218+
/// and does not send the static credential.
22172219
/// </summary>
22182220
[JsonIgnore]
22192221
[Experimental(Diagnostics.Experimental)]
2220-
public Func<ProviderTokenArgs, Task<string>>? GetBearerToken { get; set; }
2222+
public Func<ProviderTokenArgs, Task<string>>? BearerTokenProvider { get; set; }
22212223

22222224
/// <summary>
22232225
/// Azure-specific configuration options.

dotnet/test/E2E/ByokBearerTokenProviderE2ETests.cs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ namespace GitHub.Copilot.Test.E2E;
1313

1414
/// <summary>
1515
/// End-to-end coverage for the experimental BYOK bearer-token-provider surface
16-
/// (<c>GetBearerToken</c> on a provider config). The callback stays entirely on
16+
/// (<c>BearerTokenProvider</c> on a provider config). The callback stays entirely on
1717
/// the SDK/client side: the SDK strips it from the wire config, sets the
1818
/// <c>hasBearerTokenProvider</c> flag, and the runtime calls back over the
1919
/// session-scoped <c>providerToken.getToken</c> RPC before each outbound model
@@ -81,7 +81,7 @@ private static async Task RunTurnAsync(
8181
{
8282
await session.SendAndWaitAsync(new MessageOptions { Prompt = prompt });
8383
}
84-
catch
84+
catch (InvalidOperationException)
8585
{
8686
// The handler always 404s the BYOK endpoint, so the turn errors after
8787
// the token-bearing request was already captured. Expected.
@@ -110,7 +110,7 @@ public async Task Applies_The_Callbacks_Token_As_The_Authorization_Header()
110110
Type = "openai",
111111
WireApi = "completions",
112112
BaseUrl = PrimaryBaseUrl,
113-
GetBearerToken = _ =>
113+
BearerTokenProvider = _ =>
114114
{
115115
Interlocked.Increment(ref calls);
116116
return Task.FromResult(sentinel);
@@ -149,7 +149,7 @@ public async Task Re_Acquires_A_Fresh_Token_For_Each_Request()
149149
BaseUrl = PrimaryBaseUrl,
150150
// A distinct token per acquisition proves the runtime re-invokes
151151
// the callback per request rather than caching a previous token.
152-
GetBearerToken = _ =>
152+
BearerTokenProvider = _ =>
153153
{
154154
var n = Interlocked.Increment(ref calls);
155155
return Task.FromResult($"rotating-token-{n}");
@@ -208,15 +208,15 @@ Func<ProviderTokenArgs, Task<string>> MakeCallback(string providerName) =>
208208
Type = "openai",
209209
WireApi = "completions",
210210
BaseUrl = RedBaseUrl,
211-
GetBearerToken = MakeCallback("red"),
211+
BearerTokenProvider = MakeCallback("red"),
212212
},
213213
new()
214214
{
215215
Name = "blue",
216216
Type = "openai",
217217
WireApi = "completions",
218218
BaseUrl = BlueBaseUrl,
219-
GetBearerToken = MakeCallback("blue"),
219+
BearerTokenProvider = MakeCallback("blue"),
220220
},
221221
};
222222
var models = new List<ProviderModelConfig>
@@ -243,7 +243,7 @@ Func<ProviderTokenArgs, Task<string>> MakeCallback(string providerName) =>
243243
/// The runtime invokes <see cref="SendRequestAsync"/> for every model-layer HTTP
244244
/// request. Requests aimed at a fake BYOK host (<c>*.invalid</c>) are captured —
245245
/// recording the <c>Authorization</c> header the runtime applied after calling
246-
/// the provider's <c>GetBearerToken</c> callback over the session-scoped
246+
/// the provider's <c>BearerTokenProvider</c> callback over the session-scoped
247247
/// <c>providerToken.getToken</c> RPC — and answered with a synthetic <c>404</c>
248248
/// (a non-retryable status, so each outbound model request yields exactly one
249249
/// capture). Every other request (CAPI bootstrap: model catalog, policy, …) is
@@ -265,13 +265,14 @@ protected override Task<HttpResponseMessage> SendRequestAsync(HttpRequestMessage
265265
? string.Join(", ", values)
266266
: null));
267267

268-
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.NotFound)
268+
var response = new HttpResponseMessage(HttpStatusCode.NotFound)
269269
{
270270
Content = new StringContent(
271271
"{\"error\":{\"message\":\"fake byok endpoint\"}}",
272272
System.Text.Encoding.UTF8,
273273
"application/json"),
274-
});
274+
};
275+
return Task.FromResult(response);
275276
}
276277

277278
// CAPI bootstrap (model catalog, policy, …) — answered off-network.

go/client.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,18 +57,18 @@ import (
5757
// whole-session [ProviderConfig]. Named providers are keyed by their own Name.
5858
const defaultBearerTokenProviderName = "default"
5959

60-
// collectBearerTokenProviders gathers the per-provider [GetBearerToken] callbacks
60+
// collectBearerTokenProviders gathers the per-provider [BearerTokenProvider] callbacks
6161
// from the singular provider and any named providers, keyed by provider name. The
6262
// singular provider uses the implicit name "default"; named providers use their
6363
// own Name. Returns nil when no callbacks are configured.
64-
func collectBearerTokenProviders(provider *ProviderConfig, providers []NamedProviderConfig) map[string]GetBearerToken {
65-
callbacks := make(map[string]GetBearerToken)
66-
if provider != nil && provider.GetBearerToken != nil {
67-
callbacks[defaultBearerTokenProviderName] = provider.GetBearerToken
64+
func collectBearerTokenProviders(provider *ProviderConfig, providers []NamedProviderConfig) map[string]BearerTokenProvider {
65+
callbacks := make(map[string]BearerTokenProvider)
66+
if provider != nil && provider.BearerTokenProvider != nil {
67+
callbacks[defaultBearerTokenProviderName] = provider.BearerTokenProvider
6868
}
6969
for i := range providers {
70-
if providers[i].GetBearerToken != nil {
71-
callbacks[providers[i].Name] = providers[i].GetBearerToken
70+
if providers[i].BearerTokenProvider != nil {
71+
callbacks[providers[i].Name] = providers[i].BearerTokenProvider
7272
}
7373
}
7474
if len(callbacks) == 0 {

go/internal/e2e/byok_bearer_token_provider_e2e_test.go

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ type capturedBYOKRequest struct {
3737

3838
// byokCapturingRoundTripper stands in for a real HTTP upstream. It records the
3939
// `Authorization` header the runtime applied (after calling the provider's
40-
// GetBearerToken callback over the session-scoped `providerToken.getToken` RPC)
40+
// BearerTokenProvider callback over the session-scoped `providerToken.getToken` RPC)
4141
// for every request aimed at a fake `.invalid` BYOK host, answering them with a
4242
// synthetic 404 (a non-retryable status, so each outbound model request yields
4343
// exactly one capture). Every other request (CAPI bootstrap: model catalog,
@@ -96,7 +96,7 @@ func (rt *byokCapturingRoundTripper) reset() {
9696
}
9797

9898
// TestBYOKBearerTokenProvider is end-to-end coverage for the experimental BYOK
99-
// bearer-token-provider surface (GetBearerToken on a provider config). The
99+
// bearer-token-provider surface (BearerTokenProvider on a provider config). The
100100
// callback stays entirely on the SDK/client side: the SDK strips it from the
101101
// wire config, sets the `hasBearerTokenProvider` flag, and the runtime calls
102102
// back over the session-scoped `providerToken.getToken` RPC before each outbound
@@ -151,11 +151,11 @@ func TestBYOKBearerTokenProvider(t *testing.T) {
151151
}
152152

153153
providers := []copilot.NamedProviderConfig{{
154-
Name: "mi",
155-
Type: "openai",
156-
WireAPI: "completions",
157-
BaseURL: byokPrimaryBaseURL,
158-
GetBearerToken: getBearerToken,
154+
Name: "mi",
155+
Type: "openai",
156+
WireAPI: "completions",
157+
BaseURL: byokPrimaryBaseURL,
158+
BearerTokenProvider: getBearerToken,
159159
}}
160160
models := []copilot.ProviderModelConfig{{ID: "default", Provider: "mi", WireModel: "byok-gpt-4o"}}
161161

@@ -189,11 +189,11 @@ func TestBYOKBearerTokenProvider(t *testing.T) {
189189
}
190190

191191
providers := []copilot.NamedProviderConfig{{
192-
Name: "mi",
193-
Type: "openai",
194-
WireAPI: "completions",
195-
BaseURL: byokPrimaryBaseURL,
196-
GetBearerToken: getBearerToken,
192+
Name: "mi",
193+
Type: "openai",
194+
WireAPI: "completions",
195+
BaseURL: byokPrimaryBaseURL,
196+
BearerTokenProvider: getBearerToken,
197197
}}
198198
models := []copilot.ProviderModelConfig{{ID: "default", Provider: "mi", WireModel: "byok-gpt-4o"}}
199199

@@ -227,7 +227,7 @@ func TestBYOKBearerTokenProvider(t *testing.T) {
227227
}
228228
var mu sync.Mutex
229229
var acquiredFor []string
230-
makeCallback := func(providerName string) copilot.GetBearerToken {
230+
makeCallback := func(providerName string) copilot.BearerTokenProvider {
231231
return func(args copilot.ProviderTokenArgs) (string, error) {
232232
// The runtime forwards the requesting provider's name so the
233233
// client can dispatch to the right credential.
@@ -248,18 +248,18 @@ func TestBYOKBearerTokenProvider(t *testing.T) {
248248

249249
providers := []copilot.NamedProviderConfig{
250250
{
251-
Name: "red",
252-
Type: "openai",
253-
WireAPI: "completions",
254-
BaseURL: byokRedBaseURL,
255-
GetBearerToken: makeCallback("red"),
251+
Name: "red",
252+
Type: "openai",
253+
WireAPI: "completions",
254+
BaseURL: byokRedBaseURL,
255+
BearerTokenProvider: makeCallback("red"),
256256
},
257257
{
258-
Name: "blue",
259-
Type: "openai",
260-
WireAPI: "completions",
261-
BaseURL: byokBlueBaseURL,
262-
GetBearerToken: makeCallback("blue"),
258+
Name: "blue",
259+
Type: "openai",
260+
WireAPI: "completions",
261+
BaseURL: byokBlueBaseURL,
262+
BearerTokenProvider: makeCallback("blue"),
263263
},
264264
}
265265
models := []copilot.ProviderModelConfig{

go/session.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ type Session struct {
7777
elicitationMu sync.RWMutex
7878
canvasHandler CanvasHandler
7979
canvasMu sync.RWMutex
80-
bearerTokenProviders map[string]GetBearerToken
80+
bearerTokenProviders map[string]BearerTokenProvider
8181
bearerTokenMu sync.RWMutex
8282
openCanvases []rpc.OpenCanvasInstance
8383
openCanvasesMu sync.RWMutex
@@ -183,7 +183,7 @@ func (s *Session) getCanvasHandler() CanvasHandler {
183183
return s.canvasHandler
184184
}
185185

186-
// registerBearerTokenProviders installs per-provider [GetBearerToken] callbacks
186+
// registerBearerTokenProviders installs per-provider [BearerTokenProvider] callbacks
187187
// for BYOK providers configured with managed-identity / on-demand bearer-token
188188
// auth, keyed by provider name.
189189
//
@@ -192,10 +192,10 @@ func (s *Session) getCanvasHandler() CanvasHandler {
192192
// runtime needs a token it issues a session-scoped `providerToken.getToken`
193193
// request, which the session's provider-token adapter routes to the matching
194194
// per-provider callback.
195-
func (s *Session) registerBearerTokenProviders(providers map[string]GetBearerToken) {
195+
func (s *Session) registerBearerTokenProviders(providers map[string]BearerTokenProvider) {
196196
s.bearerTokenMu.Lock()
197197
defer s.bearerTokenMu.Unlock()
198-
s.bearerTokenProviders = make(map[string]GetBearerToken, len(providers))
198+
s.bearerTokenProviders = make(map[string]BearerTokenProvider, len(providers))
199199
for name, callback := range providers {
200200
if callback == nil {
201201
continue
@@ -204,7 +204,7 @@ func (s *Session) registerBearerTokenProviders(providers map[string]GetBearerTok
204204
}
205205
}
206206

207-
func (s *Session) getBearerTokenProvider(providerName string) GetBearerToken {
207+
func (s *Session) getBearerTokenProvider(providerName string) BearerTokenProvider {
208208
s.bearerTokenMu.RLock()
209209
defer s.bearerTokenMu.RUnlock()
210210
return s.bearerTokenProviders[providerName]

0 commit comments

Comments
 (0)