Skip to content

Commit 8bcf927

Browse files
committed
docs: add Architecture overview
Add a document outlining the architecture of GCM Core.
1 parent f8ac4e4 commit 8bcf927

File tree

1 file changed

+216
-0
lines changed

1 file changed

+216
-0
lines changed

docs/architecture.md

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

0 commit comments

Comments
 (0)