private StringContent GetAccessTokenRequestBody(TargetUri targetUri, TokenScope tokenScope, TimeSpan?duration = null) { const string ContentBasicJsonFormat = "{{ \"scope\" : \"{0}\", \"displayName\" : \"Git: {1} on {2}\" }}"; const string ContentTimedJsonFormat = "{{ \"scope\" : \"{0}\", \"displayName\" : \"Git: {1} on {2}\", \"validTo\": \"{3:u}\" }}"; const string HttpJsonContentType = "application/json"; if (targetUri is null) { throw new ArgumentNullException(nameof(targetUri)); } if (tokenScope is null) { throw new ArgumentNullException(nameof(tokenScope)); } string tokenUrl = GetTargetUrl(targetUri, false); Trace.WriteLine($"creating access token scoped to '{tokenScope}' for '{targetUri}'"); string jsonContent = (duration.HasValue && duration.Value > TimeSpan.FromHours(1)) ? string.Format(Culture.InvariantCulture, ContentTimedJsonFormat, tokenScope, tokenUrl, Settings.MachineName, DateTime.UtcNow + duration.Value) : string.Format(Culture.InvariantCulture, ContentBasicJsonFormat, tokenScope, tokenUrl, Settings.MachineName); var content = new StringContent(jsonContent, Encoding.UTF8, HttpJsonContentType); return(content); }
public MsaAuthentication( RuntimeContext context, TokenScope tokenScope, ICredentialStore personalAccessTokenStore) : base(context, tokenScope, personalAccessTokenStore) { Authority = new Authority(context, DefaultAuthorityHost); }
/// <summary> /// Generates a "personal access token" or service specific, usage restricted access token. /// <para/> /// Returns a "personal access token" for the user if successful; otherwise `<see langword="null"/>`. /// </summary> /// <param name="targetUri">The target resource for which to acquire the personal access token for.</param> /// <param name="accessToken">Azure Directory access token with privileges to grant access to the target resource.</param> /// <param name="options">Set of options related to generation of personal access tokens.</param> protected async Task <Credential> GeneratePersonalAccessToken( TargetUri targetUri, Token accessToken, PersonalAccessTokenOptions options) { if (targetUri is null) { throw new ArgumentNullException(nameof(targetUri)); } if (accessToken is null) { throw new ArgumentNullException(nameof(accessToken)); } TokenScope requestedScope = TokenScope; if (options.TokenScope != null) { // Take the intersection of the authority scope and the requested scope requestedScope &= options.TokenScope; // If the result of the intersection is none, then fail if (string.IsNullOrWhiteSpace(requestedScope.Value)) { throw new InvalidOperationException("Invalid scope requested. Requested scope would result in no access privileges."); } } Credential credential = null; Token personalAccessToken; if ((personalAccessToken = await Authority.GeneratePersonalAccessToken(targetUri, accessToken, requestedScope, options.RequireCompactToken, options.TokenDuration)) != null) { credential = (Credential)personalAccessToken; Trace.WriteLine($"personal access token created for '{targetUri}'."); try { await PersonalAccessTokenStore.WriteCredentials(targetUri, credential); } catch (Exception exception) { System.Diagnostics.Debug.WriteLine(exception); Trace.WriteLine($"failed to write credentials to the secure store: {exception.GetType().Name}."); } } return(credential); }
/// <summary> /// Creates a new authentication broker based for the specified resource. /// <para/> /// Returns `<see langword="true"/>` if an authority could be determined; otherwise `<see langword="false"/>`. /// </summary> /// <param name="targetUri">The resource for which authentication is being requested.</param> /// <param name="scope">The scope of the access being requested.</param> /// <param name="personalAccessTokenStore">Storage container for personal access token secrets.</param> public static async Task <BaseAuthentication> GetAuthentication( RuntimeContext context, TargetUri targetUri, TokenScope scope, ICredentialStore personalAccessTokenStore) { if (context is null) { throw new ArgumentNullException(nameof(context)); } if (targetUri is null) { throw new ArgumentNullException(nameof(targetUri)); } if (scope is null) { throw new ArgumentNullException(nameof(scope)); } if (personalAccessTokenStore is null) { throw new ArgumentNullException(nameof(personalAccessTokenStore)); } BaseAuthentication authentication = null; var result = await DetectAuthority(context, targetUri); if (!result.HasValue) { return(null); } // Query for the tenant's identity Guid tenantId = result.Value; // empty identity is MSA, anything else is AAD if (tenantId == Guid.Empty) { context.Trace.WriteLine("MSA authority detected."); authentication = new MsaAuthentication(context, scope, personalAccessTokenStore); } else { context.Trace.WriteLine($"AAD authority for tenant '{tenantId}' detected."); authentication = new AadAuthentication(context, tenantId, scope, personalAccessTokenStore); (authentication as AadAuthentication).TenantId = tenantId; } return(authentication); }
/// <summary> /// Creates a new instance of `<see cref="AadAuthentication"/>`. /// </summary> /// <param name="tenantId"> /// URI of the responsible Azure tenant. /// <para/> /// Use `<see cref="Authentication.GetAuthentication"/>` to detect the tenant identity and create the authentication object. /// </param> /// <param name="tokenScope">The scope of all access tokens acquired by the authority.</param> /// <param name="personalAccessTokenStore">The secure secret store for storing any personal access tokens acquired.</param> /// <param name="adaRefreshTokenStore">The secure secret store for storing any Azure tokens acquired.</param> public AadAuthentication( RuntimeContext context, Guid tenantId, TokenScope tokenScope, ICredentialStore personalAccessTokenStore) : base(context, tokenScope, personalAccessTokenStore) { if (tenantId == Guid.Empty) { Authority = new Authority(context, VisualStudioTeamServices.Authentication.Authority.DefaultAuthorityHostUrl); } else { // Create an authority host URL in the format of https://login.microsoft.com/12345678-9ABC-DEF0-1234-56789ABCDEF0. string authorityHost = VisualStudioTeamServices.Authentication.Authority.GetAuthorityUrl(tenantId); Authority = new Authority(context, authorityHost); } }
protected Authentication( RuntimeContext context, TokenScope tokenScope, ICredentialStore personalAccessTokenStore) : base(context) { if (tokenScope is null) { throw new ArgumentNullException(nameof(tokenScope)); } if (personalAccessTokenStore is null) { throw new ArgumentNullException(nameof(personalAccessTokenStore)); } ClientId = DefaultClientId; Resource = DefaultResource; TokenScope = tokenScope; PersonalAccessTokenStore = personalAccessTokenStore; Authority = new Authority(context); }
public static bool Find(string value, out TokenScope vstsScope) { vstsScope = EnumerateValues().FirstOrDefault(v => StringComparer.OrdinalIgnoreCase.Equals(v.Value, value)); return(vstsScope != null); }
public bool Equals(TokenScope other) => Microsoft.Alm.Authentication.TokenScope.Equals(this as Microsoft.Alm.Authentication.TokenScope, other as Microsoft.Alm.Authentication.TokenScope);
public async Task <Token> GeneratePersonalAccessToken(TargetUri targetUri, Token authorization, TokenScope tokenScope, bool requireCompactToken, TimeSpan?tokenDuration) { if (targetUri is null) { throw new ArgumentNullException(nameof(targetUri)); } if (authorization is null) { throw new ArgumentNullException(nameof(authorization)); } if (tokenScope is null) { throw new ArgumentNullException(nameof(tokenScope)); } try { var requestUri = await CreatePersonalAccessTokenRequestUri(targetUri, authorization, requireCompactToken); var options = new NetworkRequestOptions(true) { Authorization = authorization, }; using (StringContent content = GetAccessTokenRequestBody(targetUri, tokenScope, tokenDuration)) using (var response = await Network.HttpPostAsync(requestUri, content, options)) { if (response.IsSuccessStatusCode) { string responseText = response.Content.AsString; if (!string.IsNullOrWhiteSpace(responseText)) { // Find the 'token : <value>' portion of the result content, if any. Match tokenMatch = null; if ((tokenMatch = Regex.Match(responseText, @"\s*""token""\s*:\s*""([^\""]+)""\s*", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase)).Success) { string tokenValue = tokenMatch.Groups[1].Value; Token token = new Token(tokenValue, TokenType.Personal); Trace.WriteLine($"personal access token acquisition for '{targetUri}' succeeded."); return(token); } } } Trace.WriteLine($"failed to acquire personal access token for '{targetUri}' [{(int)response.StatusCode}]."); } } catch (Exception exception) { Trace.WriteException(exception); } Trace.WriteLine($"personal access token acquisition for '{targetUri}' failed."); return(null); }
public Task <Token> GeneratePersonalAccessToken(TargetUri targetUri, Token authorization, TokenScope tokenScope, bool requireCompactToken) => GeneratePersonalAccessToken(targetUri, authorization, tokenScope, requireCompactToken, null);
public static async Task LoadOperationArguments(Program program, OperationArguments operationArguments) { if (program is null) { throw new ArgumentNullException(nameof(program)); } if (operationArguments is null) { throw new ArgumentNullException(nameof(operationArguments)); } if (operationArguments.TargetUri == null) { program.Die("No host information, unable to continue."); } string value; bool? yesno; if (program.TryReadBoolean(operationArguments, KeyType.ConfigNoLocal, out yesno)) { program.Trace.WriteLine($"{program.KeyTypeName(KeyType.ConfigNoLocal)} = '{yesno}'."); operationArguments.UseConfigLocal = yesno.Value; } if (program.TryReadBoolean(operationArguments, KeyType.ConfigNoSystem, out yesno)) { program.Trace.WriteLine($"{program.KeyTypeName(KeyType.ConfigNoSystem)} = '{yesno}'."); operationArguments.UseConfigSystem = yesno.Value; } // Load/re-load the Git configuration after setting the use local/system config values. await operationArguments.LoadConfiguration(); // If a user-agent has been specified in the environment, set it globally. if (program.TryReadString(operationArguments, KeyType.HttpUserAgent, out value)) { program.Trace.WriteLine($"{program.KeyTypeName(KeyType.HttpUserAgent)} = '{value}'."); Global.UserAgent = value; } // Look for authority settings. if (program.TryReadString(operationArguments, KeyType.Authority, out value)) { program.Trace.WriteLine($"{program.KeyTypeName(KeyType.Authority)} = '{value}'."); if (Program.ConfigKeyComparer.Equals(value, "MSA") || Program.ConfigKeyComparer.Equals(value, "Microsoft") || Program.ConfigKeyComparer.Equals(value, "MicrosoftAccount") || Program.ConfigKeyComparer.Equals(value, "Live") || Program.ConfigKeyComparer.Equals(value, "LiveConnect") || Program.ConfigKeyComparer.Equals(value, "LiveID")) { operationArguments.Authority = AuthorityType.MicrosoftAccount; } else if (Program.ConfigKeyComparer.Equals(value, "AAD") || Program.ConfigKeyComparer.Equals(value, "Azure") || Program.ConfigKeyComparer.Equals(value, "AzureDirectory")) { operationArguments.Authority = AuthorityType.AzureDirectory; } else if (Program.ConfigKeyComparer.Equals(value, "Integrated") || Program.ConfigKeyComparer.Equals(value, "Windows") || Program.ConfigKeyComparer.Equals(value, "TFS") || Program.ConfigKeyComparer.Equals(value, "Kerberos") || Program.ConfigKeyComparer.Equals(value, "NTLM") || Program.ConfigKeyComparer.Equals(value, "SSO")) { operationArguments.Authority = AuthorityType.Ntlm; } else if (Program.ConfigKeyComparer.Equals(value, "GitHub")) { operationArguments.Authority = AuthorityType.GitHub; } else if (Program.ConfigKeyComparer.Equals(value, "Atlassian") || Program.ConfigKeyComparer.Equals(value, "Bitbucket")) { operationArguments.Authority = AuthorityType.Bitbucket; } else { operationArguments.Authority = AuthorityType.Basic; } } // Look for interactivity config settings. if (program.TryReadString(operationArguments, KeyType.Interactive, out value)) { program.Trace.WriteLine($"{program.KeyTypeName(KeyType.Interactive)} = '{value}'."); if (Program.ConfigKeyComparer.Equals(value, "always") || Program.ConfigKeyComparer.Equals(value, "true") || Program.ConfigKeyComparer.Equals(value, "force")) { operationArguments.Interactivity = Interactivity.Always; } else if (Program.ConfigKeyComparer.Equals(value, "never") || Program.ConfigKeyComparer.Equals(value, "false")) { operationArguments.Interactivity = Interactivity.Never; } } // Look for credential validation config settings. if (program.TryReadBoolean(operationArguments, KeyType.Validate, out yesno)) { program.Trace.WriteLine($"{program.KeyTypeName(KeyType.Validate)} = '{yesno}'."); operationArguments.ValidateCredentials = yesno.Value; } // Look for write log config settings. if (program.TryReadBoolean(operationArguments, KeyType.Writelog, out yesno)) { program.Trace.WriteLine($"{program.KeyTypeName(KeyType.Writelog)} = '{yesno}'."); operationArguments.WriteLog = yesno.Value; } // Look for modal prompt config settings. if (program.TryReadBoolean(operationArguments, KeyType.ModalPrompt, out yesno)) { program.Trace.WriteLine($"{program.KeyTypeName(KeyType.ModalPrompt)} = '{yesno}'."); operationArguments.UseModalUi = yesno.Value; } // Look for credential preservation config settings. if (program.TryReadBoolean(operationArguments, KeyType.PreserveCredentials, out yesno)) { program.Trace.WriteLine($"{program.KeyTypeName(KeyType.PreserveCredentials)} = '{yesno}'."); operationArguments.PreserveCredentials = yesno.Value; } else if (operationArguments.EnvironmentVariables.TryGetValue("GCM_PRESERVE_CREDS", out value)) { if (StringComparer.OrdinalIgnoreCase.Equals(value, "true") || StringComparer.OrdinalIgnoreCase.Equals(value, "yes") || StringComparer.OrdinalIgnoreCase.Equals(value, "1") || StringComparer.OrdinalIgnoreCase.Equals(value, "on")) { program.Trace.WriteLine($"GCM_PRESERVE_CREDS = '{yesno}'."); operationArguments.PreserveCredentials = true; program.Trace.WriteLine($"WARNING: the 'GCM_PRESERVE_CREDS' variable has been deprecated, use '{ program.KeyTypeName(KeyType.PreserveCredentials) }' instead."); } } // Look for HTTP path usage config settings. if (program.TryReadBoolean(operationArguments, KeyType.HttpPath, out yesno)) { program.Trace.WriteLine($"{program.KeyTypeName(KeyType.HttpPath)} = '{value}'."); operationArguments.UseHttpPath = yesno.Value; } // Look for HTTP proxy config settings. if ((operationArguments.TargetUri.Scheme.Equals(Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase) && program.TryReadString(operationArguments, KeyType.HttpsProxy, out value)) || program.TryReadString(operationArguments, KeyType.HttpProxy, out value)) { program.Trace.WriteLine($"{program.KeyTypeName(KeyType.HttpProxy)} = '{value}'."); operationArguments.SetProxy(value); } // Check environment variables just-in-case. else if ((operationArguments.EnvironmentVariables.TryGetValue("GCM_HTTP_PROXY", out value) && !string.IsNullOrWhiteSpace(value))) { program.Trace.WriteLine($"GCM_HTTP_PROXY = '{value}'."); var keyName = (operationArguments.TargetUri.Scheme.Equals(Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase)) ? "HTTPS_PROXY" : "HTTP_PROXY"; var warning = $"WARNING: the 'GCM_HTTP_PROXY' variable has been deprecated, use '{ keyName }' instead."; program.Trace.WriteLine(warning); program.WriteLine(warning); operationArguments.SetProxy(value); } // Check the git-config http.proxy setting just-in-case. else { Git.Configuration.Entry entry; if (operationArguments.GitConfiguration.TryGetEntry("http", operationArguments.QueryUri, "proxy", out entry) && !string.IsNullOrWhiteSpace(entry.Value)) { program.Trace.WriteLine($"http.proxy = '{entry.Value}'."); operationArguments.SetProxy(entry.Value); } } // Look for custom namespace config settings. if (program.TryReadString(operationArguments, KeyType.Namespace, out value)) { program.Trace.WriteLine($"{program.KeyTypeName(KeyType.Namespace)} = '{value}'."); operationArguments.CustomNamespace = value; } // Look for custom token duration settings. if (program.TryReadString(operationArguments, KeyType.TokenDuration, out value)) { program.Trace.WriteLine($"{program.KeyTypeName(KeyType.TokenDuration)} = '{value}'."); int hours; if (int.TryParse(value, out hours)) { operationArguments.TokenDuration = TimeSpan.FromHours(hours); } } // Look for custom VSTS scope settings. if (program.TryReadString(operationArguments, KeyType.VstsScope, out value)) { program.Trace.WriteLine($"{program.KeyTypeName(KeyType.VstsScope)} = '{value}'."); Vsts.TokenScope vstsTokenScope = Vsts.TokenScope.None; var scopes = value.Split(TokenScopeSeparatorCharacters.ToCharArray(), StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < scopes.Length; i += 1) { scopes[i] = scopes[i].Trim(); if (Vsts.TokenScope.Find(scopes[i], out Vsts.TokenScope scope)) { vstsTokenScope = vstsTokenScope | scope; } else { program.Trace.WriteLine($"Unknown VSTS Token scope: '{scopes[i]}'."); } } operationArguments.VstsTokenScope = vstsTokenScope; } // Check for configuration supplied user-info. if (program.TryReadString(operationArguments, KeyType.Username, out value)) { program.Trace.WriteLine($"{program.KeyTypeName(KeyType.Username)} = '{value}'."); operationArguments.Username = value; } // Check for parent window handles in the environment. if (operationArguments.EnvironmentVariables.TryGetValue(string.Empty, out value)) { Trace.WriteLine($"{program.KeyTypeName(KeyType.ParentHwnd)} = '{value}'"); if (TryParse(value, out int ownerHwnd)) { operationArguments.ParentHwnd = new IntPtr(ownerHwnd); } else { Trace.WriteLine($"Failed to parse {program.KeyTypeName(KeyType.ParentHwnd)}."); } } }