|
| 1 | +# Architecture |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +```text |
| 6 | ++---------------------------------------------------------------------------------------------------------------------+ |
| 7 | +| | |
| 8 | +| Git-Credential-Manager | |
| 9 | +| | |
| 10 | ++----------------+--------------------+--------------+--------------+-----+-----------------------+-----------------+-+ |
| 11 | + | | | | | | | |
| 12 | + | Mac | | | | Windows | Windows | |
| 13 | + | | | | | | | |
| 14 | + | | +-----------v-----------+ | | +----------------v---------------+ | |
| 15 | + | | | | | Windows | | | |
| 16 | + | | | GitHub <--------+------+ GitHub.UI.Windows | | |
| 17 | + | | | | | | | | | |
| 18 | + | | +-+---------------------+ | | +-+------------------------------+ | |
| 19 | + | | | | | | | |
| 20 | + | | | +---------------------v-+ | | +------------------------------v-+ |
| 21 | + | | | | | |Windows | | | |
| 22 | + | | | | Atlassian.Bitbucket <--------------+ Atlassian.Bitbucket.UI.Windows | |
| 23 | + | | | | | | | | | |
| 24 | + | | | +-+---------------------+ | | +---------------+----------------+ |
| 25 | + | | | | | | | |
| 26 | ++----------------v----------------+ | | | +----------------------v-+ | | |
| 27 | +| | | Mac | | | | | |
| 28 | +| Microsoft.Authentication.Helper <----------------+ Microsoft.AzureRepos | | | |
| 29 | +| | | | | | | | | |
| 30 | ++---------------------------------+ | | | +-----------+------------+ | | |
| 31 | + | | | | | | |
| 32 | + | | | | | | |
| 33 | + | | | | | | |
| 34 | + | | | | | | |
| 35 | + +-v----v----v--------------v------------+ +-v-----------------v----------------+ |
| 36 | + | | | | |
| 37 | + | Microsoft.Git.CredentialManager <----+ Microsoft.Git.CredentialManager.UI | |
| 38 | + | | | | |
| 39 | + +---------------------------------------+ +------------------------------------+ |
| 40 | +``` |
| 41 | + |
| 42 | +Git Credential Manager Core (GCM Core) is built to be Git host and platform/OS |
| 43 | +agonstic. Most of the shared logic (command execution, the abstract platform |
| 44 | +subsystems, etc) can be found in the `Microsoft.Git.CredentialManager` class |
| 45 | +library (C#). The library targets .NET Standard as well as .NET Framework. |
| 46 | + |
| 47 | +> **Note** |
| 48 | +> |
| 49 | +> The reason for also targeting .NET Framework directly is that the |
| 50 | +> `Microsoft.Identity.Client` ([MSAL.NET](https://github.com/AzureAD/microsoft-authentication-library-for-dotnet)) |
| 51 | +> library requires a .NET Framework target to be able to show the embedded web |
| 52 | +> browser auth pop-up on Windows platforms. |
| 53 | +> |
| 54 | +> There are extension points that now exist in MSAL.NET meaning we can plug-in |
| 55 | +> our own browser pop-up handling code on .NET Core meaning both Windows and |
| 56 | +> Mac. We haven't yet gotten around to exploring this. |
| 57 | +> |
| 58 | +> See [this](https://github.com/microsoft/Git-Credential-Manager-Core/issues/113) |
| 59 | +> issue for more information. |
| 60 | +
|
| 61 | +The entry-point for GCM Core can be found in the `Git-Credential-Manager` |
| 62 | +project, a console application that targets both .NET Core and .NET Framework. |
| 63 | +This project emits the `git-credential-manager-core(.exe)` executable, and |
| 64 | +contains very little code - registration of all supported host providers and |
| 65 | +running the `Application` object found in `Microsoft.Git.CredentialManager`. |
| 66 | + |
| 67 | +Providers have their own projects/assemblies that take dependencies on the |
| 68 | +`Microsoft.Git.CredentialManager` core assembly, and are dependents of the main |
| 69 | +entry point application `Git-Credential-Manager`. Code in these binaries is |
| 70 | +expected to run on all supported platforms and typically (see MSAL.NET note |
| 71 | +above) does not include any graphical user interface; they use terminal prompts |
| 72 | +only. |
| 73 | + |
| 74 | +Where a provider needs some platform-specific interaction or graphical user |
| 75 | +interface, the recommended model is to have a separate 'helper' executable that |
| 76 | +the shared, core binaries shell out to. Currently the Bitbucket and GitHub |
| 77 | +providers each have a WPF (Windows only) helper executable that shows |
| 78 | +authentication prompts and messages. |
| 79 | + |
| 80 | +The `Microsoft.Git.CredentialHelper.UI` project is a WPF (Windows only) assembly |
| 81 | +that contains common WPF components and styles that are shared between provider |
| 82 | +helpers on Windows. |
| 83 | + |
| 84 | +### Cross-platform UI |
| 85 | + |
| 86 | +We hope to be able to migrate the WPF/Windows only helpers to [Avalonia](https://avaloniaui.net/) |
| 87 | +in order to gain cross-platform graphical user interface support. See [this](https://github.com/microsoft/Git-Credential-Manager-Core/issues/136) |
| 88 | +issue for up-to-date progress on this effort. |
| 89 | + |
| 90 | +### Microsoft authentication |
| 91 | + |
| 92 | +For authentication using Microsoft Accounts or Azure Active Directory, things |
| 93 | +are a little different. The `MicrosoftAuthentication` component is present in |
| 94 | +the core `Microsoft.Git.CredentialManager` assembly, rather than bundled with a |
| 95 | +specific host provider. This was done to allow any service that may wish to in |
| 96 | +the future integrate with Microsoft Accounts or Azure Active Directory can make |
| 97 | +use of this reusable authentication component. |
| 98 | + |
| 99 | +Since MSAL.NET includes embedded GUI on Windows (when targeting .NET Frameonly |
| 100 | +only - see note above) we have no helper executable on Windows. However, on |
| 101 | +macOS the `MicrosoftAuthentication` component shells out to a native macOS |
| 102 | +helper that completely takes over all authentication flows using the older ADAL |
| 103 | +Objective-C libary. This was done because MSAL.NET does not offer the same level |
| 104 | +of integration for [MDM](https://en.wikipedia.org/wiki/Mobile_device_management) |
| 105 | +purposes, as well as lacking an embedded UI on non-Windows platforms. As |
| 106 | +MSAL.NET continues to evolve we hope to replace the ADAL/macOS helper |
| 107 | +altogether. |
| 108 | + |
| 109 | +## Asynchronous programming |
| 110 | + |
| 111 | +GCM Core makes use of the `async`/`await` model of .NET and C# in almost all |
| 112 | +parts of the codebase where appropriate as usually requests end up going to the |
| 113 | +network at some point. |
| 114 | + |
| 115 | +## Command execution |
| 116 | + |
| 117 | +```text |
| 118 | + +---------------+ |
| 119 | + | | |
| 120 | + | Git | |
| 121 | + | | |
| 122 | + +---+-------^---+ |
| 123 | + | | |
| 124 | + +---v---+---+---+ |
| 125 | + | stdin | stdout| |
| 126 | + +---+---+---^---+ |
| 127 | + | | |
| 128 | + (2) | | (7) |
| 129 | + Select | | Serialize |
| 130 | + Command | | Result |
| 131 | + | | |
| 132 | + (3) | | |
| 133 | + Select | | |
| 134 | ++---------------+ Provider +---v-------+---+ |
| 135 | +| Host Provider | | | |
| 136 | +| Registry <------------+ Command | |
| 137 | +| | | | |
| 138 | ++-------^-------+ +----+------^---+ |
| 139 | + | | | |
| 140 | + | (4) | | (6) |
| 141 | + | Execute | | Return |
| 142 | + | Operation | | Result |
| 143 | + | (1) | | |
| 144 | + | Register +----v------+---+ |
| 145 | + | | | |
| 146 | + +--------------------+ Host Provider | |
| 147 | + | | |
| 148 | + +-------^-------+ |
| 149 | + | |
| 150 | + (5) Use services | |
| 151 | + | |
| 152 | + +-------v-------+ |
| 153 | + | Command | |
| 154 | + | Context | |
| 155 | + +---------------+ |
| 156 | +``` |
| 157 | + |
| 158 | +Git Credential Manager Core maintains a set of known commands including |
| 159 | +`Get|Store|EraseCommand`, as well as commands for install and help/usage. |
| 160 | + |
| 161 | +GCM Core also maintains a set of known, registered host providers that implement |
| 162 | +the `IHostProvider` interface. Providers register themselves by adding an |
| 163 | +instance of the provider to the `Application` object via the `RegisterProvider` |
| 164 | +method [in `Microsoft.Git.CredentialManager.Program`](../src/shared/Git-Credential-Manager/Program.cs). |
| 165 | +The `GenericHostProvider` is registered last so that it can handle all other |
| 166 | +HTTP-based remotes as a catch-all, and provide basic username/password auth and |
| 167 | +detect the presense of Windows Integrated Authentication (Kerberos, NTLM, |
| 168 | +Negotiate) support (1). |
| 169 | + |
| 170 | +For each invocation of GCM Core, the first argument on the command-line is |
| 171 | +matched against the known commands and if there is a successful match, the input |
| 172 | +from Git (over standard input) is deserialized and the command is executed (2). |
| 173 | + |
| 174 | +The `Get|Store|EraseCommand`s consult the host provider registry for the most |
| 175 | +appropriate host provider. The default registry implementation select the a host |
| 176 | +provider by asking each registered provider in turn if they understand the |
| 177 | +request. The provider selection can be overriden by the user via the |
| 178 | +[`credential.provider`](configuration.md#credentialprovider) or [`GCM_PROVIDER`](environment.md#GCM_PROVIDER) |
| 179 | +configuration and environment variable respectively (3)). |
| 180 | + |
| 181 | +The `Get|Store|EraseCommand`s call the corresponding |
| 182 | +`Get|Store|EraseCredentialAsync` methods on the `IHostProvider`, passing the |
| 183 | +request from Git together with an instance of the `ICommandContext` (4). The |
| 184 | +host provider can then make use of various services available on the command |
| 185 | +context to complete the requested operation (5). |
| 186 | + |
| 187 | +Once a credential has been created, retrieved, stored or erased, the host |
| 188 | +provider returns the credential (for `get` operations only) to the calling |
| 189 | +command (6). The credential is then serialized and returned to Git over standard |
| 190 | +output (7) and GCM Core terminates with a successful exit code. |
| 191 | + |
| 192 | +## Host provider |
| 193 | + |
| 194 | +Host providers implement the `IHostProvider` interface. They can choose to |
| 195 | +directly implement the interface they can also derive from the `HostProvider` |
| 196 | +abstract class (which itself implements the `IHostProvider` interface). |
| 197 | + |
| 198 | +The `HostProvider` abstract class implements the |
| 199 | +`Get|Store|EraseCredentialAsync` methods and instead has a |
| 200 | +`GenerateCredentialAsync` and `GetCredentialKey` abstract methods. Calls to |
| 201 | +`get`, `store`, or `erase` result in first a call to `GetCredentialKey` which |
| 202 | +should return a stable and unique "key" for the request. This forms the key for |
| 203 | +any stored credential in the credential store. During a `get` operation the |
| 204 | +credential store is queried for an existing credential with the computed key. |
| 205 | +If a credential is found it is returned immediately. Similarly, calls to `store` |
| 206 | +and `erase` are handles automatically to store credentials against, and erase |
| 207 | +credentials matching the computed key. Methods are implemented as `virtual` |
| 208 | +meaning you can always override this behaviour, for example to clear other |
| 209 | +custom caches on an `erase` request, without having to reimplement the |
| 210 | +lookup/store credential logic. |
| 211 | + |
| 212 | +Host providers are queried in turn (registration order) via the |
| 213 | +`IHostProvider.IsSupported` method and passed the input recieved from Git. If |
| 214 | +the provider recognises the request, for example by a matching known host name, |
| 215 | +they can return `true`. If the provider wants to cancel and abort an |
| 216 | +authentication request, for example if this is a HTTP (not HTTPS) request for a |
| 217 | +known host, they should still return `true` and later cancel the request. |
| 218 | + |
| 219 | +Depending on the request from Git, one of `GetCredentialAsync` (for `get` |
| 220 | +requests), `StoreCredentialAsync` (for `store` requests) or |
| 221 | +`EraseCredentialAsync` (for `erase` requests) will be called. The argument |
| 222 | +`InputArguments` contains the request information passed over standard input |
| 223 | +from Git/the caller; the same as was passed to `IsSupported`. |
| 224 | + |
| 225 | +The return value for the `get` operation must be an `ICredential` that Git can |
| 226 | +use to complete authentication. |
| 227 | + |
| 228 | +> **Note:** |
| 229 | +> |
| 230 | +> The credential can also be an instance where both username and password are |
| 231 | +> the empty string, to signal to Git it should let cURL use "any auth" |
| 232 | +> detection - typically to use Windows Integrated Authentication. |
| 233 | +
|
| 234 | +There are no return values for the `store` and `erase` operations as Git ignores |
| 235 | +any output or exit codes for these commands. Failures for these operations are |
| 236 | +best communicated via writing to the Standard Error stream via |
| 237 | +`ICommandContext.Streams.Error`. |
| 238 | + |
| 239 | +## Command context |
| 240 | + |
| 241 | +The `ICommandContext` which contains numerous services which are useful for |
| 242 | +interacting with various platform subsystems, such as the file system or |
| 243 | +environment variables. All services on the command context are exposed as |
| 244 | +interfaces for ease of testing and portability between different operating |
| 245 | +systems and platforms. |
| 246 | + |
| 247 | +Component|Description |
| 248 | +-|- |
| 249 | +CredentialStore|A secure operating system controlled location for storing and retrieving `ICredential` objects. |
| 250 | +Settings|Abstraction over all GCM Core settings. |
| 251 | +Streams|Abstraction over standard input, output and error streams connected to the parent process (typically Git). |
| 252 | +Terminal|Provides interactions with an attached terminal, if it exists. |
| 253 | +SessionManager|Provides information about the current user session. |
| 254 | +Trace|Provides tracing information that may be useful for debugging issues in the wild. Secret information MUST be filtered out completely or via the `Write___Secret` method(s). |
| 255 | +FileSystem|Abstraction over file system operations. |
| 256 | +HttpClientFactory|Factory for creating `HttpClient` instances that are configured with the correct user agent, headers, and proxy settings. |
| 257 | +Git|Provides interactions with Git and Git configuration. |
| 258 | +Environment|Abstraction over the current system/user environment variables. |
| 259 | +SystemPrompts|Provides services for showing system/OS native credential prompts. |
| 260 | + |
| 261 | +## Error handling and tracing |
| 262 | + |
| 263 | +GCM Core operates a 'fail fast' approach to unrecoverable errors. This usually |
| 264 | +means throwing an `Exception` which will propagate up to the entry-point and be |
| 265 | +caught, a non-zero exit code returned, and the error message printed with the |
| 266 | +"fatal:" prefix. For errors originating from interop/native code, you should |
| 267 | +throw an exception of the `InteropException` type. Error messages in exceptions |
| 268 | +should be human readable. When there is a known or user-fixable issue, |
| 269 | +instructions on how to self-rememdy the issue, or links to relevant |
| 270 | +documentation should be given. |
| 271 | + |
| 272 | +Warnings can be emitted over the standard error stream |
| 273 | +(`ICommandContext.Streams.Error`) when you want to alert the user to a potential |
| 274 | +issue with their configuration that does not necessarily stop the |
| 275 | +operation/authentication. |
| 276 | + |
| 277 | +The `ITrace` component can be found on the `ICommandContext` object or passed in |
| 278 | +directly to some constructors. Verbose and diagnostic information is be written |
| 279 | +to the trace object in most places of GCM Core. |
0 commit comments