示例#1
0
        public override async Task <ICredential> GenerateCredentialAsync(InputArguments input)
        {
            ThrowIfDisposed();

            // We should not allow unencrypted communication and should inform the user
            if (StringComparer.OrdinalIgnoreCase.Equals(input.Protocol, "http"))
            {
                throw new Exception("Unencrypted HTTP is not supported for GitHub. Ensure the repository remote URL is using HTTPS.");
            }

            Uri remoteUri = input.GetRemoteUri();

            string service = GetServiceName(input);

            AuthenticationModes authModes = await GetSupportedAuthenticationModesAsync(remoteUri);

            AuthenticationPromptResult promptResult = await _gitHubAuth.GetAuthenticationAsync(remoteUri, input.UserName, authModes);

            switch (promptResult.AuthenticationMode)
            {
            case AuthenticationModes.Basic:
                GitCredential patCredential = await GeneratePersonalAccessTokenAsync(remoteUri, promptResult.Credential);

                // HACK: Store the PAT immediately in case this PAT is not valid for SSO.
                // We don't know if this PAT is valid for SAML SSO and if it's not Git will fail
                // with a 403 and call neither 'store' or 'erase'. The user is expected to fiddle with
                // the PAT permissions manually on the web and then retry the Git operation.
                // We must store the PAT now so they can resume/repeat the operation with the same,
                // now SSO authorized, PAT.
                // See: https://github.com/GitCredentialManager/git-credential-manager/issues/133
                Context.CredentialStore.AddOrUpdate(service, patCredential.Account, patCredential.Password);
                return(patCredential);

            case AuthenticationModes.Browser:
                return(await GenerateOAuthCredentialAsync(remoteUri, useBrowser : true));

            case AuthenticationModes.Device:
                return(await GenerateOAuthCredentialAsync(remoteUri, useBrowser : false));

            case AuthenticationModes.Pat:
                // The token returned by the user should be good to use directly as the password for Git
                string token = promptResult.Credential.Password;

                // Resolve the GitHub user handle if we don't have a specific username already from the
                // initial request. The reason for this is GitHub requires a (any?) value for the username
                // when Git makes calls to GitHub.
                string userName = promptResult.Credential.Account;
                if (userName is null)
                {
                    GitHubUserInfo userInfo = await _gitHubApi.GetUserInfoAsync(remoteUri, token);

                    userName = userInfo.Login;
                }

                return(new GitCredential(userName, token));

            default:
                throw new ArgumentOutOfRangeException(nameof(promptResult));
            }
        }
示例#2
0
        private async Task <GitCredential> GenerateOAuthCredentialAsync(Uri targetUri)
        {
            OAuth2TokenResult result = await _gitHubAuth.GetOAuthTokenAsync(targetUri, GitHubOAuthScopes);

            // Resolve the GitHub user handle
            GitHubUserInfo userInfo = await _gitHubApi.GetUserInfoAsync(targetUri, result.AccessToken);

            return(new GitCredential(userInfo.Login, result.AccessToken));
        }
示例#3
0
        private async Task <GitCredential> GeneratePersonalAccessTokenAsync(Uri targetUri, ICredential credentials)
        {
            AuthenticationResult result = await _gitHubApi.CreatePersonalTokenAsync(
                targetUri, credentials, null, GitHubCredentialScopes);

            string token = null;

            if (result.Type == GitHubAuthenticationResultType.Success)
            {
                Context.Trace.WriteLine($"Token acquisition for '{targetUri}' succeeded");

                token = result.Token;
            }
            else if (result.Type == GitHubAuthenticationResultType.TwoFactorApp ||
                     result.Type == GitHubAuthenticationResultType.TwoFactorSms)
            {
                bool isSms = result.Type == GitHubAuthenticationResultType.TwoFactorSms;

                string authCode = await _gitHubAuth.GetTwoFactorCodeAsync(targetUri, isSms);

                result = await _gitHubApi.CreatePersonalTokenAsync(targetUri, credentials, authCode, GitHubCredentialScopes);

                if (result.Type == GitHubAuthenticationResultType.Success)
                {
                    Context.Trace.WriteLine($"Token acquisition for '{targetUri}' succeeded.");

                    token = result.Token;
                }
            }

            if (token != null)
            {
                // Resolve the GitHub user handle
                GitHubUserInfo userInfo = await _gitHubApi.GetUserInfoAsync(targetUri, token);

                return(new GitCredential(userInfo.Login, token));
            }

            throw new Exception($"Interactive logon for '{targetUri}' failed.");
        }