Beispiel #1
0
        /// <summary>
        /// Gets a configured authentication object for 'github.com'.
        /// </summary>
        /// <param name="targetUri">The uniform resource indicator of the resource which requires
        /// authentication.</param>
        /// <param name="tokenScope">The desired scope of any personal access tokens acquired.</param>
        /// <param name="personalAccessTokenStore">A secure secret store for any personal access
        /// tokens acquired.</param>
        /// <param name="authentication">(out) The authentication object if successful.</param>
        /// <returns>True if success; otherwise false.</returns>
        public static BaseAuthentication GetAuthentication(
            TargetUri targetUri,
            TokenScope tokenScope,
            ICredentialStore personalAccessTokenStore,
            AcquireCredentialsDelegate acquireCredentialsCallback,
            AcquireAuthenticationCodeDelegate acquireAuthenticationCodeCallback,
            AuthenticationResultDelegate authenticationResultCallback)
        {
            const string GitHubBaseUrlHost = "github.com";

            BaseAuthentication authentication = null;

            BaseSecureStore.ValidateTargetUri(targetUri);
            if (personalAccessTokenStore == null)
            {
                throw new ArgumentNullException("personalAccessTokenStore", "The `personalAccessTokenStore` is null or invalid.");
            }

            if (targetUri.DnsSafeHost.EndsWith(GitHubBaseUrlHost, StringComparison.OrdinalIgnoreCase))
            {
                authentication = new Authentication(tokenScope, personalAccessTokenStore, acquireCredentialsCallback, acquireAuthenticationCodeCallback, authenticationResultCallback);
                Git.Trace.WriteLine($"created GitHub authentication for '{targetUri}'.");
            }
            else
            {
                authentication = null;
                Git.Trace.WriteLine($"not github.com, authentication creation aborted.");
            }

            return(authentication);
        }
Beispiel #2
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="tokenScope"></param>
        /// <param name="personalAccessTokenStore"></param>
        public Authentication(
            TokenScope tokenScope,
            ICredentialStore personalAccessTokenStore,
            AcquireCredentialsDelegate acquireCredentialsCallback,
            AcquireAuthenticationCodeDelegate acquireAuthenticationCodeCallback,
            AuthenticationResultDelegate authenticationResultCallback)
        {
            if (tokenScope == null)
            {
                throw new ArgumentNullException("tokenScope", "The parameter `tokenScope` is null or invalid.");
            }
            if (personalAccessTokenStore == null)
            {
                throw new ArgumentNullException("personalAccessTokenStore", "The parameter `personalAccessTokenStore` is null or invalid.");
            }
            if (acquireCredentialsCallback == null)
            {
                throw new ArgumentNullException("acquireCredentialsCallback", "The parameter `acquireCredentialsCallback` is null or invalid.");
            }
            if (acquireAuthenticationCodeCallback == null)
            {
                throw new ArgumentNullException("acquireAuthenticationCodeCallback", "The parameter `acquireAuthenticationCodeCallback` is null or invalid.");
            }

            TokenScope = tokenScope;

            PersonalAccessTokenStore = personalAccessTokenStore;
            Authority = new Authority();

            AcquireCredentialsCallback        = acquireCredentialsCallback;
            AcquireAuthenticationCodeCallback = acquireAuthenticationCodeCallback;
            AuthenticationResultCallback      = authenticationResultCallback;
        }
        private static HttpContent GetTokenJsonContent(TargetUri targetUri, TokenScope scope)
        {
            const string HttpJsonContentType = "application/x-www-form-urlencoded";
            const string JsonContentFormat   = @"{{ ""scopes"": {0}, ""note"": ""git: {1} on {2} at {3:dd-MMM-yyyy HH:mm}"" }}";

            StringBuilder scopesBuilder = new StringBuilder();

            scopesBuilder.Append('[');

            foreach (var item in scope.ToString().Split(' '))
            {
                scopesBuilder.Append("\"")
                .Append(item)
                .Append("\"")
                .Append(", ");
            }

            // remove trailing ", "
            if (scopesBuilder.Length > 0)
            {
                scopesBuilder.Remove(scopesBuilder.Length - 2, 2);
            }

            scopesBuilder.Append(']');

            string jsonContent = string.Format(JsonContentFormat, scopesBuilder, targetUri, Environment.MachineName, DateTime.Now);

            return(new StringContent(jsonContent, Encoding.UTF8, HttpJsonContentType));
        }
Beispiel #4
0
        /// <summary>
        /// Creates a new authentication
        /// </summary>
        /// <param name="targetUri">
        /// The uniform resource indicator of the resource which requires authentication.
        /// </param>
        /// <param name="tokenScope">The desired scope of any personal access tokens acquired.</param>
        /// <param name="personalAccessTokenStore">
        /// A secure secret store for any personal access tokens acquired.
        /// </param>
        public Authentication(
            RuntimeContext context,
            TargetUri targetUri,
            TokenScope tokenScope,
            ICredentialStore personalAccessTokenStore,
            AcquireCredentialsDelegate acquireCredentialsCallback,
            AcquireAuthenticationCodeDelegate acquireAuthenticationCodeCallback,
            AuthenticationResultDelegate authenticationResultCallback)
            : base(context)
        {
            if (context is null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            TokenScope = tokenScope
                         ?? throw new ArgumentNullException(nameof(tokenScope));
            PersonalAccessTokenStore = personalAccessTokenStore
                                       ?? throw new ArgumentNullException(nameof(personalAccessTokenStore));
            AcquireCredentialsCallback = acquireCredentialsCallback
                                         ?? throw new ArgumentNullException(nameof(acquireCredentialsCallback));
            AcquireAuthenticationCodeCallback = acquireAuthenticationCodeCallback
                                                ?? throw new ArgumentNullException(nameof(acquireAuthenticationCodeCallback));

            Authority = new Authority(context, NormalizeUri(targetUri));
            AuthenticationResultCallback = authenticationResultCallback;
        }
Beispiel #5
0
        /// <summary>
        /// Gets a configured authentication object for 'github.com'.
        /// <para/>
        /// Returns a `<see cref="Authentication"/>` if successful; otherwise `<see langword="null"/>`.
        /// </summary>
        /// <param name="targetUri">The uniform resource indicator of the resource which requires authentication.</param>
        /// <param name="tokenScope">The desired scope of any personal access tokens acquired.</param>
        /// <param name="personalAccessTokenStore">A secure secret store for any personal access tokens acquired.</param>
        /// <param name="authentication">(out) The authentication object if successful.</param>
        public static BaseAuthentication GetAuthentication(
            RuntimeContext context,
            TargetUri targetUri,
            TokenScope tokenScope,
            ICredentialStore personalAccessTokenStore,
            AcquireCredentialsDelegate acquireCredentialsCallback,
            AcquireAuthenticationCodeDelegate acquireAuthenticationCodeCallback,
            AuthenticationResultDelegate authenticationResultCallback)
        {
            if (context is null)
            {
                throw new ArgumentNullException(nameof(context));
            }
            if (targetUri is null)
            {
                throw new ArgumentNullException(nameof(targetUri));
            }
            if (personalAccessTokenStore is null)
            {
                throw new ArgumentNullException(nameof(personalAccessTokenStore));
            }

            BaseAuthentication authentication = null;

            if (targetUri.DnsSafeHost.EndsWith(GitHubBaseUrlHost, StringComparison.OrdinalIgnoreCase))
            {
                var normalizedTargetUri = NormalizeUri(targetUri);
                authentication = new Authentication(context,
                                                    normalizedTargetUri,
                                                    tokenScope,
                                                    personalAccessTokenStore,
                                                    acquireCredentialsCallback,
                                                    acquireAuthenticationCodeCallback,
                                                    authenticationResultCallback);
                context.Trace.WriteLine($"created GitHub authentication for '{normalizedTargetUri}'.");
            }
            else
            {
                authentication = null;
            }

            return(authentication);
        }
        /// <summary>
        /// Creates a new authentication
        /// </summary>
        /// <param name="targetUri">
        /// The uniform resource indicator of the resource which requires authentication.
        /// </param>
        /// <param name="tokenScope">The desired scope of any personal access tokens acquired.</param>
        /// <param name="personalAccessTokenStore">
        /// A secure secret store for any personal access tokens acquired.
        /// </param>
        public Authentication(
            TargetUri targetUri,
            TokenScope tokenScope,
            ICredentialStore personalAccessTokenStore,
            AcquireCredentialsDelegate acquireCredentialsCallback,
            AcquireAuthenticationCodeDelegate acquireAuthenticationCodeCallback,
            AuthenticationResultDelegate authenticationResultCallback)
        {
            TokenScope = tokenScope
                         ?? throw new ArgumentNullException("tokenScope", "The parameter `tokenScope` is null or invalid.");
            PersonalAccessTokenStore = personalAccessTokenStore
                                       ?? throw new ArgumentNullException("personalAccessTokenStore", "The parameter `personalAccessTokenStore` is null or invalid.");
            AcquireCredentialsCallback = acquireCredentialsCallback
                                         ?? throw new ArgumentNullException("acquireCredentialsCallback", "The parameter `acquireCredentialsCallback` is null or invalid.");
            AcquireAuthenticationCodeCallback = acquireAuthenticationCodeCallback
                                                ?? throw new ArgumentNullException("acquireAuthenticationCodeCallback", "The parameter `acquireAuthenticationCodeCallback` is null or invalid.");

            Authority = new Authority(NormalizeUri(targetUri));
            AuthenticationResultCallback = authenticationResultCallback;
        }
Beispiel #7
0
        public async Task <AuthenticationResult> AcquireToken(
            TargetUri targetUri,
            string username,
            string password,
            string authenticationCode,
            TokenScope scope)
        {
            const string GitHubOptHeader = "X-GitHub-OTP";

            Token token = null;

            using (HttpClientHandler handler = targetUri.HttpClientHandler)
                using (HttpClient httpClient = new HttpClient(handler)
                {
                    Timeout = TimeSpan.FromMilliseconds(RequestTimeout)
                })
                {
                    httpClient.DefaultRequestHeaders.Add("User-Agent", Global.UserAgent);
                    httpClient.DefaultRequestHeaders.Add("Accept", GitHubApiAcceptsHeaderValue);

                    string basicAuthValue = String.Format("{0}:{1}", username, password);
                    byte[] authBytes      = Encoding.UTF8.GetBytes(basicAuthValue);
                    basicAuthValue = Convert.ToBase64String(authBytes);

                    httpClient.DefaultRequestHeaders.Add("Authorization", "Basic " + basicAuthValue);

                    if (!String.IsNullOrWhiteSpace(authenticationCode))
                    {
                        httpClient.DefaultRequestHeaders.Add(GitHubOptHeader, authenticationCode);
                    }

                    const string HttpJsonContentType = "application/x-www-form-urlencoded";
                    const string JsonContentFormat   = @"{{ ""scopes"": {0}, ""note"": ""git: {1} on {2} at {3:dd-MMM-yyyy HH:mm}"" }}";

                    StringBuilder scopesBuilder = new StringBuilder();
                    scopesBuilder.Append('[');

                    foreach (var item in scope.ToString().Split(' '))
                    {
                        scopesBuilder.Append("\"")
                        .Append(item)
                        .Append("\"")
                        .Append(", ");
                    }

                    // remove trailing ", "
                    if (scopesBuilder.Length > 0)
                    {
                        scopesBuilder.Remove(scopesBuilder.Length - 2, 2);
                    }

                    scopesBuilder.Append(']');

                    string jsonContent = String.Format(JsonContentFormat, scopesBuilder, targetUri, Environment.MachineName, DateTime.Now);

                    using (StringContent content = new StringContent(jsonContent, Encoding.UTF8, HttpJsonContentType))
                        using (HttpResponseMessage response = await httpClient.PostAsync(_authorityUrl, content))
                        {
                            Git.Trace.WriteLine($"server responded with {response.StatusCode}.");

                            switch (response.StatusCode)
                            {
                            case HttpStatusCode.OK:
                            case HttpStatusCode.Created:
                            {
                                string responseText = await response.Content.ReadAsStringAsync();

                                Match tokenMatch;
                                if ((tokenMatch = Regex.Match(responseText, @"\s*""token""\s*:\s*""([^""]+)""\s*", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase)).Success &&
                                    tokenMatch.Groups.Count > 1)
                                {
                                    string tokenText = tokenMatch.Groups[1].Value;
                                    token = new Token(tokenText, TokenType.Personal);
                                }

                                if (token == null)
                                {
                                    Git.Trace.WriteLine($"authentication for '{targetUri}' failed.");
                                    return(new AuthenticationResult(GitHubAuthenticationResultType.Failure));
                                }
                                else
                                {
                                    Git.Trace.WriteLine($"authentication success: new personal acces token for '{targetUri}' created.");
                                    return(new AuthenticationResult(GitHubAuthenticationResultType.Success, token));
                                }
                            }

                            case HttpStatusCode.Unauthorized:
                            {
                                if (String.IsNullOrWhiteSpace(authenticationCode) &&
                                    response.Headers.Any(x => String.Equals(GitHubOptHeader, x.Key, StringComparison.OrdinalIgnoreCase)))
                                {
                                    var mfakvp = response.Headers.First(x => String.Equals(GitHubOptHeader, x.Key, StringComparison.OrdinalIgnoreCase) && x.Value != null && x.Value.Count() > 0);

                                    if (mfakvp.Value.First().Contains("app"))
                                    {
                                        Git.Trace.WriteLine($"two-factor app authentication code required for '{targetUri}'.");
                                        return(new AuthenticationResult(GitHubAuthenticationResultType.TwoFactorApp));
                                    }
                                    else
                                    {
                                        Git.Trace.WriteLine($"two-factor sms authentication code required for '{targetUri}'.");
                                        return(new AuthenticationResult(GitHubAuthenticationResultType.TwoFactorSms));
                                    }
                                }
                                else
                                {
                                    Git.Trace.WriteLine($"authentication failed for '{targetUri}'.");
                                    return(new AuthenticationResult(GitHubAuthenticationResultType.Failure));
                                }
                            }

                            default:
                                Git.Trace.WriteLine($"authentication failed for '{targetUri}'.");
                                return(new AuthenticationResult(GitHubAuthenticationResultType.Failure));
                            }
                        }
                }
        }
        public async Task <AuthenticationResult> AcquireToken(
            TargetUri targetUri,
            string username,
            string password,
            string authenticationCode,
            TokenScope scope)
        {
            const string GitHubOptHeader = "X-GitHub-OTP";

            Token token = null;

            var options = new NetworkRequestOptions(true)
            {
                Authorization = new Credential(username, password),
                Timeout       = TimeSpan.FromMilliseconds(RequestTimeout),
            };

            options.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(GitHubApiAcceptsHeaderValue));
            options.Headers.Add(GitHubOptHeader, authenticationCode);

            // Create the authority Uri.
            var requestUri = targetUri.CreateWith(_authorityUrl);

            using (HttpContent content = GetTokenJsonContent(targetUri, scope))
                using (var response = await Network.HttpPostAsync(requestUri, content, options))
                {
                    Trace.WriteLine($"server responded with {response.StatusCode}.");

                    switch (response.StatusCode)
                    {
                    case HttpStatusCode.OK:
                    case HttpStatusCode.Created:
                    {
                        string responseText = await response.Content.ReadAsStringAsync();

                        Match tokenMatch;
                        if ((tokenMatch = Regex.Match(responseText, @"\s*""token""\s*:\s*""([^""]+)""\s*", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase)).Success &&
                            tokenMatch.Groups.Count > 1)
                        {
                            string tokenText = tokenMatch.Groups[1].Value;
                            token = new Token(tokenText, TokenType.Personal);
                        }

                        if (token == null)
                        {
                            Trace.WriteLine($"authentication for '{targetUri}' failed.");
                            return(new AuthenticationResult(GitHubAuthenticationResultType.Failure));
                        }
                        else
                        {
                            Trace.WriteLine($"authentication success: new personal access token for '{targetUri}' created.");
                            return(new AuthenticationResult(GitHubAuthenticationResultType.Success, token));
                        }
                    }

                    case HttpStatusCode.Unauthorized:
                    {
                        if (string.IsNullOrWhiteSpace(authenticationCode) &&
                            response.Headers.Any(x => string.Equals(GitHubOptHeader, x.Key, StringComparison.OrdinalIgnoreCase)))
                        {
                            var mfakvp = response.Headers.First(x => string.Equals(GitHubOptHeader, x.Key, StringComparison.OrdinalIgnoreCase) && x.Value != null && x.Value.Count() > 0);

                            if (mfakvp.Value.First().Contains("app"))
                            {
                                Trace.WriteLine($"two-factor app authentication code required for '{targetUri}'.");
                                return(new AuthenticationResult(GitHubAuthenticationResultType.TwoFactorApp));
                            }
                            else
                            {
                                Trace.WriteLine($"two-factor sms authentication code required for '{targetUri}'.");
                                return(new AuthenticationResult(GitHubAuthenticationResultType.TwoFactorSms));
                            }
                        }
                        else
                        {
                            Trace.WriteLine($"authentication failed for '{targetUri}'.");
                            return(new AuthenticationResult(GitHubAuthenticationResultType.Failure));
                        }
                    }

                    case HttpStatusCode.Forbidden:
                        // This API only supports Basic authentication. If a valid OAuth token is supplied
                        // as the password, then a Forbidden response is returned instead of an Unauthorized.
                        // In that case, the supplied password is an OAuth token and is valid and we don't need
                        // to create a new personal access token.
                        var contentBody = await response.Content.ReadAsStringAsync();

                        if (contentBody.Contains("This API can only be accessed with username and password Basic Auth"))
                        {
                            Trace.WriteLine($"authentication success: user supplied personal access token for '{targetUri}'.");

                            return(new AuthenticationResult(GitHubAuthenticationResultType.Success, new Token(password, TokenType.Personal)));
                        }
                        Trace.WriteLine($"authentication failed for '{targetUri}'.");
                        return(new AuthenticationResult(GitHubAuthenticationResultType.Failure));

                    default:
                        Trace.WriteLine($"authentication failed for '{targetUri}'.");
                        return(new AuthenticationResult(GitHubAuthenticationResultType.Failure));
                    }
                }
        }