Property | Value |
---|---|
Author(s) | Matthew John Cheetham (@mjcheetham) |
Revision | 1.2 |
Last updated | 2021-01-08 |
- 1.0. Initial revision.
- 1.1. Replaced
GetCredentialKey
withGetServiceName
. - 1.2. Added new
IsSupported(HttpResponseMessage)
overload, the optionalICommandProvider
interface, and registration priorities.
Git Credential Manger, 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 (GCM) is a host and platform agnostic Git credential helper application. Support for authenticating to any Git hosting service can be added to GCM 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/GitCredentialManager/git-credential-manager.
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" is abbreviated to "GCM". "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 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 application host provider registry to be considered for handling
requests.
The main GCM Application
object has one principal registry which you can
register providers with by calling the RegisterProvider
method.
The default host provider registry in GCM has multiple priority levels that host providers can be registered at: High, Normal, and Low.
For each priority level (starting with High, then Normal, then Low), the registry 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 and at the Low priority.
The generic provider is a catch-all provider implementation that will handle any
request in a standard way.
The IsSupported(InputArguments)
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(InputArguments)
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.
If no host provider returns true
to a call to the IsSupported(InputArguments)
method for a each host provider priority level, then a HTTP HEAD request will be
made to the remote URL and each host provider will be be called via the
IsSupported(HttpResponseMessage)
method. A host provider SHOULD use this call
to check for recognised on-premises instances (for example, by inspecting
response headers) and return true
if it wishes to be called upon to handle the
credential request, otherwise it MUST return false
.
Host providers SHOULD NOT make further network calls if possible during any of
the IsSupported
method overloads to avoid degrading the performance of the
overall application.
The IsSupported
methods 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 service names.
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 primary abstract method to implement
is GenerateCredentialAsync
.
There is also an additional virtual
method named GetServiceName
that is used
by the default implementations of the Get|Store|EraseCredentialAsync
methods
to locate and store credentials.
The GetServiceName
virtual method, if overriden, MUST return a string that
identifies the service/provider for this request, and is used for storing
credentials. The value returned MUST be stable - i.e, it MUST return the same
value given the same or equivalent input arguments.
By default this method returns the full remote URI, without a trailing slash, including protocol/scheme, hostname, and path if present in the input arguments. Any username in the input arguments is never included in the URI.
The GenerateCredentialAsync
method will be called if an existing credential
with a matching service (from GetServiceName
) and account 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 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 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.
If a host provider wishes to surface custom commands the SHOULD implement the
ICommandProvider
interface.
Each provider is given the opportunity to create a single ProviderCommand
instance to which further sub-commands can be parented to. Commanding is
provided by the System.CommandLine
API library [1].
There are no limitations on what format sub-commands, arguments, or options must take, but implementors SHOULD attempt to follow existing practices and styles.
System.CommandLine
API (https://github.com/dotnet/command-line-api)