Ensure custom user-agent flows to auth HTTP calls#6790
Ensure custom user-agent flows to auth HTTP calls#6790spboyer wants to merge 5 commits intoAzure:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR implements custom user-agent propagation to Azure authentication HTTP calls, addressing issue #2723. The azd custom user-agent string (azdev/<version>) now flows through both MSAL and Azure Identity SDK authentication requests, making azd-originated auth traffic identifiable in Azure telemetry.
Changes:
- Added HTTP client wrapper for MSAL to inject user-agent header on all authentication requests
- Created
authClientOptions()helper that configuresTelemetry.ApplicationIDfor Azure Identity SDK credentials - Updated dependency injection to pass the user-agent string to the auth manager
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| cli/azd/pkg/auth/user_agent_client.go | New HTTP client wrapper that injects user-agent header for MSAL requests |
| cli/azd/pkg/auth/manager.go | Added userAgent field and authClientOptions() helper; updated credential factories to use custom user-agent |
| cli/azd/cmd/container.go | Updated auth.Manager registration to pass internal.UserAgent() |
| cli/azd/test/functional/remote_state_test.go | Updated test to pass empty user-agent string |
| cli/azd/pkg/devcentersdk/developer_client_test.go | Updated test to pass empty user-agent string |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Add the azd user-agent string (azdev/<version>) to all authentication HTTP calls made through Azure Identity SDK credentials and MSAL. Previously, auth calls used the SDK default user-agent, making it impossible to identify azd-originated auth traffic in telemetry. Changes: - Add userAgent field to auth.Manager, passed from container registration - Add authClientOptions() helper that sets Telemetry.ApplicationID on all azidentity credential ClientOptions (ManagedIdentity, ClientSecret, ClientCertificate, FederatedToken, AzurePipelines) - Add userAgentClient wrapper to inject user-agent on MSAL HTTP calls - Remove stale TODO comments about user-agent injection Fixes Azure#2723 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Apply authClientOptions() to LoginWithManagedIdentity, LoginWithServicePrincipalSecret, LoginWithServicePrincipalCertificate, and LoginWithAzurePipelinesFederatedTokenProvider - Remove remaining stale TODO comments - Guard against nil req.Header in userAgentClient.Do() Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add TestUserAgentClient with 4 test cases covering empty header, existing header append, empty userAgent passthrough, and nil header - Store existing User-Agent in variable to avoid redundant lookup Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Break long lines in NewClientSecretCredential and NewClientCertificateCredential calls by extracting options into local variables. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Address review feedback from @wbreza: use a typed UserAgent alias registered as a singleton instead of manually overriding the IoC constructor in container.go. The NewManager constructor now accepts auth.UserAgent directly via dependency injection. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
d43464c to
c9a7361
Compare
Azure Dev CLI Install InstructionsInstall scriptsMacOS/Linux
bash: pwsh: WindowsPowerShell install MSI install Standalone Binary
MSI
Documentationlearn.microsoft.com documentationtitle: Azure Developer CLI reference
|
| type userAgentClient struct { | ||
| inner HttpClient | ||
| userAgent string | ||
| } | ||
|
|
||
| func newUserAgentClient(inner HttpClient, userAgent string) HttpClient { | ||
| if userAgent == "" { | ||
| return inner | ||
| } | ||
| return &userAgentClient{inner: inner, userAgent: userAgent} | ||
| } | ||
|
|
||
| func (c *userAgentClient) Do(req *http.Request) (*http.Response, error) { | ||
| if req.Header == nil { | ||
| req.Header = make(http.Header) | ||
| } | ||
| existingUA := req.Header.Get("User-Agent") | ||
| if existingUA == "" { | ||
| req.Header.Set("User-Agent", c.userAgent) | ||
| } else { | ||
| req.Header.Set("User-Agent", existingUA+","+c.userAgent) | ||
| } | ||
| return c.inner.Do(req) | ||
| } |
There was a problem hiding this comment.
Are we able to leverage the azsdk policy / pipeline with existing user agent policy integration instead of adding this new wrapped HTTP client?
|
@wbreza Unfortunately not for the MSAL path -- MSAL's For the The HTTP client wrapper is only needed for MSAL traffic specifically because MSAL doesn't have an equivalent mechanism. The wrapper is minimal (just prepends the user-agent header on Options I see:
I'd lean toward option 1 since the wrapper is ~15 lines and gives us full coverage. What do you think? |
Summary
Fixes #2723
The custom azd user-agent string (
azdev/<version>) was not flowing to authentication HTTP calls made through the Azure Identity SDK and MSAL. This made it impossible to identify azd-originated auth traffic in Azure telemetry.Changes
cli/azd/pkg/auth/manager.go: AddeduserAgentfield toManagerstruct. NewauthClientOptions()helper returnsazcore.ClientOptionswithTelemetry.ApplicationIDset to the azd user-agent. Applied to all credential factories:ManagedIdentity,ClientSecret,ClientCertificate,FederatedToken(GitHub, AzurePipelines, OIDC). Wrapped MSAL HTTP client with user-agent injection.cli/azd/pkg/auth/user_agent_client.go(new): HTTP client wrapper that injects the user-agent header on all MSAL requests.cli/azd/cmd/container.go: Updatedauth.NewManagerregistration to passinternal.UserAgent().NewManagersignature.Approach
Per comment from @danieljurek, using
ClientOptions.Telemetry.ApplicationIDis the recommended way to mark requests as originating from azd. This prepends the value to the SDK's built-in user-agent string.For MSAL (which doesn't use
azcore.ClientOptions), a lightweight HTTP client wrapper injects the header directly.Testing
pkg/authtests pass