/// <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(
            Microsoft.Alm.Authentication.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.ToString("N")}' detected.");
                authentication = new AadAuthentication(context, tenantId, scope, personalAccessTokenStore);
                (authentication as AadAuthentication).TenantId = tenantId;
            }

            return(authentication);
        }
 public Adal(Alm.RuntimeContext context)
     : base(context)
 {
     _cache = new AdalTokenCache(context);
 }
 public Network(RuntimeContext context)
     : base(context)
 {
 }
Example #4
0
 public BasicAuthentication(RuntimeContext context, ICredentialStore credentialStore)
     : this(context, credentialStore, NtlmSupport.Auto, null, null)
 {
 }
Example #5
0
 public Settings(RuntimeContext context)
     : base(context)
 {
 }
Example #6
0
 private SecretCache(RuntimeContext context)
     : base(context)
 {
     _cache = new Dictionary <string, Secret>(KeyComparer);
 }
Example #7
0
 public SecretCache(RuntimeContext context, string @namespace)
     : this(context, @namespace, null)
 {
 }
Example #8
0
 protected BaseAuthentication(RuntimeContext context)
     : base(context)
 {
 }
        private static async Task <Dictionary <string, Guid> > DeserializeTenantCache(RuntimeContext context)
        {
            var encoding = new UTF8Encoding(false);
            var path     = GetCachePath(context);

            string data = null;
            Dictionary <string, Guid> cache = new Dictionary <string, Guid>(StringComparer.OrdinalIgnoreCase);
            Exception exception             = null;

            // Attempt up to five times to read from the cache
            for (int i = 0; i < 5; i += 1)
            {
                try
                {
                    // Just open the file from disk, the tenant identities are not secret and
                    // therefore safely left as unencrypted plain text.
                    using (var stream = context.Storage.FileOpen(path, FileMode.OpenOrCreate, FileAccess.Read, FileShare.Read))
                        using (var inflate = new GZipStream(stream, CompressionMode.Decompress))
                            using (var reader = new StreamReader(inflate, encoding))
                            {
                                data = await reader.ReadToEndAsync();

                                break;
                            }
                }
                catch when(i < 5)
                {
                    // Sleep the thread, and wait before trying again using progressive back off
                    System.Threading.Thread.Sleep(i + 1 * 100);
                }
        public static async Task <Guid?> DetectAuthority(RuntimeContext context, TargetUri targetUri)
        {
            const string VstsBaseUrlHost          = "visualstudio.com";
            const string VstsResourceTenantHeader = "X-VSS-ResourceTenant";

            BaseSecureStore.ValidateTargetUri(targetUri);

            var tenantId = Guid.Empty;

            if (targetUri.Host.EndsWith(VstsBaseUrlHost, StringComparison.OrdinalIgnoreCase))
            {
                context.Trace.WriteLine($"'{targetUri}' is subdomain of '{VstsBaseUrlHost}', checking AAD vs MSA.");

                string tenant = null;

                if (StringComparer.OrdinalIgnoreCase.Equals(targetUri.Scheme, Uri.UriSchemeHttp) ||
                    StringComparer.OrdinalIgnoreCase.Equals(targetUri.Scheme, Uri.UriSchemeHttps))
                {
                    // Query the cache first.
                    string tenantUrl = targetUri.ToString();

                    // Read the cache from disk.
                    var cache = await DeserializeTenantCache(context);

                    // Check the cache for an existing value.
                    if (cache.TryGetValue(tenantUrl, out tenantId))
                    {
                        return(tenantId);
                    }

                    var options = new NetworkRequestOptions(false)
                    {
                        Flags   = NetworkRequestOptionFlags.UseProxy,
                        Timeout = TimeSpan.FromMilliseconds(Global.RequestTimeout),
                    };

                    try
                    {
                        using (var response = await context.Network.HttpGetAsync(targetUri, options))
                        {
                            if (response.IsSuccessStatusCode)
                            {
                                if (response.Headers.TryGetValues(VstsResourceTenantHeader, out IEnumerable <string> values))
                                {
                                    tenant = System.Linq.Enumerable.First(values);

                                    if (!string.IsNullOrWhiteSpace(tenant) &&
                                        Guid.TryParse(tenant, out tenantId))
                                    {
                                        // Update the cache.
                                        cache[tenantUrl] = tenantId;

                                        // Write the cache to disk.
                                        await SerializeTenantCache(context, cache);

                                        // Success, notify the caller
                                        return(tenantId);
                                    }
                                }
                            }
                            else
                            {
                                context.Trace.WriteLine($"unable to get response from '{targetUri}', server responded with '{(int)response.StatusCode} {response.StatusCode}'.");
                            }
                        }
                    }
                    catch (HttpRequestException exception)
                    {
                        context.Trace.WriteLine($"unable to get response from '{targetUri}' due to '{exception.Message}'.");
                    }
                }
                else
                {
                    context.Trace.WriteLine($"detected non-https based protocol: '{targetUri.Scheme}'.");
                }
            }

            // Fallback to basic authentication.
            return(null);
        }