/// <summary> /// Creates a new authentication broker based for the specified resource. /// </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> /// <param name="adaRefreshTokenStore">Storage container for Azure access token secrets.</param> /// <param name="authentication"> /// An implementation of <see cref="BaseAuthentication"/> if one was detected; /// <see langword="null"/> otherwise. /// </param> /// <returns> /// <see langword="true"/> if an authority could be determined; <see langword="false"/> otherwise. /// </returns> public static bool GetAuthentication( Uri targetUri, VsoTokenScope scope, ICredentialStore personalAccessTokenStore, ITokenStore adaRefreshTokenStore, out BaseAuthentication authentication) { Trace.WriteLine("BaseVsoAuthentication::DetectAuthority"); Guid tenantId; if (DetectAuthority(targetUri, out tenantId)) { // empty Guid is MSA, anything else is AAD if (tenantId == Guid.Empty) { Trace.WriteLine(" MSA authority detected"); authentication = new VsoMsaAuthentication(scope, personalAccessTokenStore, adaRefreshTokenStore); } else { Trace.WriteLine(" AAD authority for tenant '" + tenantId + "' detected"); authentication = new VsoAadAuthentication(tenantId, scope, personalAccessTokenStore, adaRefreshTokenStore); (authentication as VsoAadAuthentication).TenantId = tenantId; } } else { authentication = null; } return(authentication != null); }
/// <summary> /// Invoked by a derived classes implementation. Allows custom back-end implementations to be used. /// </summary> /// <param name="tokenScope">The desired scope of the acquired personal access token(s).</param> /// <param name="personalAccessTokenStore">The secret store for acquired personal access token(s).</param> /// <param name="adaRefreshTokenStore">The secret store for acquired Azure refresh token(s).</param> protected BaseVsoAuthentication( VsoTokenScope tokenScope, ICredentialStore personalAccessTokenStore, ITokenStore adaRefreshTokenStore = null) : this(tokenScope, personalAccessTokenStore) { this.AdaRefreshTokenStore = adaRefreshTokenStore ?? this.AdaRefreshTokenStore; this.VsoAdalTokenCache = new VsoAdalTokenCache(); this.VsoIdeTokenCache = new TokenRegistry(); }
/// <summary> /// Invoked by a derived classes implementation. Allows custom back-end implementations to be used. /// </summary> /// <param name="tokenScope">The desired scope of the acquired personal access token(s).</param> /// <param name="personalAccessTokenStore">The secret store for acquired personal access token(s).</param> /// <param name="adaRefreshTokenStore">The secret store for acquired Azure refresh token(s).</param> protected BaseVsoAuthentication( VsoTokenScope tokenScope, ICredentialStore personalAccessTokenStore, ITokenStore adaRefreshTokenStore = null) : this(tokenScope, personalAccessTokenStore) { this.AdaRefreshTokenStore = adaRefreshTokenStore ?? this.AdaRefreshTokenStore; this.VsoAdalTokenCache = new VsoAdalTokenCache(); this.VsoIdeTokenCache = new TokenRegistry(); }
public VsoMsaAuthentication( VsoTokenScope tokenScope, ICredentialStore personalAccessTokenStore, ITokenStore adaRefreshTokenStore = null) : base(tokenScope, personalAccessTokenStore, adaRefreshTokenStore) { this.VsoAuthority = new VsoAzureAuthority(DefaultAuthorityHost); }
public VsoMsaAuthentication( VsoTokenScope tokenScope, ICredentialStore personalAccessTokenStore, ITokenStore adaRefreshTokenStore = null) : base(tokenScope, personalAccessTokenStore, adaRefreshTokenStore) { this.VsoAuthority = new VsoAzureAuthority(DefaultAuthorityHost); }
private BaseVsoAuthentication(VsoTokenScope tokenScope, ICredentialStore personalAccessTokenStore) { if (tokenScope == null) throw new ArgumentNullException("scope", "The `scope` parameter is null or invalid."); if (personalAccessTokenStore == null) throw new ArgumentNullException("personalAccessTokenStore", "The `personalAccessTokenStore` paramter is null or invalid."); AdalTrace.TraceSource.Switch.Level = SourceLevels.Off; AdalTrace.LegacyTraceSwitch.Level = TraceLevel.Off; this.ClientId = DefaultClientId; this.Resource = DefaultResource; this.TokenScope = tokenScope; this.PersonalAccessTokenStore = personalAccessTokenStore; this.AdaRefreshTokenStore = new SecretStore(AdalRefreshPrefx); this.VsoAuthority = new VsoAzureAuthority(); }
/// <summary> /// /// </summary> /// <param name="tenantId"> /// <para>The unique identifier for the responsible Azure tenant.</para> /// <para>Use <see cref="BaseVsoAuthentication.GetAuthentication"/> /// to detect the tenant identity and create the authentication object.</para> /// </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 VsoAadAuthentication( Guid tenantId, VsoTokenScope tokenScope, ICredentialStore personalAccessTokenStore, ITokenStore adaRefreshTokenStore = null) : base(tokenScope, personalAccessTokenStore, adaRefreshTokenStore) { if (tenantId == Guid.Empty) { this.VsoAuthority = new VsoAzureAuthority(AzureAuthority.DefaultAuthorityHostUrl); } else { // create an authority host url in the format of https://login.microsoft.com/12345678-9ABC-DEF0-1234-56789ABCDEF0 string authorityHost = AzureAuthority.GetAuthorityUrl(tenantId); this.VsoAuthority = new VsoAzureAuthority(authorityHost); } }
/// <summary> /// /// </summary> /// <param name="tenantId"> /// <para>The unique identifier for the responsible Azure tenant.</para> /// <para>Use <see cref="BaseVsoAuthentication.GetAuthentication"/> /// to detect the tenant identity and create the authentication object.</para> /// </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 VsoAadAuthentication( Guid tenantId, VsoTokenScope tokenScope, ICredentialStore personalAccessTokenStore, ITokenStore adaRefreshTokenStore = null) : base(tokenScope, personalAccessTokenStore, adaRefreshTokenStore) { if (tenantId == Guid.Empty) { this.VsoAuthority = new VsoAzureAuthority(AzureAuthority.DefaultAuthorityHostUrl); } else { // create an authority host url in the format of https://login.microsoft.com/12345678-9ABC-DEF0-1234-56789ABCDEF0 string authorityHost = AzureAuthority.GetAuthorityUrl(tenantId); this.VsoAuthority = new VsoAzureAuthority(authorityHost); } }
private BaseVsoAuthentication(VsoTokenScope tokenScope, ICredentialStore personalAccessTokenStore) { if (tokenScope == null) { throw new ArgumentNullException("scope", "The `scope` parameter is null or invalid."); } if (personalAccessTokenStore == null) { throw new ArgumentNullException("personalAccessTokenStore", "The `personalAccessTokenStore` paramter is null or invalid."); } AdalTrace.TraceSource.Switch.Level = SourceLevels.Off; AdalTrace.LegacyTraceSwitch.Level = TraceLevel.Off; this.ClientId = DefaultClientId; this.Resource = DefaultResource; this.TokenScope = tokenScope; this.PersonalAccessTokenStore = personalAccessTokenStore; this.AdaRefreshTokenStore = new SecretStore(AdalRefreshPrefx); this.VsoAuthority = new VsoAzureAuthority(); }
/// <summary> /// Generates a personal access token for use with Visual Studio Online. /// </summary> /// <param name="targetUri"> /// The uniform resource indicator of the resource access tokens are being requested for. /// </param> /// <param name="accessToken"></param> /// <param name="tokenScope"></param> /// <param name="requireCompactToken"></param> /// <returns></returns> public async Task <Token> GeneratePersonalAccessToken(Uri targetUri, Token accessToken, VsoTokenScope tokenScope, bool requireCompactToken) { const string TokenAuthHost = "app.vssps.visualstudio.com"; const string SessionTokenUrl = "https://" + TokenAuthHost + "/_apis/token/sessiontokens?api-version=1.0"; const string CompactTokenUrl = SessionTokenUrl + "&tokentype=compact"; const string AccessTokenHeader = "Bearer"; Debug.Assert(targetUri != null, "The targetUri parameter is null"); Debug.Assert(accessToken != null && !String.IsNullOrWhiteSpace(accessToken.Value) && (accessToken.Type == TokenType.Access || accessToken.Type == TokenType.Federated), "The accessToken parameter is null or invalid"); Debug.Assert(tokenScope != null); Trace.WriteLine("VsoAzureAuthority::GeneratePersonalAccessToken"); try { // create a `HttpClient` with a minimum number of redirects, default creds, and a reasonable timeout (access token generation seems to hang occasionally) using (HttpClientHandler handler = new HttpClientHandler() { MaxAutomaticRedirections = 2, UseDefaultCredentials = true }) using (HttpClient httpClient = new HttpClient(handler) { Timeout = TimeSpan.FromMilliseconds(RequestTimeout) }) { httpClient.DefaultRequestHeaders.Add("User-Agent", Global.GetUserAgent()); switch (accessToken.Type) { case TokenType.Access: Trace.WriteLine(" using Azure access token to acquire personal access token"); httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(AccessTokenHeader, accessToken.Value); break; case TokenType.Federated: Trace.WriteLine(" using federated authentication token to acquire personal access token"); httpClient.DefaultRequestHeaders.Add("Cookie", accessToken.Value); break; default: return(null); } if (await PopulateTokenTargetId(targetUri, accessToken)) { string requestUrl = requireCompactToken ? CompactTokenUrl : SessionTokenUrl; using (StringContent content = GetAccessTokenRequestBody(targetUri, accessToken, tokenScope)) using (HttpResponseMessage response = await httpClient.PostAsync(requestUrl, content)) { if (response.StatusCode == HttpStatusCode.OK) { string responseText = await response.Content.ReadAsStringAsync(); 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 aquisition succeeded."); return(token); } } } } } } } catch { Trace.WriteLine(" an error occured error."); } Trace.WriteLine(" personal access token aquisition failed."); return(null); }
private StringContent GetAccessTokenRequestBody(Uri targetUri, Token accessToken, VsoTokenScope tokenScope) { const string ContentJsonFormat = "{{ \"scope\" : \"{0}\", \"targetAccounts\" : [\"{1}\"], \"displayName\" : \"Git: {2} on {3}\" }}"; const string HttpJsonContentType = "application/json"; Debug.Assert(accessToken != null && (accessToken.Type == TokenType.Access || accessToken.Type == TokenType.Federated), "The accessToken parameter is null or invalid"); Debug.Assert(tokenScope != null, "The tokenScope parameter is null"); Trace.WriteLine(" creating access token scoped to '" + tokenScope + "' for '" + accessToken.TargetIdentity + "'"); string jsonContent = String.Format(ContentJsonFormat, tokenScope, accessToken.TargetIdentity, targetUri, Environment.MachineName); StringContent content = new StringContent(jsonContent, Encoding.UTF8, HttpJsonContentType); return(content); }
/// <summary> /// Creates a new authentication broker based for the specified resource. /// </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> /// <param name="adaRefreshTokenStore">Storage container for Azure access token secrets.</param> /// <param name="authentication"> /// An implementation of <see cref="BaseAuthentication"/> if one was detected; /// <see langword="null"/> otherwise. /// </param> /// <returns> /// <see langword="true"/> if an authority could be determined; <see langword="false"/> otherwise. /// </returns> public static bool GetAuthentication( Uri targetUri, VsoTokenScope scope, ICredentialStore personalAccessTokenStore, ITokenStore adaRefreshTokenStore, out BaseAuthentication authentication) { Trace.WriteLine("BaseVsoAuthentication::DetectAuthority"); Guid tenantId; if (DetectAuthority(targetUri, out tenantId)) { // empty Guid is MSA, anything else is AAD if (tenantId == Guid.Empty) { Trace.WriteLine(" MSA authority detected"); authentication = new VsoMsaAuthentication(scope, personalAccessTokenStore, adaRefreshTokenStore); } else { Trace.WriteLine(" AAD authority for tenant '" + tenantId + "' detected"); authentication = new VsoAadAuthentication(tenantId, scope, personalAccessTokenStore, adaRefreshTokenStore); (authentication as VsoAadAuthentication).TenantId = tenantId; } } else { authentication = null; } return authentication != null; }
/// <summary> /// Generates a personal access token for use with Visual Studio Online. /// </summary> /// <param name="targetUri"> /// The uniform resource indicator of the resource access tokens are being requested for. /// </param> /// <param name="accessToken"></param> /// <param name="tokenScope"></param> /// <param name="requireCompactToken"></param> /// <returns></returns> public async Task<Token> GeneratePersonalAccessToken(Uri targetUri, Token accessToken, VsoTokenScope tokenScope, bool requireCompactToken) { const string TokenAuthHost = "app.vssps.visualstudio.com"; const string SessionTokenUrl = "https://" + TokenAuthHost + "/_apis/token/sessiontokens?api-version=1.0"; const string CompactTokenUrl = SessionTokenUrl + "&tokentype=compact"; const string AccessTokenHeader = "Bearer"; Debug.Assert(targetUri != null, "The targetUri parameter is null"); Debug.Assert(accessToken != null && !String.IsNullOrWhiteSpace(accessToken.Value) && (accessToken.Type == TokenType.Access || accessToken.Type == TokenType.Federated), "The accessToken parameter is null or invalid"); Debug.Assert(tokenScope != null); Trace.WriteLine("VsoAzureAuthority::GeneratePersonalAccessToken"); try { // create a `HttpClient` with a minimum number of redirects, default creds, and a reasonable timeout (access token generation seems to hang occasionally) using (HttpClientHandler handler = new HttpClientHandler() { MaxAutomaticRedirections = 2, UseDefaultCredentials = true }) using (HttpClient httpClient = new HttpClient(handler) { Timeout = TimeSpan.FromMilliseconds(RequestTimeout) }) { httpClient.DefaultRequestHeaders.Add("User-Agent", Global.GetUserAgent()); switch (accessToken.Type) { case TokenType.Access: Trace.WriteLine(" using Azure access token to acquire personal access token"); httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(AccessTokenHeader, accessToken.Value); break; case TokenType.Federated: Trace.WriteLine(" using federated authentication token to acquire personal access token"); httpClient.DefaultRequestHeaders.Add("Cookie", accessToken.Value); break; default: return null; } if (await PopulateTokenTargetId(targetUri, accessToken)) { string requestUrl = requireCompactToken ? CompactTokenUrl : SessionTokenUrl; using (StringContent content = GetAccessTokenRequestBody(targetUri, accessToken, tokenScope)) using (HttpResponseMessage response = await httpClient.PostAsync(requestUrl, content)) { if (response.StatusCode == HttpStatusCode.OK) { string responseText = await response.Content.ReadAsStringAsync(); 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 aquisition succeeded."); return token; } } } } } } } catch { Trace.WriteLine(" an error occured error."); } Trace.WriteLine(" personal access token aquisition failed."); return null; }
private StringContent GetAccessTokenRequestBody(Uri targetUri, Token accessToken, VsoTokenScope tokenScope) { const string ContentJsonFormat = "{{ \"scope\" : \"{0}\", \"targetAccounts\" : [\"{1}\"], \"displayName\" : \"Git: {2} on {3}\" }}"; const string HttpJsonContentType = "application/json"; Debug.Assert(accessToken != null && (accessToken.Type == TokenType.Access || accessToken.Type == TokenType.Federated), "The accessToken parameter is null or invalid"); Debug.Assert(tokenScope != null, "The tokenScope parameter is null"); Trace.WriteLine(" creating access token scoped to '" + tokenScope + "' for '" + accessToken.TargetIdentity + "'"); string jsonContent = String.Format(ContentJsonFormat, tokenScope, accessToken.TargetIdentity, targetUri, Environment.MachineName); StringContent content = new StringContent(jsonContent, Encoding.UTF8, HttpJsonContentType); return content; }