azrepos: use GCM's own Entra application - #2416
Conversation
Azure Repos OAuth currently identifies GCM as the Visual Studio application. That client registration does not provide the redirect URIs required for broker authentication on macOS and Linux. Use GCM's dedicated first-party Entra application by default and enable broker support on all supported platforms. Retain the Visual Studio client ID as an explicit compatibility fallback while replacing the unrestricted developer overrides with a narrow, documented setting. Assisted-by: GPT-5.6 Sol Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
Consumer currently cannot determine which application registration or capabilities back an Entra authentication instance after it has been constructed. Azure Repos needs this information to distinguish its new client application from the legacy fallback. Expose the immutable public client configuration through the authentication interface and use it as the implementation's single source of configuration state. Assisted-by: GPT-5.6 Sol Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
There was a problem hiding this comment.
Pull request overview
Moves Azure Repos authentication to GCM’s dedicated Entra application while retaining legacy Visual Studio client compatibility.
Changes:
- Enables cross-platform broker support with the new client.
- Adds automatic legacy-client fallback and configuration overrides.
- Documents the compatibility settings.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
src/Microsoft.AzureRepos/AzureReposHostProvider.cs |
Selects clients and implements fallback. |
src/Microsoft.AzureRepos/AzureDevOpsRestApi.cs |
Removes the authority override. |
src/Microsoft.AzureRepos/AzureDevOpsConstants.cs |
Defines client IDs and settings. |
src/Core/Authentication/Entra/IEntraAuthentication.cs |
Exposes public-client configuration. |
src/Core/Authentication/Entra/EntraAuthentication.PublicClient.cs |
Exposes and consumes the configuration. |
src/Core/Authentication/Entra/EntraAuthentication.cs |
Initializes the exposed configuration. |
src/Core/Authentication/Entra/EntraAuthentication.Caching.cs |
Uses the exposed cache configuration. |
docs/environment.md |
Documents the environment override. |
docs/configuration.md |
Documents the Git configuration override. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
0411940 to
7e4be3c
Compare
The dedicated GCM Entra application may encounter authentication failures that did not occur with the legacy Visual Studio application. Failing immediately would block affected users during the transition without providing a compatibility path. When user OAuth fails through the new application, report the failure and retry once with the legacy client. Do not retry requests already using the legacy application, avoiding an unbounded fallback loop. Only need to retry user/public client flows and not SP/WIF/MI based auth since they do not use the public client ID. Assisted-by: GPT-5.6 Sol Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
The compatibility retry is the recovery path for authentication failures introduced by the new GCM Entra application. Without direct coverage, regressions would only surface for users during live authentication. Exercise both OAuth and PAT credential generation through a failed new-client attempt and successful legacy retry. Also ensure an already-legacy client propagates the failure rather than retrying indefinitely. Assisted-by: GPT-5.6 Sol Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
7e4be3c to
baaa2ad
Compare
| catch (MsalException msalEx) when (msalEx.ErrorCode != MsalError.AuthenticationCanceledError && | ||
| _entraAuth.Value.PublicClientConfig.ClientId != | ||
| AzureDevOpsConstants.LegacyClientId) | ||
| { | ||
| _context.Console.WriteError($"Failed to acquire Entra access token: {msalEx.Message}"); | ||
| _context.Console.WriteInfo($"Please report this failure at {Constants.HelpUrls.GcmNewIssue}"); | ||
| _context.Console.WriteInfo("Trying again with the legacy Entra client app..."); | ||
|
|
||
| // Recreate the lazy entra auth component forcing the legacy client ID | ||
| ResetEntraAuth(forceLegacyClientId: true); | ||
|
|
||
| // Try again! | ||
| return await GetUserCredentialAsync(request); | ||
| } |
There was a problem hiding this comment.
Every MsalException except authentication_canceled triggers legacy authentication. MSAL exposes authorization refusal separately as MsalError.AccessDenied (access_denied). If a user denies the first authorization request, GCM will immediately initiate another sign-in using the legacy application and describe the refusal as a reportable application failure. Exclude MsalError.AccessDenied as well, or restrict fallback to errors that can plausibly be resolved by changing client applications. Add a focused test alongside the new fallback tests.
This PR has been a long time in the making! We are giving Git Credential Manager its own first-party Microsoft Entra client application for Azure Repos!
GCM previously authenticated as the Visual Studio application, whose registration could not safely be extended for all of GCM's broker and Microsoft account requirements.
The dedicated application enables broker authentication on Windows, macOS, and Linux. In particular, this avoids unnecessary browser prompts on platforms where the broker can provide an existing signed-in account.
Changes
credential.azreposUseLegacyClientIdandGCM_AZREPOS_USE_LEGACY_CLIENTID.Compatibility
The new GCM application is the default. Users who need the previous behaviour can select the legacy client explicitly. An MSAL failure in the new application triggers one automatic legacy retry when obtaining a user Entra token, either directly for OAuth credentials or as part of PAT creation.
Managed identity, workload federation, and service-principal flows are unaffected.
Fixes #47