Configurable client builders and credential injection#3172
Merged
jeremydmiller merged 7 commits intoJun 22, 2026
Merged
Conversation
… and subscriber client builders for GCP.
…s applied in order.
…e API clients at once. Particularly useful when working in cross-cloud scenarios where you might be constructing a GoogleCredential via WIF (Workload Identiy Federation) from another cloud provider.
…urces such as Azure Key Vault etc.
jeremydmiller
added a commit
that referenced
this pull request
Jun 22, 2026
… GCP Pub/Sub (#3195) Follow-up to #3172. Documentation and test-only additions: - Docs: clarify that the UseCredential async factory is re-invoked on every listener (re)connect (not once at startup), and add a "Rolling credentials" section with a thread-safe RollingCredentialSource sample (cache + refresh margin + double-checked locking) for short-lived/rotated tokens. - Tests: round out unit-test symmetry across all three Configure* hooks (compose-in-order, async-await, async credential factory) and add two tests that lock in the rolling semantics (factory re-invoked per connect; fresh credential instance per connect). Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This was referenced Jun 23, 2026
Closed
This was referenced Jul 9, 2026
This was referenced Jul 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Background
We utilise a combination of cloud providers (Azure and GCP) and in some cases have workloads hosted in Azure that need to utilise GCP Pub/Sub as a transport. We secure access to Pub/Sub from Azure utilising DefaultAzureCredential identities on Azure which are exchanged for GCP Credentials via WIF (Workload identity federation).
With the current PubSub transport setup it's not possible to set the credentials or override other API settings as necessary. This PR aims to address this.
Summary
The GCP Pub/Sub transport currently hard-codes its client construction — only
EmulatorDetectionis configurable. There is no way to inject credentials or customise the underlying GCP client builders, which blocks cross-cloud authentication scenarios (e.g. an application running on Azure App Service or Azure Container Apps authenticating to GCP Pub/Sub via Workload Identity Federation).This PR adds:
ConfigurePublisherApiClient(Action<PublisherServiceApiClientBuilder>)/(Func<PublisherServiceApiClientBuilder, ValueTask>)— async-capable callback applied to the publisher API client builder beforeBuildAsync(), afterEmulatorDetectionis set (so callbacks can override transport-level defaults). Multiple calls compose in order.ConfigureSubscriberApiClient— same, for the subscriber API client builder used for topic/subscription management.ConfigureSubscriberClient— same, for theSubscriberClientBuilderused by each streaming listener (BatchedPubsubListenerandInlinePubsubListener).UseCredential(GoogleCredential)— convenience shorthand that setsGoogleCredentialon all three builders.GoogleCredentialmanages its own token refresh lifecycle, including Workload Identity Federation scenarios, so no background refresh task is needed in the application.UseCredential(Func<ValueTask<GoogleCredential>>)— async overload for when the credential itself must be fetched asynchronously at startup (e.g. reading from Azure Key Vault).All callbacks and
UseCredentialare onPubsubConfiguration(the fluent builder returned byUsePubsub()), consistent with how other Wolverine transports expose connection configuration (Azure Service BusTokenCredential, KafkaAction<ProducerBuilder>, etc.).Document updates
A new Customisation page has been added under the Google PubSub section (
docs/guide/messaging/transports/gcp-pubsub/customisation.md) covering:UseCredentialfor WIF/file-based credentials, including a worked Azure Container Apps example showing how to construct a URL-sourced external account credential from ACA's managed identity environment variablesUseCredential(Func<ValueTask<GoogleCredential>>)overload for credentials that must be fetched at startupConfigurePublisherApiClient,ConfigureSubscriberApiClient, andConfigureSubscriberClientwith a reference table and examples for custom endpoints and flow control settingsThe existing Connecting to the Broker section on the index page has a new authentication sub-section summarising
UseCredentialwith a link through to the Customisation page for deeper configuration. The Customisation page has also been added to the sidebar nav.Tests
A new
PubsubConfigurationTestsclass covers:Configure*method sets the correct callback on the transport, that multiple calls compose in order, that async callbacks are properly awaited, and thatUseCredential(both sync and async factory overloads) threads the credential through to all three builders via a second capture callback.configure_callbacks_are_applied_to_live_builders, requiresdocker compose up -d gcp-pubsub) — setsEmulatorDetection.ProductionOnlyat the transport level, then overrides toEmulatorOnlyexclusively via the threeConfigure*callbacks.StartAsyncsucceeding (viaShould.NotThrowAsync) proves all three callbacks were applied to the real builders;AutoProvisionmakes live API calls during startup and listener initialisation is awaited, so any misconfiguration surfaces as a thrown exception.