Property | Value |
---|---|
Author(s) | Matthew John Cheetham (@mjcheetham) |
Revision | 1.0 |
Last updated | 2020-06-22 |
Git Credential Manger Core, the cross-platform and cross-host Git credential helper, can be extended to support any Git hosting service allowing seamless authentication to secured Git repositories by implementing and registering a "host provider".
Git Credential Manager Core (GCM Core) is a host and platform agnostic Git credential helper application. Support for authenticating to any Git hosting service can be added to GCM Core by creating a custom "host provider" and registering it within the product. Host providers can be submitted via a pull request on GitHub at https://github.com/microsoft/Git-Credential-Manager-Core.
This document outlines the required and expected behaviour of a host provider, and what is required to implement and register one.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this specification are to be interpreted as described in [RFC2119].
Throughout this document you may see multiple abbreviations of product names and security or credential objects.
"Git Credential Manager Core" is abbreviated to "GCM Core". "Git Credential Manager for Windows" is abbreviated to "GCM for Windows" or "GCM Windows". "Git Credential Manager for Mac & Linux" is abbreviated to "GCM for Mac/Linux" or "GCM Mac/Linux".
OAuth2 [RFC6749] "access tokens" are abbreviated to "ATs" and "refresh tokens" to "RTs". "Personal Access Tokens" are abbreviated to "PATs".
Writing and adding a host provider to GCM Core requires two main actions:
implementing the IHostProvider
interface, and registering an instance of the
provider with the application via the host provider registry.
Host providers MUST implement the IHostProvider
interface. They can choose to
directly implement the interface they MAY derive from the HostProvider
abstract class (which itself implements the IHostProvider
interface) - see
§2.6.
Implementors MUST implement all interface properties and abstract methods.
The Id
and Name
properties MUST be implemented and MUST NOT return default
or empty values.
The Id
field MUST be unique over the set of all providers, or
else an error will be thrown at registration time. The Id
field MAY be a
unique random string of characters and digits such as a UUID, but it is
RECOMMENDED to use a human-readable value consisting of letter characters in the
range [a-z] only.
The Name
property MUST be a human readable string and MUST identify the Git
hosting service this provider supports.
The SupportedAuthorityIds
property MUST return an instance of an object and
NOT a null
reference. Populating this collection with values is OPTIONAL but
highly RECOMMENDED. You should return a set of stable identifiers of all
authorities that the provider supports authentication against.
Host providers must provide an instance of their IHostProvider
type to the
GCM Core application host provider registry to be considered for handling
requests.
The main GCM Core Application
object has one principal registry which you can
register providers with by calling the RegisterProvider
method.
The default host provider registry in GCM Core will call each host provider in the order they were registered in, unless the user has overridden the provider selection process.
There are no rules or restrictions on the ordering of host providers, except
that the GenericHostProvider
MUST be registered last. The generic provider is
a catch-all provider implementation that will handle any request in a standard
way.
The IsSupported
method will be called on all registered host providers in-turn
on the invocation of a get
, store
, or erase
request. The first host
provider to return true
will be called upon to handle the specific request.
If the user has overridden the host provider selection process, a specific host
provider may be selected instead, and the IsSupported
method will NOT be
called.
This method MUST return true
if and only if the provider understands the
request and can serve or handle the request. If the provider does not know how
to handle the request it MUST return false
instead.
The IsSupported
method MUST return true
if the host provider would like to
cancel the authentication operation based on the current context or input.
For example, if provider requires a secure protocol but the requested protocol
for a supported hostname is http
and not https
.
Host providers MUST instead cancel the request from the GetCredentialAsync
method by throwing an Exception
. Implementors MUST provide detailed
information regarding the reason why the authentication cannot continue, for
example "HTTP is not secure, please use HTTPS".
The GetCredentialAsync
method will be called when a get
request is made.
The method MUST return an instance of an ICredential
capable of fulfilling the
specific access request. The argument passed to GetCredentialAsync
contains
properties indicating the required protocol
and host
for this request. The
username
and path
properties are OPTIONAL, however if they are present, they
MUST be considered and used to direct the authentication.
The host provider MAY attempt to locate any existing credential, stored by the
StoreCredentialAsync
method, before resorting to the creation a new one.
The host provider MAY choose to check if a stored credential is still valid by inspecting any stored metadata associated with the value. A host provider MAY also choose to further validate a retrieved stored credential by making a web request. However, it is NOT RECOMMENDED to make any request that is known to be slow or that typically produces inconclusive validation results.
If a provider chooses to make a validation web request and that request fails or is inconclusive, it SHOULD assume the credential is still valid and return it anyway, letting Git (the caller) attempt to use it and validate it itself.
The returned ICredential
MAY leave both the username and password values as
the empty string or null
. This signals to Git (or rather cURL) that it should
negotiate the authentication mechanism with the remote itself. This is typically
used for Windows Integrated Authentication.
When it is not possible to locate an existing credential suitable for the current request, a host provider SHOULD prompt the user to complete an authentication flow.
The method, modes, and interactions for performing authentication will vary widely between Git hosting services and their supported authentication authorities. A host provider SHOULD attempt to detect the best authentication experience given the current environment or context, and select that one to attempt first.
Host providers are RECOMMENDED to attempt authentication mechanisms that do not require user interaction if possible. If there are multiple authentication mechanisms that could be equally considered "best" they MAY prompt the user to make a selection. Host providers MAY wish to remember such a selection for future use, however they MUST make it clear how to clear this stored selection to the user.
If interaction is required to complete authentication a host provider MUST first
check if interaction has been disabled (ISettings.IsInteractionAllowed
), and
an exception MUST be thrown if interaction has been disallowed.
Authentication prompts that display a graphical user interface such as a window are MUST be preferred when an interactive "desktop" session is available.
If an authentication prompt is required when an interactive session is not
available and a terminal/TTY is attached then a provider MUST first check if
terminal prompts are enabled (ISettings.IsTerminalPromptsEnabled
), and an
exception MUST be thrown if interaction has been disallowed.
Host providers MAY store credentials at various stages of a typical
authentication flow, or when explicitly requested to do so in a call to
StoreCredentialAsync
.
Providers SHOULD use the credential store (exposed as ICredentialStore
) to
persist secret values and credential entities such as passwords, PATs and OAuth
tokens.
The typical Git credential helper call pattern is one call to get
, followed by
either a store
request in case of a HTTP 200 (OK) response, or erase
in case
of HTTP 401 (Unauthorized) response. In some cases there is additional context
that is present as part of the get
request or during the generation of a new
credential that is not present during the subsequent call to store
(or
erase
). In these cases providers MAY store the credential during the get
rather than, or as well as during the store
.
Host providers MAY store multiple credentials or tokens in the same request if it is required. One example where multiple credential storage is needed is with OAuth2 access tokens (AT) and refresh tokens (RT). Both the AT and RT SHOULD be stored in the same location using the credential store with complementary credential keys.
If host providers have stored credentials in the credential store, they MUST
respond to requests to erase them in calls to EraseCredentialAsync
.
If a host provider cannot locate a credential to erase it MUST NOT raise an error and MUST exit successfully. A warning message MAY be emitted to the tracing system.
Host providers MUST NOT perform their own repeated validation of credentials for the purposes of ignoring the request to erase them. The ultimate authority on the validity of a credential is the caller (Git).
Providers MAY validate any additional or ancillary credentials (such as OAuth RTs) are still valid when a request to erase the primary credential (such as an OAuth AT) is made, and choose not to delete those additional credentials. The primary credential MUST still always be erased in all cases.
The HostProvider
abstract base class is provided for the convenience of host
provider implementors. This base class implements most required methods of the
IHostProvider
interface with common credential recall and storage behaviour.
The GetCredentialAsync
, StoreCredentialAsync
, and EraseCredentialAsync
methods are implemented as virtual
meaning they MAY be overridden by derived
classes to customise the behaviour of those operations. It is NOT RECOMMENDED
to derive from the HostProvider
base class if the implementor must override
most of the methods as implemented - implementors SHOULD implement the
IHostProvider
interface directly instead.
Implementors that choose to derive from this base class MUST implement all
abstract methods and properties. The two primary abstract methods to implement
are GetCredentialKey
and GenerateCredentialAsync
.
The GetCredentialKey
method MUST return a string that forms a key for storing
credentials for this provider and request. The key returned MUST be stable -
i.e, it MUST return the same value given the same or equivalent input arguments.
This key is used by the GetCredentialAsync
method to first check for any
existing credential stored in the credential store, returning it if found.
The key is also similarly used by the StoreCredentialAsync
and
EraseCredentialAsync
methods to store and erase, respectively, credentials
passed as arguments by Git.
The GenerateCredentialAsync
method will be called if an existing credential
with a matching credential key is not found in the credential store.
This method MUST return a freshly created/generated credential and not any existing or stored one. It MAY use existing or stored ancillary data or tokens, such as OAuth refresh tokens, to generate the new token (such as an OAuth AT).
Host providers MAY wish to store extra data about authentications or users collected or produced during authentication operations. These SHOULD be stored in a per-user, local location such as the user's home or profile directory.
Secrets, credentials or other sensitive data SHOULD be stored in the credential store, or otherwise protected by some form of per-user, local encryption.
In the case of stored data caches, providers SHOULD invalidate relevant parts
of, or the entire cache, when a call to EraseCredentialAsync
is made.
Host providers MAY wish to make use of platform or operating system specific features such as native APIs and native graphical user interfaces, in order to offer a better authentication experience.
Host providers MUST function without the presence of a helper, even if that function is to fail gracefully with a user-friendly error message, including a remedy to correct their installation. Host providers SHOULD always offer a terminal/TTY or text-based authentication mechanism alongside any graphical interface provided by a helper.
In order to achieve this host providers MUST introduce an out-of-process "helper" executable that can be invoked from the main GCM Core process. This allows the "helper" executable full implementation freedom of runtime, language, etc.
Communications between the main and helper processes MAY use any IPC mechanism available. It is RECOMMENDED implementors use standard input/output streams or file descriptors to send and receive data as this is consistent with how Git and GCM Core communicate. UNIX sockets or Windows Named Pipes MAY also be used when an ongoing back-and-forth communication is required.
It is RECOMMENDED that helper discovery is achieved by simply checking for the presence of the expected executable file. The name and path of the helper executable SHOULD be configurable by the user via Git's configuration files.
If an unrecoverable error occurs a host provider MUST throw an exception and MUST include detailed failure information in the error message. If the reason for failure can be fixed by the user the error message MUST include instructions to fix the problem, or a link to online documentation.
In the case of a recoverable error, host providers SHOULD print a warning message to the standard error stream, and MUST include the error information and the recovery steps take in the trace log.
In the case of an authentication error, providers SHOULD attempt to prompt the user again with a message indicating the incorrect authentication details have been entered.