/// <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); }
/// <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)); }
/// <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; }
/// <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; }
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)); } } }