Ensure custom user-agent flows to auth HTTP calls#6790
Ensure custom user-agent flows to auth HTTP calls#6790spboyer wants to merge 3 commits intoAzure:mainfrom
Conversation
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>
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.
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. | ||
|
|
||
| package auth | ||
|
|
||
| import "net/http" | ||
|
|
||
| // userAgentClient wraps an HttpClient to inject a User-Agent header on all requests. | ||
| 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) | ||
| } | ||
| if req.Header.Get("User-Agent") == "" { | ||
| req.Header.Set("User-Agent", c.userAgent) | ||
| } else { | ||
| req.Header.Set("User-Agent", req.Header.Get("User-Agent")+","+c.userAgent) | ||
| } | ||
| return c.inner.Do(req) | ||
| } | ||
|
|
There was a problem hiding this comment.
The new userAgentClient wrapper lacks test coverage. While the functionality is simple, tests should verify: 1) User-Agent is correctly set when header is empty, 2) User-Agent is correctly appended when header already exists, 3) Empty userAgent string returns the inner client unchanged. This is especially important since this code affects all MSAL authentication traffic.
| if req.Header.Get("User-Agent") == "" { | ||
| req.Header.Set("User-Agent", c.userAgent) | ||
| } else { | ||
| req.Header.Set("User-Agent", req.Header.Get("User-Agent")+","+c.userAgent) |
There was a problem hiding this comment.
The existing User-Agent value is retrieved twice (line 25) - once in the condition check and once in the concatenation. Consider storing it in a variable to avoid the redundant lookup.
| if req.Header.Get("User-Agent") == "" { | |
| req.Header.Set("User-Agent", c.userAgent) | |
| } else { | |
| req.Header.Set("User-Agent", req.Header.Get("User-Agent")+","+c.userAgent) | |
| existingUA := req.Header.Get("User-Agent") | |
| if existingUA == "" { | |
| req.Header.Set("User-Agent", c.userAgent) | |
| } else { | |
| req.Header.Set("User-Agent", existingUA+","+c.userAgent) |
- 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>
Azure Dev CLI Install InstructionsInstall scriptsMacOS/Linux
bash: pwsh: WindowsPowerShell install MSI install Standalone Binary
MSI
Documentationlearn.microsoft.com documentationtitle: Azure Developer CLI reference
|
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