diff --git a/README.md b/README.md index 66a50ce6e8..92ab1c7f93 100644 --- a/README.md +++ b/README.md @@ -37,8 +37,8 @@ how to set up SSH: [Azure DevOps][azure-devops-ssh], [GitHub][github-ssh], Feature|Windows|macOS|Linux -|:-:|:-:|:-: Installer/uninstaller|✓|✓|✓ -Secure platform credential storage [(see more)][gcm-credstores]|✓|✓|✓ -Entra authentication with broker support|[opt-in][gcm-windows-broker]|✗|✗ +Secure platform [credential storage][gcm-credstores]|✓|✓|✓ +Entra authentication with [broker support][gcm-broker]|✓|✓|✓ Azure DevOps authentication|✓|✓|✓ GitHub & GHES authentication|✓|✓|✓ Bitbucket Cloud & DC authentication|✓|✓|✓ @@ -172,7 +172,7 @@ When using GitHub logos, please be sure to follow the [gcm-license]: LICENSE [gcm-usage]: docs/usage.md [gcm-wsl]: docs/wsl.md -[gcm-windows-broker]: docs/windows-broker.md +[gcm-broker]: docs/msauth-broker.md [git-tools-credential-storage]: https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage [github]: https://github.com [github-ssh]: https://help.github.com/en/articles/connecting-to-github-with-ssh diff --git a/docs/azrepos-users-and-tokens.md b/docs/azrepos-users-and-tokens.md index 58654f79a3..bcbf1a0052 100644 --- a/docs/azrepos-users-and-tokens.md +++ b/docs/azrepos-users-and-tokens.md @@ -27,8 +27,8 @@ resulted in a new authentication prompt. ### Microsoft identity OAuth tokens "Microsoft identity OAuth token" is the generic term for OAuth-based access -tokens issued by Azure Active Directory for either Work and School Accounts -(AAD tokens) or Personal Accounts (Microsoft Account/MSA tokens). +tokens issued by Entra ID for either Work and School Accounts +(Entra ID/AAD tokens) or Personal Accounts (Microsoft Account/MSA tokens). Azure DevOps supports Git authentication using Microsoft identity OAuth tokens as well as PATs. Microsoft identity OAuth tokens created by Git Credential diff --git a/docs/configuration.md b/docs/configuration.md index eb56af9f97..27ad5de235 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -699,28 +699,24 @@ git config --global credential.msauthFlow devicecode --- -### credential.msauthUseBroker _(experimental)_ +### credential.msauthUseBroker Use the operating system account manager where available. -Defaults to `false`. In certain cloud hosted environments when using a work or -school account, such as [Microsoft DevBox][devbox], the default is `true`. - -These defaults are subject to change in the future. +Defaults to `true`. -_**Note:** before you enable this option on Windows, please review the -[Windows Broker][wam] details for what this means to your local Windows user -account._ +_**Note:** for more information about the authentication broker, please review +the [Microsoft authentication broker][msauth-broker] information._ Value|Description -|- -`true`|Use the operating system account manager as an authentication broker. -`false` _(default)_|Do not use the broker. +`true` _(default)_|Use the operating system account manager as an authentication broker. +`false`|Do not use the broker. #### Example ```shell -git config --global credential.msauthUseBroker true +git config --global credential.msauthUseBroker false ``` **Also see: [GCM_MSAUTH_USEBROKER][gcm-msauth-usebroker]** @@ -880,14 +876,12 @@ git -c credential.azreposUseLegacyClientId=true fetch Specify the type of credential the Azure Repos host provider should return. -Defaults to the value `pat`. In certain cloud hosted environments when using a -work or school account, such as [Microsoft DevBox][devbox], the default value is -`oauth`. +Defaults to the value `oauth`. Value|Description -|- `pat`|Azure DevOps personal access tokens -`oauth`|Microsoft identity OAuth tokens (AAD or MSA tokens) +`oauth`|Microsoft identity OAuth tokens (Entra ID or MSA tokens) Here is more information about [Azure Access tokens][azure-tokens]. @@ -1277,7 +1271,7 @@ Defaults to disabled. [trace2-event-env]: environment.md#GIT_TRACE2_EVENT [trace2-performance-docs]: https://git-scm.com/docs/api-trace2#_the_performance_format_target [trace2-performance-env]: environment.md#GIT_TRACE2_PERF -[wam]: windows-broker.md +[msauth-broker]: msauth-broker.md [service-principal]: https://docs.microsoft.com/en-us/azure/active-directory/develop/app-objects-and-service-principals [azrepos-sp-mid]: https://learn.microsoft.com/en-us/azure/devops/integrate/get-started/authentication/service-principal-managed-identity [azrepos-wif-doc]: azrepos-wif.md diff --git a/docs/environment.md b/docs/environment.md index d325588420..424867a689 100644 --- a/docs/environment.md +++ b/docs/environment.md @@ -856,28 +856,24 @@ export GCM_MSAUTH_FLOW="devicecode" --- -### GCM_MSAUTH_USEBROKER _(experimental)_ +### GCM_MSAUTH_USEBROKER Use the operating system account manager where available. -Defaults to `false`. In certain cloud hosted environments when using a work or -school account, such as [Microsoft DevBox][devbox], the default is `true`. - -These defaults are subject to change in the future. +Defaults to `true`. -_**Note:** before you enable this option on Windows, please -[review the details][windows-broker] about what this means to your local Windows -user account._ +_**Note:** for more information about the authentication broker, please review +the [Microsoft authentication broker][msauth-broker] information._ Value|Description -|- -`true`|Use the operating system account manager as an authentication broker. -`false` _(default)_|Do not use the broker. +`true` _(default)_|Use the operating system account manager as an authentication broker. +`false`|Do not use the broker. #### Windows ```batch -SET GCM_MSAUTH_USEBROKER=true +SET GCM_MSAUTH_USEBROKER=false ``` #### macOS/Linux @@ -982,14 +978,12 @@ export GCM_AZREPOS_USE_LEGACY_CLIENTID="true" Specify the type of credential the Azure Repos host provider should return. -Defaults to the value `pat`. In certain cloud hosted environments when using a -work or school account, such as [Microsoft DevBox][devbox], the default value is -`oauth`. +Defaults to the value `oauth`. Value|Description -|- `pat`|Azure DevOps personal access tokens -`oauth`|Microsoft identity OAuth tokens (AAD or MSA tokens) +`oauth`|Microsoft identity OAuth tokens (Entra ID or MSA tokens) More information about Azure Access tokens can be found [here][azure-access-tokens]. @@ -1465,7 +1459,7 @@ Defaults to disabled. [trace2-event-config]: configuration.md#trace2eventTarget [trace2-performance-docs]: https://git-scm.com/docs/api-trace2#_the_performance_format_target [trace2-performance-config]: configuration.md#trace2perfTarget -[windows-broker]: windows-broker.md +[msauth-broker]: msauth-broker.md [service-principal]: https://docs.microsoft.com/en-us/azure/active-directory/develop/app-objects-and-service-principals [azrepos-sp-mid]: https://learn.microsoft.com/en-us/azure/devops/integrate/get-started/authentication/service-principal-managed-identity [azrepos-wif-doc]: azrepos-wif.md diff --git a/docs/img/broker-companyportal-mac.png b/docs/img/broker-companyportal-mac.png new file mode 100644 index 0000000000..5751579483 Binary files /dev/null and b/docs/img/broker-companyportal-mac.png differ diff --git a/docs/img/broker-companyportal-profileicon-mac.png b/docs/img/broker-companyportal-profileicon-mac.png new file mode 100644 index 0000000000..e4c342888a Binary files /dev/null and b/docs/img/broker-companyportal-profileicon-mac.png differ diff --git a/docs/img/broker-companyportal-removesso-mac.png b/docs/img/broker-companyportal-removesso-mac.png new file mode 100644 index 0000000000..2851aa18dc Binary files /dev/null and b/docs/img/broker-companyportal-removesso-mac.png differ diff --git a/docs/img/broker-diagram.png b/docs/img/broker-diagram.png new file mode 100644 index 0000000000..d16c3830e9 Binary files /dev/null and b/docs/img/broker-diagram.png differ diff --git a/docs/img/broker-entradisconnect-win11.png b/docs/img/broker-entradisconnect-win11.png new file mode 100755 index 0000000000..956739eda4 Binary files /dev/null and b/docs/img/broker-entradisconnect-win11.png differ diff --git a/docs/img/broker-intuneapp-linux.png b/docs/img/broker-intuneapp-linux.png new file mode 100644 index 0000000000..c1596ebf6c Binary files /dev/null and b/docs/img/broker-intuneapp-linux.png differ diff --git a/docs/img/broker-manageaccounts-win11.png b/docs/img/broker-manageaccounts-win11.png new file mode 100755 index 0000000000..3c766db720 Binary files /dev/null and b/docs/img/broker-manageaccounts-win11.png differ diff --git a/docs/img/broker-msadisconnect-win11.png b/docs/img/broker-msadisconnect-win11.png new file mode 100755 index 0000000000..ec75c1d9b1 Binary files /dev/null and b/docs/img/broker-msadisconnect-win11.png differ diff --git a/docs/img/broker-osaccount.png b/docs/img/broker-osaccount.png new file mode 100644 index 0000000000..d969c57b57 Binary files /dev/null and b/docs/img/broker-osaccount.png differ diff --git a/docs/img/broker-userpicker-mac.png b/docs/img/broker-userpicker-mac.png new file mode 100644 index 0000000000..681c9e5ce2 Binary files /dev/null and b/docs/img/broker-userpicker-mac.png differ diff --git a/docs/img/broker-userpicker-windows.png b/docs/img/broker-userpicker-windows.png new file mode 100644 index 0000000000..10d5a15ace Binary files /dev/null and b/docs/img/broker-userpicker-windows.png differ diff --git a/docs/msauth-broker.md b/docs/msauth-broker.md new file mode 100644 index 0000000000..f72cc5f45a --- /dev/null +++ b/docs/msauth-broker.md @@ -0,0 +1,342 @@ +# Microsoft Authentication Broker + +Git Credential Manager (GCM) can integrate with the Microsoft authentication +broker for your operating system to enable seamless single sign-on and secure +authentication. + +> [!IMPORTANT] +> As of **GCM 3.0**, broker integration is the **default option** for authentication +on Windows, macOS, and Linux, when supported. Previous versions of GCM only +supported the broker on Windows and required an explicit opt-in. + +## How to enable or disable + +As of GCM 3.0, wherever possible, the broker will automatically be used for +Microsoft authentication _by default_. + +You can opt-out of broker support by setting the environment variable +[`GCM_MSAUTH_USEBROKER`][GCM_MSAUTH_USEBROKER] or setting the Git configuration +value [`credential.msauthUseBroker`][credential.msauthUseBroker] to `false`. + +> [!NOTE] +> Brokered authentication only applies to Git hosting providers that use +> Microsoft authentication (e.g., Azure Repos). It does not impact +> authentication with GitHub, Bitbucket or other Git hosts. + +## What is the broker? + +The Microsoft authentication broker performs credential negotiation on behalf of +an application, simplifies many common authentication challenges and provides a +more seamless and secure authentication experience. + +Brokered authentication also has the benefit of deeper integration with +operating system features such as biometrics (Windows Hello, Touch ID), +hardware-backed credential storage ([Secure Enclave][secure-enclave], +[TPM][tpm], [passkeys][passkeys]), and compliance with enterprise +[conditional access security policies][conditional-access]. + +![Diagram of how the authentication broker interacts with other components][broker-diagram] + +### Surprising behaviors + +Integration with the broker offers convenience and other benefits, but may also +make unexpected other changes on your device. On a device owned and managed by +your institution or employer, the broker is probably the right choice. + +On a personal device or a device owned by a different institution (e.g. if +you're a contractor working for Company A with access to resources at +Company B), there are surprising behaviors that you should be aware of. + +> [!IMPORTANT] +> GCM does **not** have control over these behaviors, but we want to call them +> out clearly for full transparency and awareness. + +#### Work or school accounts + +If your device is not already joined to Entra ID or enrolled in Intune when you +first sign in with a work or school account, you may be asked to complete the +[Entra ID join][entrajoin-info] or [Intune join][intunejoin-info] process +depending on your company policies. + +> [!WARNING] +> If your company requires you to join Entra ID or enroll in Intune to access +> your work or school account, please be aware that doing so may result in +> changes to your device configuration such as: +> +> - your company's IT administrators may have access to monitor your device +> - software packages or updates may be installed +> - device settings may be modified without the ability to revert them +> - disk encryption may be enabled +> +> If you have questions about these changes, **please contact your company's +> IT administrators before continuing**. + +It is possible to disconnect your device from corporate policies after the fact. +Please see the instructions for your operating system below. + +##### Windows + +Open the Settings app: +[**Accounts** > **Access work or school**][appx-settings-workplace] and select +**Disconnect** + +![Manage device connection to corporate policies (Windows 11)][entradisconnect-win11] + +##### macOS + +To remove a single sign-on (SSO) account from macOS, open the +[Company Portal][appx-companyportal-mac] app, click on your **profile icon** +in the _top right_ of the window, then select **Remove account from this +device**: + +![Open the profile page in the Company Portal on macOS][companyportal-profileicon-macos] + +![Remove SSO account from the Company Portal on macOS][companyportal-removesso-macos] + +##### Linux + +The Intune app provides a command-line tool `dsreg` to manage device +registration on Linux devices, including deregistration using the `--unregister` +option. + +```shell +$ dsreg --help +dsreg - Device Registration Command Tool for Linux + +Similar to Windows dsregcmd, this tool queries device registration status, +PRT (Primary Refresh Token) information, and broker configuration. + +USAGE: + dsreg [options] + +OPTIONS: + --status Display comprehensive device registration and PRT status (default) + --help Display this help message + --tenant-id Query device registration for a specific tenant + --getdrstoken Acquire DRS access token for device registration + --unregister Unregister device from the tenant (requires root and DRS access token) + --cleanup Clean all broker state (accounts, tokens, credentials) + +EXAMPLES: + dsreg + dsreg --status + dsreg --tenant-id 12345678-90ab-cdef-1234-567890abcdef + dsreg --tenant-id --unregister + echo "" | dsreg --tenant-id --unregister + +OUTPUT INFORMATION: + Device State - Registration status, Device ID, Tenant information + Primary Refresh Token - PRT presence, timestamps, age, expiration + Broker Information - Version, client ID, device mode + +REQUIREMENTS: + - Microsoft Identity Broker installed and configured + - Appropriate permissions to query device state +``` + +#### Microsoft accounts (Windows only) + +> [!WARNING] +> If you are using local accounts on Windows, when you first sign in to a +> Microsoft account, that account may be linked to your local account and +> sign-in options may be affected. + +You can disconnect your Microsoft account from your local account after the fact +from the Settings app: [**Accounts** > **Your info**][appx-settings-yourinfo] +and select **Sign in with a local account instead** + +![Disconnect Microsoft account from local account (Windows 11)][msadisconnect-win11] + +## Windows + +The [Web Account Manager][wam] component of Windows provides the broker +functionality on Windows and comes _built into_ **Windows 10 version 1703** +(build 15063) and later, or **Windows Server 2019** (build 17763) and later on +servers. + +Here is an example of the broker account selection prompt on Windows: + +![Broker account selection on Windows][userpicker-windows] + +Accounts that appear above can be managed or removed from the Settings app: + +- [**Accounts** > **Your accounts**][appx-settings-accounts] (Windows 11) +- [**Accounts** > **Email & accounts**][appx-settings-accounts] (Windows 10) + +![Manage known Windows accounts from Settings (Windows 11)][manageaccounts-win11] + +## macOS + +On macOS the Platform Single-sign-on Extension (PSSO) provides the underlying +broker functionality for authentication. This is bundled with the Microsoft +Intune Company Portal app. The app must be installed and _your device must be +enrolled_ for broker integration to work correctly. + +Please read the Microsoft Intune [enrollment guide][enroll-macos] for further +instructions on enrolling your Mac. + +Here is an example of the broker account selection prompt on macOS: + +![Broker account selection on macOS][userpicker-macos] + +You can see the join status of your Mac in the +[Company Portal][appx-companyportal-mac] app: + +![Company Portal app on macOS showing device status][companyportal-status-macos] + +## Linux + +The [Microsoft Intune app][intunebroker-linux] acts as the authentication +broker on Linux distributions. Devices must be configured with a _GNOME +graphical desktop environment_ - **headless devices are not supported**. +Furthermore, the [Microsoft Edge browser][msedge] _must also be installed_. + +> [!NOTE] +> The Microsoft Intune app for Linux is **not supported** on Windows Subsystem +> for Linux (WSL). Please see the [WSL documentation][wsl] for integrating with +> the host Windows environment instead. + +Please read the Microsoft Intune [enrollment guide][enroll-linux] for further +instructions on enrolling your Linux device. + +You can see the join status of your Linux device in the +[Microsoft Intune app][intunebroker-linux]: + +![Microsoft Intune app on Linux showing device status][intuneapp-linux] + +In addition to the Intune app, you can also see join status using the `dsreg` +command-line tool with the `--status` option: + +```shell +$ dsreg --status +Account +----------------------------------------------------------------- +Home Account ID : 00000000-0000-0000-0000-000000000000.00000000-0000-0000-0000-000000000000 +Environment : login.windows.net +Tenant ID : 00000000-0000-0000-0000-000000000000 + +Device State +----------------------------------------------------------------- +Device Registration Status : Registered +Device ID : 00000000-0000-0000-0000-000000000000 +Tenant ID : 00000000-0000-0000-0000-000000000000 + +Primary Refresh Token +----------------------------------------------------------------- +PRT Present : YES +PRT Cached At : 2026-09-15 10:33:57 UTC +PRT Age : 0.04 hours +PRT Expires On : 2026-09-29 10:33:56 UTC +Session Key Protocol : 3.0 + +Broker Information +----------------------------------------------------------------- +Broker Version : 3.0.2 +Broker Service Name : microsoft-identity-device-broker.service +Broker Binary Path : /opt/microsoft/identity-broker/bin/microsoft-identity-broker +Broker Binary Timestamp : 2026-04-22 19:28:58 UTC +``` + +## Using the current OS account by default + +GCM can be configured to automatically use the default or current OS account for +authentication when using the broker. By default GCM will first ask if you wish +to continue with the default account before proceeding. + +![Default OS account prompt][default-account-prompt] + +If you wish to **always use** the current OS account, you can set the +[`GCM_MSAUTH_USEDEFAULTACCOUNT`][GCM_MSAUTH_USEDEFAULTACCOUNT] environment +variable or set the +[`credential.msauthUseDefaultAccount`][credential.msauthUseDefaultAccount] Git +configuration value to `true`. + +In certain cloud hosted environments such as [Microsoft Dev Box][devbox], this +setting is **_automatically enabled (`true`)_**. + +To disable the prompt and/or automatic account selection, set the environment +variable [`GCM_MSAUTH_USEDEFAULTACCOUNT`][GCM_MSAUTH_USEDEFAULTACCOUNT] or the +[`credential.msauthUseDefaultAccount`][credential.msauthUseDefaultAccount] Git +configuration value explicitly to `false`. + +## Legacy broker support + +In previous versions of GCM, support for the authentication broker was limited +to Windows only, required explicit opt-in, and had several limitations compared +to the current implementation. + +> [!TIP] +> Please update to the [latest Git Credential Manager][windows-install] where +> the below issues have now been addressed. + +Please read below for more information on previous versions of GCM and their +broker support. + +### Running as administrator + +#### GCM 2.1 and later + +From version 2.1 onwards, GCM uses a version of the [Microsoft Authentication +Library (MSAL)][msal-dotnet] that supports use of the Windows +broker from an elevated process. + +#### Previous versions + +The Windows broker ("WAM") makes heavy use of [COM][ms-com], a remote procedure +call (RPC) technology built into Windows. In order to integrate with WAM, Git +Credential Manager and the underlying +[Microsoft Authentication Library (MSAL)][msal-dotnet] must use COM interfaces +and RPCs. When you run Git Credential Manager as an elevated process, some of +the calls made between GCM and WAM may fail due to differing process security +levels. This can happen when you run `git` from an Administrator command-prompt +or perform Git operations from Visual Studio running as Administrator. + +If you've enabled using the broker, GCM will check whether it's running in an +elevated process. If it is, GCM will automatically attempt to modify the COM +security settings for the running process so that GCM and WAM can work together. +However, this automatic process security change is not guaranteed to succeed. +Various external factors like registry or system-wide COM settings may cause it +to fail. If GCM can't modify the process's COM security settings, GCM prints a +warning message and won't be able to use the broker. + +```text +warning: broker initialization failed +Failed to set COM process security to allow Windows broker from an elevated process (0x80010119). +See https://aka.ms/gcm/wamadmin for more information. +``` + +[appx-companyportal-mac]: companyportal://# +[appx-settings-accounts]: ms-settings:emailandaccounts +[appx-settings-workplace]: ms-settings:workplace +[appx-settings-yourinfo]: ms-settings:yourinfo +[broker-diagram]: img/broker-diagram.png +[conditional-access]: https://docs.microsoft.com/azure/active-directory/conditional-access/overview +[companyportal-profileicon-macos]: img/broker-companyportal-profileicon-mac.png +[companyportal-removesso-macos]: img/broker-companyportal-removesso-mac.png +[companyportal-status-macos]: img/broker-companyportal-mac.png +[credential.msauthUseBroker]: configuration.md#credentialmsauthusebroker +[credential.msauthUseDefaultAccount]: configuration.md#credentialmsauthusedefaultaccount-experimental +[default-account-prompt]: img/broker-osaccount.png +[devbox]: https://azure.microsoft.com/en-us/products/dev-box +[enroll-linux]: https://learn.microsoft.com/en-us/intune/user-help/enrollment/enroll-linux +[enroll-macos]: https://learn.microsoft.com/en-us/intune/user-help/enrollment/enroll-company-portal-macos +[entradisconnect-win11]: img/broker-entradisconnect-win11.png +[entrajoin-info]: https://learn.microsoft.com/en-us/entra/identity/devices/overview +[GCM_MSAUTH_USEBROKER]: environment.md#GCM_MSAUTH_USEBROKER +[GCM_MSAUTH_USEDEFAULTACCOUNT]: environment.md#GCM_MSAUTH_USEDEFAULTACCOUNT-experimental +[intuneapp-linux]: img/broker-intuneapp-linux.png +[intunebroker-linux]: https://learn.microsoft.com/en-us/intune/user-help/company-portal/intune-app-linux +[intunejoin-info]: https://learn.microsoft.com/en-us/intune/user-help/enrollment +[manageaccounts-win11]: img/broker-manageaccounts-win11.png +[ms-com]: https://docs.microsoft.com/en-us/windows/win32/com/the-component-object-model +[msadisconnect-win11]: img/broker-msadisconnect-win11.png +[msal-dotnet]: https://aka.ms/msal-net +[msedge]: https://www.microsoft.com/edge +[passkeys]: https://www.microsoft.com/en-gb/security/business/security-101/what-is-passkey +[secure-enclave]: https://support.apple.com/en-gb/guide/security/sec59b0b31ff/web +[tpm]: https://docs.microsoft.com/en-us/windows/security/information-protection/tpm/trusted-platform-module-overview +[userpicker-macos]: img/broker-userpicker-mac.png +[userpicker-windows]: img/broker-userpicker-windows.png +[wam]: https://docs.microsoft.com/azure/active-directory/devices/concept-primary-refresh-token#key-terminology-and-components +[windows-install]: install.md#windows +[wsl]: wsl.md diff --git a/docs/windows-broker.md b/docs/windows-broker.md deleted file mode 100644 index 679e0e5a0f..0000000000 --- a/docs/windows-broker.md +++ /dev/null @@ -1,237 +0,0 @@ -# Web Account Manager integration - -Git Credential Manager (GCM) knows how to integrate with the -[Web Account Manager (WAM)][azure-refresh-token-terms] feature of Windows. GCM -uses WAM to store credentials for Azure DevOps. Authentication requests are said -to be "brokered" to the operating system. Currently, GCM will share -authentication state with a few other Microsoft developer tools like Visual -Studio, meaning fewer authentication prompts. -Enabling WAM integration may also be required with certain -[Conditional Access policies][azure-conditional-access], which enterprises use -to help protect their assets, including source code. - -Integration with the WAM broker offers convenience and other benefits, but may -also make unexpected other changes on your device. On a device owned and managed -by your institution or employer, WAM is probably the right choice. On a personal -device or a device owned by a different institution (e.g. if you're a contractor -working for Company A with access to resources at Company B), there are -surprising behaviors that you should be aware of before enabling WAM integration. - -Note that this only affects [Azure DevOps][azure-devops]. -It doesn't impact authentication with GitHub, Bitbucket, or any other Git host. - -## How to enable - -You can opt-in to WAM support by setting the environment variable -[`GCM_MSAUTH_USEBROKER`][GCM_MSAUTH_USEBROKER] or setting the Git configuration -value [`credential.msauthUseBroker`][credential.msauthUseBroker]. - -## Features - -When you turn on WAM support, GCM can cooperate with Windows and with other -WAM-enabled software on your machine. This means a more seamless experience, -fewer multi-factor authentication prompts, and the ability to use additional -authentication technologies like smart cards and Windows Hello. These -convenience and security features make a good case for enabling WAM. - -## Using the current OS account by default - -Enabling WAM does not currently automatically use the current Windows account -for authentication. In order to opt-in to this behavior you can set the -[`GCM_MSAUTH_USEDEFAULTACCOUNT`][GCM_MSAUTH_USEDEFAULTACCOUNT] environment -variable or set the -[`credential.msauthUseDefaultAccount`][credential.msauthUseDefaultAccount] Git -configuration value to `true`. - -In certain cloud hosted environments when using a work or school account, such -as [Microsoft Dev Box][devbox], this setting is **_automatically enabled_**. - -To disable this behavior, set the environment variable -[`GCM_MSAUTH_USEDEFAULTACCOUNT`][GCM_MSAUTH_USEDEFAULTACCOUNT] or the -[`credential.msauthUseDefaultAccount`][credential.msauthUseDefaultAccount] Git -configuration value explicitly to `false`. - -## Surprising behaviors - -The WAM and Windows identity systems are complex, addressing a very broad range -of customer use cases. What works for a solo home user may not be adequate for a -corporate-managed fleet of 100,000 devices and vice versa. The GCM team isn't -responsible for the user experience or choices made by WAM, but by integrating -with WAM, we inherit some of those choices. Therefore, we want you to be aware -of some defaults and experiences if you choose to use WAM integration. - -### For work or school accounts (Azure AD-backed identities) - -When you sign into an Azure DevOps organization backed by Azure AD (often your -company or school email), if your machine is already joined to Azure AD matching -that Azure DevOps organization, you'll get a seamless and easy-to-use experience. - -If your machine isn't Azure AD-joined, or is Azure AD-joined to a different -tenant, WAM will present you with a dialog box suggesting you stay signed in and -allow the organization to manage your device. The dialog box has changed a bit -in various versions of Windows; here are two examples from 2021: - -![Consent dialog pre-21H1][aad-questions] - -![Consent dialog post-21H1][aad-questions-21h1] - -Depending on what you click, one of three things can happen: - -- If you leave "allow my organization to manage my device" checked and click -"OK", your computer will be registered with the Azure AD tenant backing the -organization. -It may also be MDM-enrolled ("Mobile Device Management" -- think Intune, -AirWatch, MobileIron, etc.), meaning an administrator can deploy policies to -your machine: requiring certain kinds of sign-in, turning on antivirus and -firewall software, and enabling BitLocker. -Your identity will also be available to other apps on the computer for signing -in, some of which may do so automatically. - -![Example of policies pushed to an Intune-enrolled device][aad-bitlocker] - -- If you uncheck "allow my organization to manage my device" and click "OK", -your computer will be registered with Azure AD but will not be MDM-enrolled. -Your identity will be available to other apps on the computer for signing in. -Other apps may log you in automatically or prompt you again to allow your -organization to manage your device. Despite joining Azure AD, your -organization's Conditional Access policies may still prevent you from accessing -Azure DevOps. -If so, you'll be prompted with instructions on how to enroll in MDM. - -- If you instead click "No, sign in to this app only", your machine will not be -joined to Azure AD or MDM-enrolled, so no policies can be enforced, and your -identity won't be made available to other apps on the computer. -Similar to the above, your organization's Conditional Access policies may -prevent you from proceeding. - -If Conditional Access is required to access your organization's Git repositories, -you can [enable WAM integration][GCM_MSAUTH_USEBROKER] (or follow other -instructions your organization provides). - -#### Removing device management - -If you've allowed your computer to be managed and want to undo it, you can go -into **Settings**, **Accounts**, **Access work or school**. -In the section where you see your email address and organization name, click -**Disconnect**. - -![Finding your work or school account][aad-work-school] - -![Disconnecting from Azure AD][aad-disconnect] - -### For Microsoft accounts - -When you sign into an Azure DevOps organization backed by Microsoft account -(MSA) identities (email addresses like `@outlook.com` or `@gmail.com` fall into -this category), you may be prompted to select an existing "work or school -account" or use a different one. - -In order to sign in with an MSA you should continue and select "Use a different -[work or school] account", but enter your MSA credentials when prompted. This is -due to a configuration outside of our control. We expect this experience to -improve over time and a "personal account" option to be presented in the future. - -![Initial dialog to choose an existing or different account][ms-sign-in] - -If you've connected your MSA to Windows or signed-in to other Microsoft -applications such as Office, then you may see this account listed in the -authentication prompts when using GCM. - ---- - -⚠️ **Important** ⚠️ - -When adding a new MSA to Windows, you'll be asked to select whether to use this -account across all of your device (**option 1**), or only permit Microsoft-apps -to access your identity (**option 2**). If you opt to use the account everywhere, -then your local Windows user account will be connected to that MSA. -This means you'll need to use your MSA credentials to sign in to Windows going -forward. - -Selecting "just this app" or "Microsoft apps only" will still allow you to use -this MSA across apps in Windows, but will not require you to use your MSA -credentials to sign in to Windows. - -![Confirmation to connect your MSA to Windows][msa-confirm] - -To disconnect an MSA added using option 1, you can go into **Settings**, -**Accounts**, **Your info** and click **Stop signing in to all Microsoft apps -automatically**. - -![Remove your Microsoft account from Windows][msa-remove] - -For MSAs added for "Microsoft apps only", you can modify whether or not these -accounts are available to other applications, and also remove the accounts from - **Settings**, **Accounts**, **Emails & accounts**: - -![Allow all Microsoft apps to access your identity][all-ms-apps] - -![Microsoft apps must ask to access your identity][apps-must-ask] - -## Running as administrator - -### GCM 2.1 and later - -From version 2.1 onwards, GCM uses a version of the [Microsoft Authentication -Library (MSAL)][msal-dotnet] that supports use of the Windows -broker from an elevated process. - -### Previous versions - -The Windows broker ("WAM") makes heavy use of [COM][ms-com], a remote procedure -call (RPC) technology built into Windows. In order to integrate with WAM, Git -Credential Manager and the underlying -[Microsoft Authentication Library (MSAL)][msal-dotnet] must use COM interfaces -and RPCs. When you run Git Credential Manager as an elevated process, some of -the calls made between GCM and WAM may fail due to differing process security -levels. This can happen when you run `git` from an Administrator command-prompt -or perform Git operations from Visual Studio running as Administrator. - -If you've enabled using the broker, GCM will check whether it's running in an -elevated process. If it is, GCM will automatically attempt to modify the COM -security settings for the running process so that GCM and WAM can work together. -However, this automatic process security change is not guaranteed to succeed. -Various external factors like registry or system-wide COM settings may cause it -to fail. If GCM can't modify the process's COM security settings, GCM prints a -warning message and won't be able to use the broker. - -```text -warning: broker initialization failed -Failed to set COM process security to allow Windows broker from an elevated process (0x80010119). -See https://aka.ms/gcm/wamadmin for more information. -``` - -### Possible solutions - -In order to fix the problem, there are a few options: - -1. Update to the [latest Git for Windows][git-for-windows-latest] - **(recommended)**. -2. Run Git or Git Credential Manager from non-elevated processes. -3. Disable the broker by setting the - [`GCM_MSAUTH_USEBROKER`][GCM_MSAUTH_USEBROKER] - environment variable or the - [`credential.msauthUseBroker`][credential.msauthUseBroker] - Git configuration setting to `false`. - -[azure-refresh-token-terms]: https://docs.microsoft.com/azure/active-directory/devices/concept-primary-refresh-token#key-terminology-and-components -[azure-conditional-access]: https://docs.microsoft.com/azure/active-directory/conditional-access/overview -[azure-devops]: https://azure.microsoft.com/en-us/products/devops -[GCM_MSAUTH_USEBROKER]: environment.md#GCM_MSAUTH_USEBROKER-experimental -[GCM_MSAUTH_USEDEFAULTACCOUNT]: environment.md#GCM_MSAUTH_USEDEFAULTACCOUNT-experimental -[credential.msauthUseBroker]: configuration.md#credentialmsauthusebroker-experimental -[credential.msauthUseDefaultAccount]: configuration.md#credentialmsauthusedefaultaccount-experimental -[aad-questions]: img/aad-questions.png -[aad-questions-21h1]: img/aad-questions-21H1.png -[aad-bitlocker]: img/aad-bitlocker.png -[aad-work-school]: img/aad-work-school.png -[aad-disconnect]: img/aad-disconnect.png -[ms-sign-in]: img/get-signed-in.png -[all-ms-apps]: img/all-microsoft.png -[apps-must-ask]: img/apps-must-ask.png -[ms-com]: https://docs.microsoft.com/en-us/windows/win32/com/the-component-object-model -[msa-confirm]: img/msa-confirm.png -[msa-remove]: img/msa-remove.png -[msal-dotnet]: https://aka.ms/msal-net -[devbox]: https://azure.microsoft.com/en-us/products/dev-box -[git-for-windows-latest]: https://git-scm.com/download/win diff --git a/src/Core.Tests/Authentication/Entra/EntraAuthenticationTests.cs b/src/Core.Tests/Authentication/Entra/EntraAuthenticationTests.cs index 4ce33ef030..160f3725e8 100644 --- a/src/Core.Tests/Authentication/Entra/EntraAuthenticationTests.cs +++ b/src/Core.Tests/Authentication/Entra/EntraAuthenticationTests.cs @@ -8,6 +8,48 @@ namespace GitCredentialManager.Tests.Authentication.Entra; public class EntraAuthenticationTests { + [Theory] + [InlineData(null, null, true)] + [InlineData(null, "true", true)] + [InlineData(null, "false", false)] + [InlineData(null, "FALSE", false)] + [InlineData(null, "0", false)] + [InlineData(null, "no", false)] + [InlineData(null, "off", false)] + [InlineData(null, "invalid", true)] + [InlineData(null, "", true)] + [InlineData(null, " ", true)] + [InlineData("true", null, true)] + [InlineData("false", null, false)] + [InlineData("FALSE", null, false)] + [InlineData("0", null, false)] + [InlineData("no", null, false)] + [InlineData("off", null, false)] + [InlineData("invalid", null, true)] + [InlineData("", null, true)] + [InlineData(" ", null, true)] + [InlineData("false", "true", false)] + [InlineData("true", "false", true)] + [InlineData("invalid", "false", true)] + [InlineData("", "false", true)] + public void IsBrokerEnabled(string environmentValue, string gitConfigValue, bool expected) + { + var context = new TestCommandContext(); + if (gitConfigValue is not null) + { + string key = + $"{Constants.GitConfiguration.Credential.SectionName}.{Constants.GitConfiguration.Credential.MsAuthUseBroker}"; + context.Git.Configuration.Global[key] = [gitConfigValue]; + } + if (environmentValue is not null) + { + context.Environment.Variables[Constants.EnvironmentVariables.MsAuthUseBroker] = environmentValue; + } + var entraAuth = new EntraAuthentication(context); + + Assert.Equal(expected, entraAuth.IsBrokerEnabled()); + } + [Fact] public async Task GetTokenForUserAsync_NoInteraction_ThrowsException() { diff --git a/src/Core/Authentication/Entra/EntraAuthentication.PublicClient.cs b/src/Core/Authentication/Entra/EntraAuthentication.PublicClient.cs index 87d8f74c61..ba68573ceb 100644 --- a/src/Core/Authentication/Entra/EntraAuthentication.PublicClient.cs +++ b/src/Core/Authentication/Entra/EntraAuthentication.PublicClient.cs @@ -455,7 +455,7 @@ private PublicClientApplicationBuilder GetPublicAppBuilder(out bool useBroker) .WithLegacyCacheCompatibility(false) .WithDefaultRedirectUri(); - // Try and configure the broker if the user has opted in to using it, + // Try and configure the broker if it is enabled in user preferences, // and it is available in the current environment if (Context.SessionManager.IsDesktopSession && IsBrokerEnabled()) { @@ -618,7 +618,7 @@ private bool TryGetModePreference(out InteractionMode mode) } /// - /// Check if the user has opted-in to using the authentication broker. + /// Check if the authentication broker is enabled in user preferences. /// /// /// This reflects the user preference for use of the broker, and may return true @@ -626,10 +626,10 @@ private bool TryGetModePreference(out InteractionMode mode) /// out parameter of to see if the /// broker will be used for authentication. /// - private bool IsBrokerEnabled() + internal bool IsBrokerEnabled() { - // Default to using the OS broker only on DevBox for the time being - bool defaultValue = PlatformUtils.IsDevBox(); + // Default to using the OS broker + const bool defaultValue = true; if (Context.Settings.TryGetSetting(Constants.EnvironmentVariables.MsAuthUseBroker, Constants.GitConfiguration.Credential.SectionName, diff --git a/src/Microsoft.AzureRepos.Tests/AzureReposHostProviderTests.cs b/src/Microsoft.AzureRepos.Tests/AzureReposHostProviderTests.cs index 3e9fba0f6a..8837181a96 100644 --- a/src/Microsoft.AzureRepos.Tests/AzureReposHostProviderTests.cs +++ b/src/Microsoft.AzureRepos.Tests/AzureReposHostProviderTests.cs @@ -51,6 +51,46 @@ public void AzureReposProvider_GetUseSharedCache_EnvironmentOverridesGitConfig() Assert.True(provider.GetUseSharedCache()); } + [Theory] + [InlineData(null, null, false)] + [InlineData(null, "oauth", false)] + [InlineData(null, "OAUTH", false)] + [InlineData(null, "pat", true)] + [InlineData(null, "PAT", true)] + [InlineData(null, "invalid", false)] + [InlineData(null, "", false)] + [InlineData(null, " ", false)] + [InlineData("oauth", null, false)] + [InlineData("OAUTH", null, false)] + [InlineData("pat", null, true)] + [InlineData("PAT", null, true)] + [InlineData("invalid", null, false)] + [InlineData("", null, false)] + [InlineData(" ", null, false)] + [InlineData("pat", "oauth", true)] + [InlineData("oauth", "pat", false)] + [InlineData("invalid", "pat", false)] + [InlineData("", "pat", false)] + public void AzureReposProvider_UsePersonalAccessTokens( + string environmentValue, string gitConfigValue, bool expected) + { + var context = new TestCommandContext(); + if (gitConfigValue is not null) + { + string key = + $"{Constants.GitConfiguration.Credential.SectionName}.{AzureDevOpsConstants.GitConfiguration.Credential.CredentialType}"; + context.Git.Configuration.Global[key] = new List {gitConfigValue}; + } + if (environmentValue is not null) + { + context.Environment.Variables[AzureDevOpsConstants.EnvironmentVariables.CredentialType] = + environmentValue; + } + var provider = new AzureReposHostProvider(context); + + Assert.Equal(expected, provider.UsePersonalAccessTokens()); + } + [Fact] public void AzureReposProvider_IsSupported_AzureHost_UnencryptedHttp_ReturnsTrue() { @@ -650,6 +690,8 @@ public async Task AzureReposProvider_GetCredentialAsync_PatMode_OrgInUserName_No var authResult = CreateAuthResult(account, accessToken); var context = new TestCommandContext(); + context.Environment.Variables[AzureDevOpsConstants.EnvironmentVariables.CredentialType] = + AzureDevOpsConstants.PatCredentialType; var azDevOpsMock = new Mock(MockBehavior.Strict); azDevOpsMock.Setup(x => x.GetAuthorityAsync(expectedOrgUri)).ReturnsAsync(authorityUrl); @@ -696,6 +738,8 @@ public async Task AzureReposProvider_GetCredentialAsync_PatMode_NoExistingPat_Ge var authResult = CreateAuthResult(account, accessToken); var context = new TestCommandContext(); + context.Environment.Variables[AzureDevOpsConstants.EnvironmentVariables.CredentialType] = + AzureDevOpsConstants.PatCredentialType; var azDevOpsMock = new Mock(MockBehavior.Strict); azDevOpsMock.Setup(x => x.GetAuthorityAsync(expectedOrgUri)).ReturnsAsync(authorityUrl); @@ -738,6 +782,8 @@ public async Task AzureReposProvider_GetCredentialAsync_PatMode_ExistingPat_Retu const string account = "john.doe"; var context = new TestCommandContext(); + context.Environment.Variables[AzureDevOpsConstants.EnvironmentVariables.CredentialType] = + AzureDevOpsConstants.PatCredentialType; context.CredentialStore.Add(service, account, personalAccessToken); diff --git a/src/Microsoft.AzureRepos/AzureReposHostProvider.cs b/src/Microsoft.AzureRepos/AzureReposHostProvider.cs index 78456e1c2b..709f301c2e 100644 --- a/src/Microsoft.AzureRepos/AzureReposHostProvider.cs +++ b/src/Microsoft.AzureRepos/AzureReposHostProvider.cs @@ -367,7 +367,7 @@ private async Task GetEntraAccessTokenAsync(GitReque // // If the remote URI is a classic "*.visualstudio.com" host name and we have a user specified from the - // remote then take that as the current AAD/MSA user in the first instance. + // remote then take that as the current Entra/MSA user in the first instance. // // For "dev.azure.com" host names we only use the user info part of the remote when this doesn't // match the Azure DevOps organization name. Our friends in Azure DevOps decided "borrow" the username @@ -404,7 +404,7 @@ private async Task GetEntraAccessTokenAsync(GitReque : $"Found cached account '{account.HomeAccountId}'"); } - // Get an AAD access token for the Azure DevOps SPS + // Get an Entra access token for the Azure DevOps SPS _context.Trace.WriteLine("Getting Entra access token..."); IEntraAuthenticationResult result = await _entraAuth.Value.GetTokenForUserAsync( AzureDevOpsConstants.AzureDevOpsDefaultScopes, @@ -565,10 +565,10 @@ private static string GetAccountNameForCredentialQuery(GitRequest request) /// Check if Azure DevOps Personal Access Tokens should be used or not. /// /// True if Personal Access Tokens should be used, false otherwise. - private bool UsePersonalAccessTokens() + internal bool UsePersonalAccessTokens() { - // Default to using PATs except on DevBox where we prefer OAuth tokens - bool defaultValue = !PlatformUtils.IsDevBox(); + // Default to using OAuth tokens + const bool defaultValue = false; if (_context.Settings.TryGetSetting( AzureDevOpsConstants.EnvironmentVariables.CredentialType, @@ -588,7 +588,7 @@ private bool UsePersonalAccessTokens() default: _context.Console.WriteWarning( - $"unknown Azure Repos credential type '{valueStr}' - using PATs"); + $"unknown Azure Repos credential type '{valueStr}' - using OAuth"); return defaultValue; } }