public void ValidateCtorOptionsPassedToCredentials()
        {
            string expClientId        = Guid.NewGuid().ToString();
            string expUsername        = Guid.NewGuid().ToString();
            string expCacheTenantId   = Guid.NewGuid().ToString();
            string expBrowserTenantId = Guid.NewGuid().ToString();
            string expVsTenantId      = Guid.NewGuid().ToString();
            string expCodeTenantId    = Guid.NewGuid().ToString();
            string actClientId        = null;
            string actUsername        = null;
            string actCacheTenantId   = null;
            string actBrowserTenantId = null;
            string actVsTenantId      = null;
            string actCodeTenantId    = null;

            var credFactory = new MockDefaultAzureCredentialFactory(CredentialPipeline.GetInstance(null));

            credFactory.OnCreateManagedIdentityCredential    = (clientId, _) => actClientId = clientId;
            credFactory.OnCreateSharedTokenCacheCredential   = (tenantId, username, _) => { actCacheTenantId = tenantId; actUsername = username; };
            credFactory.OnCreateInteractiveBrowserCredential = (tenantId, _) => { actBrowserTenantId = tenantId; };
            credFactory.OnCreateVisualStudioCredential       = (tenantId, _) => { actVsTenantId = tenantId; };
            credFactory.OnCreateVisualStudioCodeCredential   = (tenantId, _) => { actCodeTenantId = tenantId; };

            var options = new DefaultAzureCredentialOptions
            {
                ManagedIdentityClientId             = expClientId,
                SharedTokenCacheUsername            = expUsername,
                SharedTokenCacheTenantId            = expCacheTenantId,
                VisualStudioTenantId                = expVsTenantId,
                VisualStudioCodeTenantId            = expCodeTenantId,
                InteractiveBrowserTenantId          = expBrowserTenantId,
                ExcludeInteractiveBrowserCredential = false
            };

            var cred = new DefaultAzureCredential(credFactory, options);

            Assert.AreEqual(expClientId, actClientId);
            Assert.AreEqual(expUsername, actUsername);
            Assert.AreEqual(expCacheTenantId, actCacheTenantId);
            Assert.AreEqual(expBrowserTenantId, actBrowserTenantId);
            Assert.AreEqual(expVsTenantId, actVsTenantId);
            Assert.AreEqual(expCodeTenantId, actCodeTenantId);
        }
Beispiel #2
0
        public void ValidateCtorWithExcludeOptions([Values(true, false)] bool excludeEnvironmentCredential, [Values(true, false)] bool excludeManagedIdentityCredential, [Values(true, false)] bool excludeSharedTokenCacheCredential, [Values(true, false)] bool excludeInteractiveBrowserCredential)
        {
            var credFactory = new MockDefaultAzureCredentialFactory(CredentialPipeline.GetInstance(null));

            bool environmentCredentialIncluded        = false;
            bool managedIdentityCredentialIncluded    = false;
            bool sharedTokenCacheCredentialIncluded   = false;
            bool interactiveBrowserCredentialIncluded = false;

            credFactory.OnCreateEnvironmentCredential        = (_) => environmentCredentialIncluded = true;
            credFactory.OnCreateInteractiveBrowserCredential = (tenantId, _) => interactiveBrowserCredentialIncluded = true;
            credFactory.OnCreateManagedIdentityCredential    = (clientId, _) =>
            {
                managedIdentityCredentialIncluded = true;
            };
            credFactory.OnCreateSharedTokenCacheCredential = (tenantId, username, _) =>
            {
                sharedTokenCacheCredentialIncluded = true;
            };

            var options = new DefaultAzureCredentialOptions
            {
                ExcludeEnvironmentCredential        = excludeEnvironmentCredential,
                ExcludeManagedIdentityCredential    = excludeManagedIdentityCredential,
                ExcludeSharedTokenCacheCredential   = excludeSharedTokenCacheCredential,
                ExcludeInteractiveBrowserCredential = excludeInteractiveBrowserCredential
            };

            if (excludeEnvironmentCredential && excludeManagedIdentityCredential && excludeSharedTokenCacheCredential && excludeInteractiveBrowserCredential)
            {
                Assert.Throws <ArgumentException>(() => new DefaultAzureCredential(options));
            }
            else
            {
                var cred = new DefaultAzureCredential(credFactory, options);

                Assert.AreEqual(!excludeEnvironmentCredential, environmentCredentialIncluded);
                Assert.AreEqual(!excludeManagedIdentityCredential, managedIdentityCredentialIncluded);
                Assert.AreEqual(!excludeSharedTokenCacheCredential, sharedTokenCacheCredentialIncluded);
                Assert.AreEqual(!excludeInteractiveBrowserCredential, interactiveBrowserCredentialIncluded);
            }
        }
Beispiel #3
0
        public void ValidateEnvironmentBasedOptionsPassedToCredentials([Values] bool clientIdSpecified, [Values] bool usernameSpecified, [Values] bool tenantIdSpecified)
        {
            var  expClientId            = clientIdSpecified ? Guid.NewGuid().ToString() : null;
            var  expUsername            = usernameSpecified ? Guid.NewGuid().ToString() : null;
            var  expTenantId            = tenantIdSpecified ? Guid.NewGuid().ToString() : null;
            bool onCreateSharedCalled   = false;
            bool onCreatedManagedCalled = false;

            using (new TestEnvVar("AZURE_CLIENT_ID", expClientId))
                using (new TestEnvVar("AZURE_USERNAME", expUsername))
                    using (new TestEnvVar("AZURE_TENANT_ID", expTenantId))
                    {
                        var credFactory = new MockDefaultAzureCredentialFactory(CredentialPipeline.GetInstance(null));

                        credFactory.OnCreateManagedIdentityCredential = (clientId, _) =>
                        {
                            onCreatedManagedCalled = true;
                            Assert.AreEqual(expClientId, clientId);
                        };

                        credFactory.OnCreateSharedTokenCacheCredential = (tenantId, username, _) =>
                        {
                            onCreateSharedCalled = true;
                            Assert.AreEqual(expTenantId, tenantId);
                            Assert.AreEqual(expUsername, username);
                        };

                        var options = new DefaultAzureCredentialOptions
                        {
                            ExcludeEnvironmentCredential        = true,
                            ExcludeManagedIdentityCredential    = false,
                            ExcludeSharedTokenCacheCredential   = false,
                            ExcludeInteractiveBrowserCredential = true
                        };

                        var cred = new DefaultAzureCredential(credFactory, options);

                        Assert.IsTrue(onCreateSharedCalled);
                        Assert.IsTrue(onCreatedManagedCalled);
                    }
        }
    private static TokenCredential CreateKeyVaultCredential(this IConfiguration configuration)
    {
        // WARNING: Make sure to give the App in the Azure Portal access to the KeyVault.
        //          In the Identity tab: System Assigned part: turn Status On and copy the Object ID.
        //          In the KeyVault: Access Policies > Add Access Policy > Secret Permissions Get, List and Select Principal: Object ID copied above.
        // When running on Azure, you do NOT need to set the KeyVaultTenantId.
        var keyVaultTenantId = configuration[ConfigurationKeys.KeyVaultTenantId];

        if (string.IsNullOrEmpty(keyVaultTenantId))
        {
            return(new DefaultAzureCredential());
        }

        // When debugging local from VisualStudio AND the TenantId differs from default AZURE_TENANT_ID (in Windows settings/environment variables),
        // you can store KeyVaultTenantId= in appsettings or in UserSecrets and read it here from the configuration (as done above)
        var options = new DefaultAzureCredentialOptions {
            VisualStudioTenantId = keyVaultTenantId
        };

        return(new DefaultAzureCredential(options));
    }
Beispiel #5
0
        public SnippetsController(IConfiguration config)
        {
            _config = config;
            var    containerUri = new Uri(_config["SnippetsContainerUrl"]);
            string accessKey    = _config["SnippetsAccessKey"];

            if (accessKey == "secret")
            {
                var defaultAzureCredentialOptions = new DefaultAzureCredentialOptions();
                defaultAzureCredentialOptions.ManagedIdentityClientId = _config["ManagedCredentialsId"];
                containerClient = new BlobContainerClient(containerUri,
                                                          new DefaultAzureCredential(defaultAzureCredentialOptions));
            }
            else
            {
                var blobUri      = new BlobUriBuilder(containerUri);
                var acccountName = blobUri.AccountName;
                var key          = new StorageSharedKeyCredential(acccountName, accessKey);
                containerClient = new BlobContainerClient(containerUri, key);
            }
        }
Beispiel #6
0
        private static async Task StoreInKeyVault(Credentials awsCredentials)
        {
            var kvUrl = Environment.GetEnvironmentVariable("KeyVaultUrl");

            Log.LogInformation($"Storing Session Token in KeyVault '{kvUrl}'");
            var isLocal = string.IsNullOrEmpty(Environment.GetEnvironmentVariable("WEBSITE_INSTANCE_ID"));
            var defaultAzureCredentialOptions = new DefaultAzureCredentialOptions
            {
                ExcludeAzureCliCredential           = true,
                ExcludeEnvironmentCredential        = true,
                ExcludeInteractiveBrowserCredential = true,
                ExcludeManagedIdentityCredential    = true,
                ExcludeSharedTokenCacheCredential   = true,
                ExcludeVisualStudioCodeCredential   = true,
                ExcludeVisualStudioCredential       = true
            };

            // need to decide where to get the credentials from to connect to KeyVault
            if (isLocal)
            {
                defaultAzureCredentialOptions.ExcludeAzureCliCredential = false;
            }
            else
            {
                defaultAzureCredentialOptions.ExcludeManagedIdentityCredential = false;
            }

            Log.LogInformation("Storing secrets in KeyVault");
            var client = new SecretClient(new Uri(kvUrl), new DefaultAzureCredential(defaultAzureCredentialOptions));
            // Log.LogInformation("Storing 'AccessKeyId': '{awsCredentials.AccessKeyId}'");
            await client.SetSecretAsync("AccessKeyId", awsCredentials.AccessKeyId);

            // Log.LogInformation("Storing 'SecretAccessKey': '{awsCredentials.SecretAccessKey}'");
            await client.SetSecretAsync("SecretAccessKey", awsCredentials.SecretAccessKey);

            // Log.LogInformation("Storing 'SessionToken': '{awsCredentials.SessionToken}'");
            await client.SetSecretAsync("SessionToken", awsCredentials.SessionToken);

            Log.LogInformation("Secrets saved in KeyVault");
        }
        /// <summary>
        /// Returns a token to authenticate with Azure services
        /// </summary>
        /// <param name="serviceUri"></param>
        /// <returns>An access token for the requested endpoint/scope</returns>
        /// <remarks>
        /// If the web application is configured with ClientId and Secret using the standard MicrosoftIdentityOptions
        /// Then ClientCredentials are used to provide the identity requesting a token otherwise it will fall through to use
        /// an DefaultAzureCredential object. The following credential types if enabled will be tried, in order:
        /// - EnvironmentCredential
        /// - ManagedIdentityCredential
        /// - SharedTokenCacheCredential
        /// - VisualStudioCredential
        /// - VisualStudioCodeCredential
        /// - AzureCliCredential
        /// - InteractiveBrowserCredential
        /// </remarks>
        public async Task <string> GetMsalRestApiToken(TokenRequestContext requestContext, CancellationToken cancellationToken)
        {
            TokenCredential credential = null;

            if (!string.IsNullOrEmpty(_authOptions.Value.ClientSecret))
            {
                credential = new ClientSecretCredential(_authOptions.Value.TenantId, _authOptions.Value.ClientId, _authOptions.Value.ClientSecret);
            }
            else
            {
                var defaultAzureCredentialOptions = new DefaultAzureCredentialOptions()
                {
                    ExcludeAzureCliCredential        = true,
                    ExcludeManagedIdentityCredential = !_appOptions.Value.UseMSI
                };
                credential = new DefaultAzureCredential(defaultAzureCredentialOptions);
            }

            var result = await credential.GetTokenAsync(requestContext, cancellationToken);

            return(result.Token);
        }
        public async Task <string> GetSecretAsUserAsync()
        {
            logger.LogInformation("----- User Async");
            SecretClientOptions options = KeyVaultUtility.CreateSecretClientOptions();

            /*
             * Environment variables enabled. VS2019->application->properties->debug->Environment variable
             * AZURE_CLIENT_SECRET, AZURE_CLIENT_ID, AZURE_TENANT_ID
             * You can login in VS2019 with credentials having access to key vault. Add ExcludeEnvironmentCredential = true and
             * remove ExcludeVisualStudioCodeCredential
             */
            var credentialOptions = new DefaultAzureCredentialOptions
            {
                ExcludeAzureCliCredential           = true,
                ExcludeInteractiveBrowserCredential = true,
                ExcludeSharedTokenCacheCredential   = true,
                ExcludeEnvironmentCredential        = true,
                ExcludeManagedIdentityCredential    = true,
                ExcludeVisualStudioCodeCredential   = true
            };
            var credentials = new DefaultAzureCredential(credentialOptions);

            var keyVault = keyVaultConfiguration.Url;
            var client   = new SecretClient(new Uri(keyVault), credentials, options);

            KeyVaultSecret secret = null;

            try
            {
                secret = await client.GetSecretAsync(keyVaultConfiguration.SecretName);

                return(secret.Value);
            }
            catch (Exception ex)
            {
                logger.LogError(ex, "Failed to get secret as user");
                throw;
            }
        }
        public static IServiceCollection CreateKeyVaultClient(this IServiceCollection services)
        {
            var serviceProvider = services.BuildServiceProvider();

            var keyvaultConfig        = serviceProvider.GetRequiredService <IOptions <KeyVaultConfiguration> >();
            var keyVaultConfiguration = keyvaultConfig.Value;

            var options = new SecretClientOptions()
            {
                Retry =
                {
                    Delay      = TimeSpan.FromSeconds(2),
                    MaxDelay   = TimeSpan.FromSeconds(5),
                    MaxRetries =                       5,
                    Mode       = RetryMode.Exponential
                }
            };
            var credentialOptions = new DefaultAzureCredentialOptions
            {
                ExcludeAzureCliCredential           = true,
                ExcludeInteractiveBrowserCredential = true,
                ExcludeSharedTokenCacheCredential   = true,
                ExcludeVisualStudioCodeCredential   = true,
                ExcludeVisualStudioCredential       = true,
            };

            if (keyVaultConfiguration.ManagedIdentity)
            {
                credentialOptions.ExcludeEnvironmentCredential = true;
            }

            var keyVaultUrl = keyVaultConfiguration.Url;
            var client      = new SecretClient(new Uri(keyVaultUrl), new DefaultAzureCredential(credentialOptions), options);

            services.AddSingleton(client);

            return(services);
        }
        public void ValidateCtorOptionsPassedToCredentials()
        {
            string expClientId = Guid.NewGuid().ToString();
            string expUsername = Guid.NewGuid().ToString();
            string actClientId = null;
            string actUsername = null;

            var credFactory = new MockDefaultAzureCredentialFactory(CredentialPipeline.GetInstance(null));

            credFactory.OnCreateManagedIdentityCredential  = (clientId, _) => actClientId = clientId;
            credFactory.OnCreateSharedTokenCacheCredential = (username, _) => actUsername = username;

            var options = new DefaultAzureCredentialOptions
            {
                ManagedIdentityClientId  = expClientId,
                SharedTokenCacheUsername = expUsername
            };

            var cred = new DefaultAzureCredential(credFactory, options);

            Assert.AreEqual(expClientId, actClientId);
            Assert.AreEqual(expUsername, actUsername);
        }
        public TokenCredential GetDeveloperCredentials(string?username, string?currentApplicationTenantId)
        {
#if AzureSDK
            DefaultAzureCredentialOptions defaultAzureCredentialOptions = new DefaultAzureCredentialOptions()
            {
                SharedTokenCacheTenantId = currentApplicationTenantId,
                SharedTokenCacheUsername = username,
            };
            defaultAzureCredentialOptions.ExcludeManagedIdentityCredential    = true;
            defaultAzureCredentialOptions.ExcludeInteractiveBrowserCredential = true;
            defaultAzureCredentialOptions.ExcludeAzureCliCredential           = true;
            defaultAzureCredentialOptions.ExcludeEnvironmentCredential        = true;



            DefaultAzureCredential credential = new DefaultAzureCredential(defaultAzureCredentialOptions);
            return(credential);
#endif
            TokenCredential tokenCredential = new MsalTokenCredential(
                currentApplicationTenantId,
                username);
            return(tokenCredential);
        }
Beispiel #12
0
        public async Task ValidateSelectedCredentialCaching(Type availableCredential)
        {
            var         expToken          = new AccessToken(Guid.NewGuid().ToString(), DateTimeOffset.MaxValue);
            List <Type> calledCredentials = new();
            var         credFactory       = GetMockDefaultAzureCredentialFactory(availableCredential, expToken, calledCredentials);

            var options = new DefaultAzureCredentialOptions
            {
                ExcludeEnvironmentCredential        = false,
                ExcludeManagedIdentityCredential    = false,
                ExcludeSharedTokenCacheCredential   = false,
                ExcludeAzureCliCredential           = false,
                ExcludeAzurePowerShellCredential    = false,
                ExcludeInteractiveBrowserCredential = false
            };

            var cred = new DefaultAzureCredential(credFactory, options);

            AccessToken actToken = await cred.GetTokenAsync(new TokenRequestContext(MockScopes.Default));

            Assert.AreEqual(expToken.Token, actToken.Token);

            // assert that the available credential was the last credential called
            Assert.AreEqual(calledCredentials[calledCredentials.Count - 1], availableCredential);

            calledCredentials.Clear();

            actToken = await cred.GetTokenAsync(new TokenRequestContext(MockScopes.Default));

            Assert.AreEqual(expToken.Token, actToken.Token);

            // assert that the available credential was the only credential called
            Assert.AreEqual(calledCredentials.Count, 1);

            Assert.AreEqual(calledCredentials[0], availableCredential);
        }
Beispiel #13
0
        public TokenCredential GetDeveloperCredentials(string?username, string?currentApplicationTenantId)
        {
#if AzureSDK
            *Tried but does not work if another tenant than the home tenant id is specified
            DefaultAzureCredentialOptions defaultAzureCredentialOptions = new DefaultAzureCredentialOptions()
            {
                SharedTokenCacheTenantId = provisioningToolOptions.TenantId ?? currentApplicationTenantId,
                SharedTokenCacheUsername = provisioningToolOptions.Username,
            };
            defaultAzureCredentialOptions.ExcludeManagedIdentityCredential    = true;
            defaultAzureCredentialOptions.ExcludeInteractiveBrowserCredential = true;
            defaultAzureCredentialOptions.ExcludeAzureCliCredential           = true;
            defaultAzureCredentialOptions.ExcludeEnvironmentCredential        = true;



            DefaultAzureCredential credential = new DefaultAzureCredential(defaultAzureCredentialOptions);
            return(credential);
#endif
            TokenCredential tokenCredential = new MsalTokenCredential(
                currentApplicationTenantId,
                username);
            return(tokenCredential);
        }
        public void ValidateAllUnavailable([Values(true, false)] bool excludeEnvironmentCredential, [Values(true, false)] bool excludeManagedIdentityCredential, [Values(true, false)] bool excludeSharedTokenCacheCredential, [Values(true, false)] bool excludeCliCredential, [Values(true, false)] bool excludeInteractiveBrowserCredential)
        {
            if (excludeEnvironmentCredential && excludeManagedIdentityCredential && excludeSharedTokenCacheCredential && excludeCliCredential && excludeInteractiveBrowserCredential)
            {
                Assert.Pass();
            }

            var credFactory = new MockDefaultAzureCredentialFactory(CredentialPipeline.GetInstance(null));

            credFactory.OnCreateEnvironmentCredential = (c) =>
            {
                ((MockTokenCredential)c).TokenFactory = (context, cancel) => { throw new CredentialUnavailableException("EnvironmentCredential Unavailable"); };
            };
            credFactory.OnCreateInteractiveBrowserCredential = (_, c) =>
            {
                ((MockTokenCredential)c).TokenFactory = (context, cancel) => { throw new CredentialUnavailableException("InteractiveBrowserCredential Unavailable"); };
            };
            credFactory.OnCreateManagedIdentityCredential = (clientId, c) =>
            {
                ((MockTokenCredential)c).TokenFactory = (context, cancel) => { throw new CredentialUnavailableException("ManagedIdentityCredential Unavailable"); };
            };
            credFactory.OnCreateSharedTokenCacheCredential = (tenantId, username, c) =>
            {
                ((MockTokenCredential)c).TokenFactory = (context, cancel) => { throw new CredentialUnavailableException("SharedTokenCacheCredential Unavailable"); };
            };
            credFactory.OnCreateAzureCliCredential = (c) =>
            {
                ((MockTokenCredential)c).TokenFactory = (context, cancel) => { throw new CredentialUnavailableException("CliCredential Unavailable"); };
            };

            var options = new DefaultAzureCredentialOptions
            {
                ExcludeEnvironmentCredential        = excludeEnvironmentCredential,
                ExcludeManagedIdentityCredential    = excludeManagedIdentityCredential,
                ExcludeSharedTokenCacheCredential   = excludeSharedTokenCacheCredential,
                ExcludeAzureCliCredential           = excludeCliCredential,
                ExcludeInteractiveBrowserCredential = excludeInteractiveBrowserCredential
            };

            var cred = new DefaultAzureCredential(credFactory, options);

            var ex = Assert.ThrowsAsync <CredentialUnavailableException>(async() => await cred.GetTokenAsync(new TokenRequestContext(MockScopes.Default)));

            if (!excludeEnvironmentCredential)
            {
                Assert.True(ex.Message.Contains("EnvironmentCredential Unavailable"));
            }
            if (!excludeManagedIdentityCredential)
            {
                Assert.True(ex.Message.Contains("ManagedIdentityCredential Unavailable"));
            }
            if (!excludeSharedTokenCacheCredential)
            {
                Assert.True(ex.Message.Contains("SharedTokenCacheCredential Unavailable"));
            }
            if (!excludeCliCredential)
            {
                Assert.True(ex.Message.Contains("CliCredential Unavailable"));
            }
            if (!excludeInteractiveBrowserCredential)
            {
                Assert.True(ex.Message.Contains("InteractiveBrowserCredential Unavailable"));
            }
        }
Beispiel #15
0
 public override TokenCredential CreateManagedIdentityCredential(DefaultAzureCredentialOptions options)
 => new ManagedIdentityCredential(new ManagedIdentityClient(Pipeline, options.ManagedIdentityClientId));
Beispiel #16
0
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddSingleton <ILocalizationProvider, StorageLocalizationProvider>();
            services.AddTextLocalization(options =>
            {
                options.ReturnOnlyKeyIfNotFound = !_environment.IsDevelopment();
                options.FallBackNeutralCulture  = !_environment.IsDevelopment();
            }).Configure <RequestLocalizationOptions>(options =>
            {
                options.DefaultRequestCulture = new RequestCulture(Settings.SupportedCultures[0]);
                options.AddSupportedCultures(Settings.SupportedCultures);
                options.AddSupportedUICultures(Settings.SupportedCultures);
            });

            services.AddScoped <LazyAssemblyLoader>();

            var dataProtectionBuilder = services.AddDataProtection().SetApplicationName(projectName);

            services.RegisterStorage(Configuration);

            services.Configure <ApiBehaviorOptions>(options => { options.SuppressModelStateInvalidFilter = true; });

            services.AddIdentity <ApplicationUser, ApplicationRole>()
            .AddRoles <ApplicationRole>()
            .AddEntityFrameworkStores <ApplicationDbContext>()
            .AddDefaultTokenProviders()
            .AddErrorDescriber <LocalizedIdentityErrorDescriber>();

            services.AddScoped <IUserClaimsPrincipalFactory <ApplicationUser>,
                                AdditionalUserClaimsPrincipalFactory>();

            var authAuthority = Configuration[$"{projectName}:IS4ApplicationUrl"].TrimEnd('/');

            // Adds IdentityServer https://identityserver4.readthedocs.io/en/latest/reference/options.html
            var identityServerBuilder = services.AddIdentityServer(options =>
            {
                options.IssuerUri = authAuthority;
                options.Events.RaiseErrorEvents       = true;
                options.Events.RaiseInformationEvents = true;
                options.Events.RaiseFailureEvents     = true;
                options.Events.RaiseSuccessEvents     = true;
                options.UserInteraction.ErrorUrl      = "/identityserver/error";
            })
                                        .AddIdentityServerStores(Configuration)
                                        .AddAspNetIdentity <ApplicationUser>(); //https://identityserver4.readthedocs.io/en/latest/reference/aspnet_identity.html

            X509Certificate2 cert = null;

            var keysLocalFolder = Path.Combine(_environment.ContentRootPath, "Keys");

            if (_environment.IsDevelopment())
            {
                // The AddDeveloperSigningCredential extension creates temporary key material tempkey.jwk for signing tokens.
                // This might be useful to get started, but needs to be replaced by some persistent key material for production scenarios.
                // See http://docs.identityserver.io/en/release/topics/crypto.html#refcrypto for more information.
                // https://stackoverflow.com/questions/42351274/identityserver4-hosting-in-iis

                identityServerBuilder.AddDeveloperSigningCredential();

                dataProtectionBuilder.PersistKeysToFileSystem(new DirectoryInfo(keysLocalFolder));
            }
            else
            {
                // Running on Azure Web App service - read the setup doc at blazor-boilerplate.readthedocs.io

                // appsettings.json parameters used:
                //  "RunsOnAzure": true,
                //  "RunningAsAppService": true,
                //  "RunningAsDocker": false, // not implemented yet
                //  "AzureKeyVault": {
                //      "UsingKeyVault": true,
                //      "UseManagedAppIdentity": true,
                //      "AppKey": "", // not implemented yet.
                //      "AppSecret": "",
                //      "KeyVaultURI": "https://YOURVAULTNAMEHERE.vault.azure.net/",
                //      "CertificateIdentifier": "https://YOURVAULTNAMEHERE.vault.azure.net/certificates/BBAUTH/<HEX_VERSION_STRING_HERE>",
                //      "CertificateName": "BBAUTH",
                //      "StorageAccountBlobBaseUrl": "https://<YOUR_STORAGE_ACCOUNT_NAME_HERE>.blob.core.windows.net",
                //      "ContainerName": "blazor-boilerplate-keys",
                //      "KeysBlobName": "keys.xml"
                if (Convert.ToBoolean(Configuration["HostingOnAzure:RunsOnAzure"]) == true)
                {
                    if (Convert.ToBoolean(Configuration["HostingOnAzure:AzureKeyVault:UsingKeyVault"]) == true)
                    {
                        if (Convert.ToBoolean(Configuration["HostingOnAzure:AzurekeyVault:UseManagedAppIdentity"]) == true)
                        {
                            //https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/overview

                            // In production environment we have already configured Managed Identity (blazor-boilerplate) using Azure Portal for our app to access Key Vault and Blob Storage.

                            // Set up TokenCredential options for production and development environments
                            bool isDeployed        = !_environment.IsDevelopment();
                            var  credentialOptions = new DefaultAzureCredentialOptions
                            {
                                ExcludeEnvironmentCredential        = isDeployed,
                                ExcludeManagedIdentityCredential    = false, // we only use this one in production
                                ExcludeSharedTokenCacheCredential   = isDeployed,
                                ExcludeVisualStudioCredential       = isDeployed,
                                ExcludeVisualStudioCodeCredential   = isDeployed,
                                ExcludeAzureCliCredential           = isDeployed,
                                ExcludeInteractiveBrowserCredential = isDeployed,
                            };

                            // In development environment DefaultAzureCredential() will use the shared token credential from the IDE. In Visual Studio this is under Options - Azure Service Authentication.
                            if (_environment.IsDevelopment())
                            {
                                // credentialOptions.SharedTokenCacheUsername = "******"; // specify user name to use if more than one Azure username is configured in IDE.
                                // var defaultTenantId = "?????-????-????-????-????????"; // specify AAD tenant to authenticate against (from Azure Portal - AAD - Tenant ID)
                                // credentialOptions.SharedTokenCacheTenantId = defaultTenantId;
                                // credentialOptions.VisualStudioCodeTenantId = defaultTenantId;
                                // credentialOptions.VisualStudioTenantId = defaultTenantId;
                            }

                            TokenCredential tokenCredential = new DefaultAzureCredential(credentialOptions);

                            // The Azure Storage Container (blazor-boilerplate-keys) Access Control must grant blazor-boilerplate the following roles:
                            // - Storage Blob Data Contributor

                            var blobServiceClient = new BlobServiceClient(
                                new Uri(Configuration["HostingOnAzure:AzureKeyVault:StorageAccountBlobBaseUrl"]),
                                tokenCredential);

                            BlobContainerClient blobContainerClient = blobServiceClient.GetBlobContainerClient(Configuration["HostingOnAzure:AzureKeyVault:ContainerName"]);
                            BlobClient          blobClient          = blobContainerClient.GetBlobClient(Configuration["HostingOnAzure:AzureKeyVault:KeysBlobName"]);

                            var certificateIdentifier = Configuration["HostingOnAzure:AzureKeyVault:CertificateIdentifier"];

                            dataProtectionBuilder.PersistKeysToAzureBlobStorage(blobClient);
                            // 1. Remove the call to ProtectKeysWithAzureKeyVault below for the first run to create the keys.xml blob in place.
                            // 2. Add the call to ProtectKeysWithAzureKeyVault for subsequent runs.so that keys.xml gets created - see the setup doc for more information
                            dataProtectionBuilder.ProtectKeysWithAzureKeyVault(new Uri(certificateIdentifier), tokenCredential);

                            // Azure Key Vault Access Policy must grant the following permissions to the blazor-boilerplate app:
                            // - Secret Permissions: Get
                            // - Certificate Permissions: Get

                            // Retrieve the certificate and extract the secret so that we can build the new X509 certificate for later use by Identity Server
                            var certificateClient = new CertificateClient(vaultUri: new Uri(Configuration["HostingOnAzure:AzureKeyVault:KeyVaultUri"]), credential: new DefaultAzureCredential());
                            var secretClient      = new SecretClient(vaultUri: new Uri(Configuration["HostingOnAzure:AzureKeyVault:KeyVaultUri"]), credential: new DefaultAzureCredential());
                            KeyVaultCertificateWithPolicy certificateWithPolicy = certificateClient.GetCertificateAsync(Configuration["HostingOnAzure:AzureKeyVault:CertificateName"]).GetAwaiter().GetResult(); // retrieves latest version of certificate
                            KeyVaultSecretIdentifier      secretIdentifier      = new KeyVaultSecretIdentifier(certificateWithPolicy.SecretId);
                            KeyVaultSecret secret          = secretClient.GetSecretAsync(secretIdentifier.Name, secretIdentifier.Version).GetAwaiter().GetResult();
                            byte[]         privateKeyBytes = Convert.FromBase64String(secret.Value);

                            cert = new X509Certificate2(privateKeyBytes, (string)null, X509KeyStorageFlags.MachineKeySet);
                        }
                    }
                    else // if app id and app secret are used
                    {
                        throw new NotImplementedException();
                    }
                }
                else
                {
                    dataProtectionBuilder.PersistKeysToFileSystem(new DirectoryInfo(keysLocalFolder));
                }

                //TODO this implementation does not consider certificate expiration
                if (Convert.ToBoolean(Configuration[$"{projectName}:UseLocalCertStore"]) == true)
                {
                    var certificateThumbprint = Configuration[$"{projectName}:CertificateThumbprint"];

                    var storeLocation = StoreLocation.LocalMachine;

                    dynamic storeName = "WebHosting";

                    if (OperatingSystem.IsLinux())
                    {
                        storeLocation = StoreLocation.CurrentUser;
                        storeName     = StoreName.My;
                    }

                    using X509Store store = new(storeName, storeLocation);
                    store.Open(OpenFlags.ReadOnly);
                    var certs = store.Certificates.Find(X509FindType.FindByThumbprint, certificateThumbprint, false);
                    if (certs.Count > 0)
                    {
                        cert = certs[0];
                    }
                    else
                    {
                        var certPath = Path.Combine(_environment.ContentRootPath, "AuthSample.pfx");

                        if (File.Exists(certPath))
                        {
                            string certificatePassword = Configuration[$"{projectName}:CertificatePassword"] ?? "Admin123";
                            cert = new X509Certificate2(certPath, certificatePassword,
                                                        X509KeyStorageFlags.MachineKeySet |
                                                        X509KeyStorageFlags.PersistKeySet |
                                                        X509KeyStorageFlags.Exportable);
                        }
                    }

                    store.Close();
                }

                // pass the resulting certificate to Identity Server
                if (cert != null)
                {
                    identityServerBuilder.AddSigningCredential(cert);
                    Log.Logger.Information($"Added certificate {cert.Subject} to Identity Server");
                }
                else if (OperatingSystem.IsWindows())
                {
                    Log.Logger.Debug("Trying to use WebHosting Certificate for Identity Server");
                    identityServerBuilder.AddWebHostingCertificate();
                }
                else
                {
                    throw new Exception("Missing Certificate for Identity Server");
                }
            }

            var authBuilder = services.AddAuthentication(options =>
            {
                options.DefaultScheme = IdentityServerAuthenticationDefaults.AuthenticationScheme;
            })
                              .AddIdentityServerAuthentication(options =>
            {
                options.Authority            = authAuthority;
                options.SupportedTokens      = SupportedTokens.Jwt;
                options.RequireHttpsMetadata = _environment.IsProduction();
                options.ApiName = IdentityServerConfig.LocalApiName;
                options.Events  = new JwtBearerEvents
                {
                    OnMessageReceived = context =>
                    {
                        var accessToken = context.Request.Query["access_token"];

                        // If the request is for our hub...
                        var path = context.HttpContext.Request.Path;
                        if (!string.IsNullOrEmpty(accessToken) &&
                            (path.StartsWithSegments("/chathub")))
                        {
                            // Read the token out of the query string
                            context.Token = accessToken;
                        }
                        return(Task.CompletedTask);
                    }
                };
            });

            #region ExternalAuthProviders
            //https://github.com/dotnet/aspnetcore/blob/master/src/Security/Authentication/samples/SocialSample/Startup.cs
            //https://docs.microsoft.com/en-us/aspnet/core/security/authentication/social/google-logins
            if (Convert.ToBoolean(Configuration["ExternalAuthProviders:Google:Enabled"] ?? "false"))
            {
                authBuilder.AddGoogle(options =>
                {
                    options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;

                    options.ClientId     = Configuration["ExternalAuthProviders:Google:ClientId"];
                    options.ClientSecret = Configuration["ExternalAuthProviders:Google:ClientSecret"];

                    options.AuthorizationEndpoint += "?prompt=consent"; // Hack so we always get a refresh token, it only comes on the first authorization response
                    options.AccessType             = "offline";
                    options.SaveTokens             = true;
                    options.Events = new OAuthEvents()
                    {
                        OnRemoteFailure = HandleOnRemoteFailure
                    };
                    options.ClaimActions.MapJsonSubKey("urn:google:image", "image", "url");
                    options.ClaimActions.Remove(ClaimTypes.GivenName);
                });
            }

            if (Convert.ToBoolean(Configuration["ExternalAuthProviders:Facebook:Enabled"] ?? "false"))
            {
                // You must first create an app with Facebook and add its ID and Secret to your user-secrets.
                // https://developers.facebook.com/apps/
                // https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow#login
                authBuilder.AddFacebook(options =>
                {
                    options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;

                    options.AppId     = Configuration["ExternalAuthProviders:Facebook:AppId"];
                    options.AppSecret = Configuration["ExternalAuthProviders:Facebook:AppSecret"];

                    options.Scope.Add("email");
                    options.Fields.Add("name");
                    options.Fields.Add("email");
                    options.SaveTokens = true;
                    options.Events     = new OAuthEvents()
                    {
                        OnRemoteFailure = HandleOnRemoteFailure
                    };
                });
            }

            if (Convert.ToBoolean(Configuration["ExternalAuthProviders:Twitter:Enabled"] ?? "false"))
            {
                // You must first create an app with Twitter and add its key and Secret to your user-secrets.
                // https://apps.twitter.com/
                // https://developer.twitter.com/en/docs/basics/authentication/api-reference/access_token
                authBuilder.AddTwitter(options =>
                {
                    options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;

                    options.ConsumerKey    = Configuration["ExternalAuthProviders:Twitter:ConsumerKey"];
                    options.ConsumerSecret = Configuration["ExternalAuthProviders:Twitter:ConsumerSecret"];

                    // http://stackoverflow.com/questions/22627083/can-we-get-email-id-from-twitter-oauth-api/32852370#32852370
                    // http://stackoverflow.com/questions/36330675/get-users-email-from-twitter-api-for-external-login-authentication-asp-net-mvc?lq=1
                    options.RetrieveUserDetails = true;
                    options.SaveTokens          = true;
                    options.ClaimActions.MapJsonKey("urn:twitter:profilepicture", "profile_image_url", ClaimTypes.Uri);
                    options.Events = new TwitterEvents()
                    {
                        OnRemoteFailure = HandleOnRemoteFailure
                    };
                });
            }

            //https://github.com/xamarin/Essentials/blob/master/Samples/Sample.Server.WebAuthenticator/Startup.cs
            if (Convert.ToBoolean(Configuration["ExternalAuthProviders:Apple:Enabled"] ?? "false"))
            {
                authBuilder.AddApple(options =>
                {
                    options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;

                    options.ClientId = Configuration["ExternalAuthProviders:Apple:ClientId"];
                    options.KeyId    = Configuration["ExternalAuthProviders:Apple:KeyId"];
                    options.TeamId   = Configuration["ExternalAuthProviders:Apple:TeamId"];

                    options.UsePrivateKey(keyId
                                          => _environment.ContentRootFileProvider.GetFileInfo($"AuthKey_{keyId}.p8"));
                    options.SaveTokens = true;
                });
            }

            // You must first create an app with Microsoft Account and add its ID and Secret to your user-secrets.
            // https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app
            if (Convert.ToBoolean(Configuration["ExternalAuthProviders:Microsoft:Enabled"] ?? "false"))
            {
                authBuilder.AddMicrosoftAccount(options =>
                {
                    options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;

                    options.ClientId     = Configuration["ExternalAuthProviders:Microsoft:ClientId"];
                    options.ClientSecret = Configuration["ExternalAuthProviders:Microsoft:ClientSecret"];

                    options.SaveTokens = true;
                    options.Scope.Add("offline_access");
                    options.Events = new OAuthEvents()
                    {
                        OnRemoteFailure = HandleOnRemoteFailure
                    };
                });
            }
            #endregion

            #region Authorization
            //Add Policies / Claims / Authorization - https://identityserver4.readthedocs.io/en/latest/topics/add_apis.html#advanced
            services.AddScoped <EntityPermissions>();
            services.AddSingleton <IAuthorizationPolicyProvider, AuthorizationPolicyProvider>();
            services.AddTransient <IAuthorizationHandler, DomainRequirementHandler>();
            services.AddTransient <IAuthorizationHandler, PermissionRequirementHandler>();
            #endregion

            services.Configure <IdentityOptions>(options =>
            {
                // Password settings
                options.Password.RequireDigit           = RequireDigit;
                options.Password.RequiredLength         = RequiredLength;
                options.Password.RequireNonAlphanumeric = RequireNonAlphanumeric;
                options.Password.RequireUppercase       = RequireUppercase;
                options.Password.RequireLowercase       = RequireLowercase;
                //options.Password.RequiredUniqueChars = 6;

                // Lockout settings
                options.Lockout.DefaultLockoutTimeSpan  = TimeSpan.FromMinutes(30);
                options.Lockout.MaxFailedAccessAttempts = 10;
                options.Lockout.AllowedForNewUsers      = true;

                // Require Confirmed Email User settings
                if (Convert.ToBoolean(Configuration[$"{projectName}:RequireConfirmedEmail"] ?? "false"))
                {
                    options.User.RequireUniqueEmail      = true;
                    options.SignIn.RequireConfirmedEmail = true;
                }
            });

            #region Cookies
            // cookie policy to deal with temporary browser incompatibilities
            services.AddSameSiteCookiePolicy();

            //https://docs.microsoft.com/en-us/aspnet/core/security/gdpr
            services.Configure <CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential
                // cookies is needed for a given request.
                options.CheckConsentNeeded = context => false; //consent not required
                // requires using Microsoft.AspNetCore.Http;
                //options.MinimumSameSitePolicy = SameSiteMode.None;
            });

            //services.ConfigureExternalCookie(options =>
            // {
            // macOS login fix
            //options.Cookie.SameSite = SameSiteMode.None;
            //});

            services.ConfigureApplicationCookie(options =>
            {
                options.Cookie.IsEssential  = true;
                options.Cookie.HttpOnly     = true;
                options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
                options.ExpireTimeSpan      = TimeSpan.FromDays(Convert.ToDouble(Configuration[$"{projectName}:CookieExpireTimeSpanDays"] ?? "30"));
                options.LoginPath           = Constants.Settings.LoginPath;
                //options.AccessDeniedPath = "/Identity/Account/AccessDenied";
                // ReturnUrlParameter requires
                //using Microsoft.AspNetCore.Authentication.Cookies;
                options.ReturnUrlParameter = CookieAuthenticationDefaults.ReturnUrlParameter;
                options.SlidingExpiration  = true;

                // Suppress redirect on API URLs in ASP.NET Core -> https://stackoverflow.com/a/56384729/54159
                options.Events = new CookieAuthenticationEvents()
                {
                    OnRedirectToAccessDenied = context =>
                    {
                        if (context.Request.Path.StartsWithSegments("/api"))
                        {
                            context.Response.StatusCode = Status403Forbidden;
                        }

                        return(Task.CompletedTask);
                    },
                    OnRedirectToLogin = context =>
                    {
                        context.Response.StatusCode = Status401Unauthorized;
                        return(Task.CompletedTask);
                    }
                };
            });
            #endregion

            services.AddMvc().AddNewtonsoftJson(opt =>
            {
                // Set Breeze defaults for entity serialization
                var ss = JsonSerializationFns.UpdateWithDefaults(opt.SerializerSettings);
                if (ss.ContractResolver is DefaultContractResolver resolver)
                {
                    resolver.NamingStrategy = null;  // remove json camelCasing; names are converted on the client.
                }
                if (_environment.IsDevelopment())
                {
                    ss.Formatting = Newtonsoft.Json.Formatting.Indented; // format JSON for debugging
                }
            })                                                           // Add Breeze exception filter to send errors back to the client
            .AddMvcOptions(o => { o.Filters.Add(new GlobalExceptionFilter()); })
            .AddViewLocalization().AddDataAnnotationsLocalization(options =>
            {
                options.DataAnnotationLocalizerProvider = (type, factory) =>
                {
                    return(factory.Create(typeof(Global)));
                };
            }).AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining <LocalizationRecordValidator>());

            services.AddServerSideBlazor().AddCircuitOptions(o =>
            {
                if (_environment.IsDevelopment())
                {
                    o.DetailedErrors = true;
                }
            }).AddHubOptions(o =>
            {
                o.MaximumReceiveMessageSize = 131072;
            });

            services.AddSignalR();

            if (_enableAPIDoc)
            {
                services.AddOpenApiDocument(document =>
                {
                    document.Title   = "BlazorBoilerplate API";
                    document.Version = typeof(Startup).GetTypeInfo().Assembly.GetName().Version.ToString();
                    document.AddSecurity("bearer", Enumerable.Empty <string>(), new OpenApiSecurityScheme
                    {
                        Type             = OpenApiSecuritySchemeType.OAuth2,
                        Description      = "Local Identity Server",
                        OpenIdConnectUrl = $"{authAuthority}/.well-known/openid-configuration", //not working
                        Flow             = OpenApiOAuth2Flow.AccessCode,
                        Flows            = new OpenApiOAuthFlows()
                        {
                            AuthorizationCode = new OpenApiOAuthFlow()
                            {
                                Scopes = new Dictionary <string, string>
                                {
                                    { LocalApi.ScopeName, IdentityServerConfig.LocalApiName }
                                },
                                AuthorizationUrl = $"{authAuthority}/connect/authorize",
                                TokenUrl         = $"{authAuthority}/connect/token"
                            },
                        }
                    });;

                    document.OperationProcessors.Add(new AspNetCoreOperationSecurityScopeProcessor("bearer"));
                    //      new OperationSecurityScopeProcessor("bearer"));
                });
            }

            services.AddScoped <IUserSession, UserSession>();

            services.AddSingleton <IHttpContextAccessor, HttpContextAccessor>();

            services.Add(ServiceDescriptor.Scoped(typeof(ITenantSettings <>), typeof(TenantSettingsManager <>)));

            services.AddTransient <IEmailFactory, EmailFactory>();

            services.AddTransient <IAccountManager, AccountManager>();
            services.AddTransient <IAdminManager, AdminManager>();
            services.AddTransient <IEmailManager, EmailManager>();
            services.AddTransient <IExternalAuthManager, ExternalAuthManager>();

            #region Automapper
            //Automapper to map DTO to Models https://www.c-sharpcorner.com/UploadFile/1492b1/crud-operations-using-automapper-in-mvc-application/
            var automapperConfig = new MapperConfiguration(configuration =>
            {
                configuration.AddProfile(new MappingProfile());
            });

            var autoMapper = automapperConfig.CreateMapper();

            services.AddSingleton(autoMapper);
            #endregion

            /* ServerSideBlazor */
            services.AddScoped <IAccountApiClient, AccountApiClient>();
            services.AddScoped <AppState>();

            // setup HttpClient for server side in a client side compatible fashion ( with auth cookie )
            // if (!services.Any(x => x.ServiceType == typeof(HttpClient)))
            // {
            services.AddScoped(s =>
            {
                // creating the URI helper needs to wait until the JS Runtime is initialized, so defer it.
                var navigationManager   = s.GetRequiredService <NavigationManager>();
                var httpContextAccessor = s.GetRequiredService <IHttpContextAccessor>();
                var cookies             = httpContextAccessor.HttpContext.Request.Cookies;
                var httpClientHandler   = new HttpClientHandler()
                {
                    UseCookies = false
                };
                if (_environment.IsDevelopment())
                {
                    // Return 'true' to allow certificates that are untrusted/invalid
                    httpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => { return(true); };
                }
                var client = new HttpClient(httpClientHandler);
                if (cookies.Any())
                {
                    var cks = new List <string>();

                    foreach (var cookie in cookies)
                    {
                        cks.Add($"{cookie.Key}={cookie.Value}");
                    }

                    client.DefaultRequestHeaders.Add("Cookie", string.Join(';', cks));
                }

                client.BaseAddress = new Uri(navigationManager.BaseUri);

                return(client);
            });
            // }

            services.AddScoped <ILocalizationApiClient, LocalizationApiClient>();
            services.AddScoped <IApiClient, ApiClient>();

            // Authentication providers
            Log.Logger.Debug("Removing AuthenticationStateProvider...");
            var serviceDescriptor = services.FirstOrDefault(descriptor => descriptor.ServiceType == typeof(AuthenticationStateProvider));
            if (serviceDescriptor != null)
            {
                services.Remove(serviceDescriptor);
            }

            Log.Logger.Debug("Adding AuthenticationStateProvider...");
            services.AddScoped <AuthenticationStateProvider, IdentityAuthenticationStateProvider>();
            /**********************/

            services.AddModules();

            if (Log.Logger.IsEnabled(Serilog.Events.LogEventLevel.Debug))
            {
                Log.Logger.Debug($"Total Services Registered: {services.Count}");
                foreach (var service in services)
                {
                    Log.Logger.Debug($"\n\tService: {service.ServiceType.FullName}\n\tLifetime: {service.Lifetime}\n\tInstance: {service.ImplementationType?.FullName}");
                }
            }
        }
Beispiel #17
0
 public DefaultAzureMgmtCredential(DefaultAzureCredentialOptions options, string[] scopes = null) : base(new DefaultAzureCredentialTokenProvider(options, scopes))
 {
 }
        public void ValidateCtorOptionsPassedToCredentials([Values(ManagedIdentityIdType.None, ManagedIdentityIdType.ClientId, ManagedIdentityIdType.ResourceId)] ManagedIdentityIdType managedIdentityIdType)
        {
            string expClientId                    = Guid.NewGuid().ToString();
            string expUsername                    = Guid.NewGuid().ToString();
            string expCacheTenantId               = Guid.NewGuid().ToString();
            string expBrowserTenantId             = Guid.NewGuid().ToString();
            string expVsTenantId                  = Guid.NewGuid().ToString();
            string expCodeTenantId                = Guid.NewGuid().ToString();
            string expResourceId                  = $"/subscriptions/{Guid.NewGuid().ToString()}/locations/MyLocation";
            string actClientId_ManagedIdentity    = null;
            string actResiurceId_ManagedIdentity  = null;
            string actClientId_InteractiveBrowser = null;
            string actUsername                    = null;
            string actCacheTenantId               = null;
            string actBrowserTenantId             = null;
            string actVsTenantId                  = null;
            string actCodeTenantId                = null;

            var credFactory = new MockDefaultAzureCredentialFactory(CredentialPipeline.GetInstance(null));

            credFactory.OnCreateManagedIdentityCredential = (options, _) =>
            {
                actClientId_ManagedIdentity   = options.ManagedIdentityClientId;
                actResiurceId_ManagedIdentity = options.ManagedIdentityResourceId?.ToString();
            };
            credFactory.OnCreateSharedTokenCacheCredential   = (tenantId, username, _) => { actCacheTenantId = tenantId; actUsername = username; };
            credFactory.OnCreateInteractiveBrowserCredential = (tenantId, clientId, _) => { actBrowserTenantId = tenantId; actClientId_InteractiveBrowser = clientId; };
            credFactory.OnCreateVisualStudioCredential       = (tenantId, _) => { actVsTenantId = tenantId; };
            credFactory.OnCreateVisualStudioCodeCredential   = (tenantId, _) => { actCodeTenantId = tenantId; };
            credFactory.OnCreateAzurePowerShellCredential    = _ => {};

            var options = new DefaultAzureCredentialOptions
            {
                InteractiveBrowserCredentialClientId = expClientId,
                SharedTokenCacheUsername             = expUsername,
                ExcludeSharedTokenCacheCredential    = false,
                SharedTokenCacheTenantId             = expCacheTenantId,
                VisualStudioTenantId                = expVsTenantId,
                VisualStudioCodeTenantId            = expCodeTenantId,
                InteractiveBrowserTenantId          = expBrowserTenantId,
                ExcludeInteractiveBrowserCredential = false,
            };

            switch (managedIdentityIdType)
            {
            case ManagedIdentityIdType.ClientId:
                options.ManagedIdentityClientId = expClientId;
                break;

            case ManagedIdentityIdType.ResourceId:
                options.ManagedIdentityResourceId = new ResourceIdentifier(expResourceId);
                break;
            }

            new DefaultAzureCredential(credFactory, options);

            Assert.AreEqual(expClientId, actClientId_InteractiveBrowser);
            Assert.AreEqual(expUsername, actUsername);
            Assert.AreEqual(expCacheTenantId, actCacheTenantId);
            Assert.AreEqual(expBrowserTenantId, actBrowserTenantId);
            Assert.AreEqual(expVsTenantId, actVsTenantId);
            Assert.AreEqual(expCodeTenantId, actCodeTenantId);
            switch (managedIdentityIdType)
            {
            case ManagedIdentityIdType.ClientId:
                Assert.AreEqual(expClientId, actClientId_ManagedIdentity);
                break;

            case ManagedIdentityIdType.ResourceId:
                Assert.AreEqual(expResourceId, actResiurceId_ManagedIdentity);
                break;

            case ManagedIdentityIdType.None:
                Assert.IsNull(actClientId_ManagedIdentity);
                Assert.IsNull(actResiurceId_ManagedIdentity);
                break;
            }
        }
Beispiel #19
0
 public DefaultAzureCredentialTokenProvider(DefaultAzureCredentialOptions options, string[] scopes = null) : this(new DefaultAzureCredential(options), scopes)
 {
 }
        /// <summary>
        /// Load a certificate from Key Vault, including the private key.
        /// </summary>
        /// <param name="keyVaultUrl">URL of Key Vault.</param>
        /// <param name="certificateName">Name of the certificate.</param>
        /// <param name="x509KeyStorageFlags">Defines where and how to import the private key of an X.509 certificate.</param>
        /// <returns>An <see cref="X509Certificate2"/> certificate.</returns>
        /// <remarks>This code is inspired by Heath Stewart's code in:
        /// https://github.com/heaths/azsdk-sample-getcert/blob/master/Program.cs#L46-L82.
        /// </remarks>
        private static X509Certificate2?LoadFromKeyVault(
            string keyVaultUrl,
            string certificateName,
            X509KeyStorageFlags x509KeyStorageFlags)
        {
            Uri keyVaultUri = new Uri(keyVaultUrl);
            DefaultAzureCredentialOptions options = new DefaultAzureCredentialOptions
            {
                ManagedIdentityClientId = UserAssignedManagedIdentityClientId,
            };
            DefaultAzureCredential credential        = new DefaultAzureCredential(options);
            CertificateClient      certificateClient = new CertificateClient(keyVaultUri, credential);
            SecretClient           secretClient      = new SecretClient(keyVaultUri, credential);

            KeyVaultCertificateWithPolicy certificate = certificateClient.GetCertificate(certificateName);

            if (certificate.Properties.NotBefore == null || certificate.Properties.ExpiresOn == null)
            {
                return(null);
            }

            if (DateTimeOffset.UtcNow < certificate.Properties.NotBefore || DateTimeOffset.UtcNow > certificate.Properties.ExpiresOn)
            {
                return(null);
            }

            // Return a certificate with only the public key if the private key is not exportable.
            if (certificate.Policy?.Exportable != true)
            {
                return(new X509Certificate2(
                           certificate.Cer,
                           (string?)null,
                           x509KeyStorageFlags));
            }

            // Parse the secret ID and version to retrieve the private key.
            string[] segments = certificate.SecretId.AbsolutePath.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
            if (segments.Length != 3)
            {
                throw new InvalidOperationException(string.Format(
                                                        CultureInfo.InvariantCulture,
                                                        CertificateErrorMessage.IncorrectNumberOfUriSegments,
                                                        segments.Length,
                                                        certificate.SecretId));
            }

            string secretName    = segments[1];
            string secretVersion = segments[2];

            KeyVaultSecret secret = secretClient.GetSecret(secretName, secretVersion);

            // For PEM, you'll need to extract the base64-encoded message body.
            // .NET 5.0 preview introduces the System.Security.Cryptography.PemEncoding class to make this easier.
            if (CertificateConstants.MediaTypePksc12.Equals(secret.Properties.ContentType, StringComparison.OrdinalIgnoreCase))
            {
                return(LoadFromBase64Encoded(secret.Value, x509KeyStorageFlags));
            }

            throw new NotSupportedException(
                      string.Format(
                          CultureInfo.InvariantCulture,
                          CertificateErrorMessage.OnlyPkcs12IsSupported,
                          secret.Properties.ContentType));
        }
Beispiel #21
0
        public void ValidateEmptyEnvironmentBasedOptionsNotPassedToCredentials([Values] bool clientIdSpecified, [Values] bool usernameSpecified, [Values] bool tenantIdSpecified)
        {
            var  expClientId               = clientIdSpecified ? string.Empty : null;
            var  expUsername               = usernameSpecified ? string.Empty : null;
            var  expTenantId               = tenantIdSpecified ? string.Empty : null;
            bool onCreateSharedCalled      = false;
            bool onCreatedManagedCalled    = false;
            bool onCreateInteractiveCalled = false;
            bool onCreateVsCalled          = false;
            bool onCreateVsCodeCalled      = false;

            using (new TestEnvVar(new Dictionary <string, string>
            {
                { "AZURE_CLIENT_ID", expClientId },
                { "AZURE_USERNAME", expUsername },
                { "AZURE_TENANT_ID", expTenantId }
            }))
            {
                var credFactory = new MockDefaultAzureCredentialFactory(CredentialPipeline.GetInstance(null));

                credFactory.OnCreateManagedIdentityCredential = (clientId, _) =>
                {
                    onCreatedManagedCalled = true;
                    Assert.IsNull(clientId);
                };

                credFactory.OnCreateSharedTokenCacheCredential = (tenantId, username, _) =>
                {
                    onCreateSharedCalled = true;
                    Assert.IsNull(tenantId);
                    Assert.IsNull(username);
                };

                credFactory.OnCreateInteractiveBrowserCredential = (tenantId, _) =>
                {
                    onCreateInteractiveCalled = true;
                    Assert.IsNull(tenantId);
                };

                credFactory.OnCreateVisualStudioCredential = (tenantId, _) =>
                {
                    onCreateVsCalled = true;
                    Assert.IsNull(tenantId);
                };

                credFactory.OnCreateVisualStudioCodeCredential = (tenantId, _) =>
                {
                    onCreateVsCodeCalled = true;
                    Assert.IsNull(tenantId);
                };
                var options = new DefaultAzureCredentialOptions
                {
                    ExcludeEnvironmentCredential        = true,
                    ExcludeManagedIdentityCredential    = false,
                    ExcludeSharedTokenCacheCredential   = false,
                    ExcludeVisualStudioCredential       = false,
                    ExcludeVisualStudioCodeCredential   = false,
                    ExcludeAzureCliCredential           = true,
                    ExcludeInteractiveBrowserCredential = false
                };

                new DefaultAzureCredential(credFactory, options);

                Assert.IsTrue(onCreateSharedCalled);
                Assert.IsTrue(onCreatedManagedCalled);
                Assert.IsTrue(onCreateInteractiveCalled);
                Assert.IsTrue(onCreateVsCalled);
                Assert.IsTrue(onCreateVsCodeCalled);
            }
        }
Beispiel #22
0
        public void ValidateAllUnavailable([Values(true, false)] bool excludeEnvironmentCredential,
                                           [Values(true, false)] bool excludeManagedIdentityCredential,
                                           [Values(true, false)] bool excludeSharedTokenCacheCredential,
                                           [Values(true, false)] bool excludeVisualStudioCredential,
                                           [Values(true, false)] bool excludeVisualStudioCodeCredential,
                                           [Values(true, false)] bool excludeCliCredential,
                                           [Values(true, false)] bool excludePowerShellCredential,
                                           [Values(true, false)] bool excludeInteractiveBrowserCredential)
        {
            if (excludeEnvironmentCredential && excludeManagedIdentityCredential && excludeSharedTokenCacheCredential && excludeVisualStudioCredential && excludeVisualStudioCodeCredential && excludeCliCredential && excludeInteractiveBrowserCredential)
            {
                Assert.Pass();
            }

            var credFactory = new MockDefaultAzureCredentialFactory(CredentialPipeline.GetInstance(null));

            void SetupMockForException <T>(Mock <T> mock) where T : TokenCredential =>
            mock.Setup(m => m.GetTokenAsync(It.IsAny <TokenRequestContext>(), It.IsAny <CancellationToken>()))
            .Throws(new CredentialUnavailableException($"{typeof(T).Name} Unavailable"));

            credFactory.OnCreateEnvironmentCredential = c =>
                                                        SetupMockForException(c);
            credFactory.OnCreateInteractiveBrowserCredential = (_, c) =>
                                                               SetupMockForException(c);
            credFactory.OnCreateManagedIdentityCredential = (_, c) =>
                                                            SetupMockForException(c);
            credFactory.OnCreateSharedTokenCacheCredential = (_, _, c) =>
                                                             SetupMockForException(c);
            credFactory.OnCreateAzureCliCredential = c =>
                                                     SetupMockForException(c);
            credFactory.OnCreateAzurePowerShellCredential = c =>
                                                            SetupMockForException(c);
            credFactory.OnCreateVisualStudioCredential = (_, c) =>
                                                         SetupMockForException(c);
            credFactory.OnCreateVisualStudioCodeCredential = (_, c) =>
                                                             SetupMockForException(c);

            var options = new DefaultAzureCredentialOptions
            {
                ExcludeEnvironmentCredential        = excludeEnvironmentCredential,
                ExcludeManagedIdentityCredential    = excludeManagedIdentityCredential,
                ExcludeSharedTokenCacheCredential   = excludeSharedTokenCacheCredential,
                ExcludeVisualStudioCredential       = excludeVisualStudioCredential,
                ExcludeVisualStudioCodeCredential   = excludeVisualStudioCodeCredential,
                ExcludeAzureCliCredential           = excludeCliCredential,
                ExcludeAzurePowerShellCredential    = excludePowerShellCredential,
                ExcludeInteractiveBrowserCredential = excludeInteractiveBrowserCredential
            };

            var cred = new DefaultAzureCredential(credFactory, options);

            var ex = Assert.ThrowsAsync <CredentialUnavailableException>(async() => await cred.GetTokenAsync(new TokenRequestContext(MockScopes.Default)));

            if (!excludeEnvironmentCredential)
            {
                Assert.True(ex.Message.Contains("EnvironmentCredential Unavailable"));
            }
            if (!excludeManagedIdentityCredential)
            {
                Assert.True(ex.Message.Contains("ManagedIdentityCredential Unavailable"));
            }
            if (!excludeSharedTokenCacheCredential)
            {
                Assert.True(ex.Message.Contains("SharedTokenCacheCredential Unavailable"));
            }
            if (!excludeCliCredential)
            {
                Assert.True(ex.Message.Contains("CliCredential Unavailable"));
            }
            if (!excludePowerShellCredential)
            {
                Assert.True(ex.Message.Contains("PowerShellCredential Unavailable"));
            }
            if (!excludeInteractiveBrowserCredential)
            {
                Assert.True(ex.Message.Contains("InteractiveBrowserCredential Unavailable"));
            }
            if (!excludeVisualStudioCredential)
            {
                Assert.True(ex.Message.Contains("VisualStudioCredential Unavailable"));
            }
            if (!excludeVisualStudioCodeCredential)
            {
                Assert.True(ex.Message.Contains("VisualStudioCodeCredential Unavailable"));
            }
        }
        /// <summary>
        /// Creates the options for the underlying default credential, given the existing options and those configured by Cogito Azure Identity.
        /// </summary>
        /// <param name="options"></param>
        /// <param name="source"></param>
        /// <returns></returns>
        static DefaultAzureCredentialOptions CreateDefaultOptions(AzureIdentityOptions options, DefaultAzureCredentialOptions source)
        {
            var o = new DefaultAzureCredentialOptions();

            if (source != null)
            {
                o.ExcludeAzureCliCredential           = source.ExcludeAzureCliCredential;
                o.ExcludeAzurePowerShellCredential    = source.ExcludeAzurePowerShellCredential;
                o.ExcludeEnvironmentCredential        = source.ExcludeEnvironmentCredential;
                o.ExcludeInteractiveBrowserCredential = source.ExcludeInteractiveBrowserCredential;
                o.ExcludeManagedIdentityCredential    = source.ExcludeManagedIdentityCredential;
                o.ExcludeSharedTokenCacheCredential   = source.ExcludeSharedTokenCacheCredential;
                o.ExcludeVisualStudioCodeCredential   = source.ExcludeVisualStudioCodeCredential;
                o.ExcludeVisualStudioCredential       = source.ExcludeVisualStudioCredential;

                if (source.ManagedIdentityClientId != null)
                {
                    o.ManagedIdentityClientId = source.ManagedIdentityClientId;
                }

                if (source.SharedTokenCacheTenantId != null)
                {
                    o.SharedTokenCacheTenantId = source.SharedTokenCacheTenantId;
                }

                if (source.SharedTokenCacheUsername != null)
                {
                    o.SharedTokenCacheUsername = source.SharedTokenCacheUsername;
                }

                if (source.InteractiveBrowserTenantId != null)
                {
                    o.InteractiveBrowserTenantId = source.InteractiveBrowserTenantId;
                }

                if (source.VisualStudioTenantId != null)
                {
                    o.VisualStudioTenantId = source.VisualStudioTenantId;
                }

                if (source.VisualStudioCodeTenantId != null)
                {
                    o.VisualStudioCodeTenantId = source.VisualStudioCodeTenantId;
                }

                if (source.Transport != null)
                {
                    o.Transport = source.Transport;
                }

                if (source.AuthorityHost != null)
                {
                    o.AuthorityHost = source.AuthorityHost;
                }
            }

            if (options != null)
            {
                if (options.Instance != null)
                {
                    o.AuthorityHost = new Uri(options.Instance);
                }

                if (options.TenantId != null)
                {
                    o.SharedTokenCacheTenantId   = options.TenantId;
                    o.InteractiveBrowserTenantId = options.TenantId;
                    o.VisualStudioTenantId       = options.TenantId;
                    o.VisualStudioCodeTenantId   = options.TenantId;
                }
            }

            return(o);
        }
        public void ValidateUnhandledException([Values(0, 1, 2, 3, 4)] int exPossition)
        {
            var credFactory = new MockDefaultAzureCredentialFactory(CredentialPipeline.GetInstance(null));

            credFactory.OnCreateEnvironmentCredential = (c) =>
            {
                ((MockTokenCredential)c).TokenFactory = (context, cancel) =>
                {
                    if (exPossition > 0)
                    {
                        throw new CredentialUnavailableException("EnvironmentCredential Unavailable");
                    }
                    else
                    {
                        throw new MockClientException("EnvironmentCredential unhandled exception");
                    }
                };
            };
            credFactory.OnCreateManagedIdentityCredential = (clientId, c) =>
            {
                ((MockTokenCredential)c).TokenFactory = (context, cancel) =>
                {
                    if (exPossition > 1)
                    {
                        throw new CredentialUnavailableException("ManagedIdentityCredential Unavailable");
                    }
                    else
                    {
                        throw new MockClientException("ManagedIdentityCredential unhandled exception");
                    }
                };
            };
            credFactory.OnCreateSharedTokenCacheCredential = (tenantId, username, c) =>
            {
                ((MockTokenCredential)c).TokenFactory = (context, cancel) =>
                {
                    if (exPossition > 2)
                    {
                        throw new CredentialUnavailableException("SharedTokenCacheCredential Unavailable");
                    }
                    else
                    {
                        throw new MockClientException("SharedTokenCacheCredential unhandled exception");
                    }
                };
            };
            credFactory.OnCreateAzureCliCredential = (c) =>
            {
                ((MockTokenCredential)c).TokenFactory = (context, cancel) =>
                {
                    if (exPossition > 3)
                    {
                        throw new CredentialUnavailableException("CliCredential Unavailable");
                    }
                    else
                    {
                        throw new MockClientException("CliCredential unhandled exception");
                    }
                };
            };
            credFactory.OnCreateInteractiveBrowserCredential = (_, c) =>
            {
                ((MockTokenCredential)c).TokenFactory = (context, cancel) =>
                {
                    throw new MockClientException("InteractiveBrowserCredential unhandled exception");
                };
            };

            var options = new DefaultAzureCredentialOptions
            {
                ExcludeEnvironmentCredential        = false,
                ExcludeManagedIdentityCredential    = false,
                ExcludeSharedTokenCacheCredential   = false,
                ExcludeAzureCliCredential           = false,
                ExcludeInteractiveBrowserCredential = false
            };

            var cred = new DefaultAzureCredential(credFactory, options);

            var ex = Assert.ThrowsAsync <AuthenticationFailedException>(async() => await cred.GetTokenAsync(new TokenRequestContext(MockScopes.Default)));

            switch (exPossition)
            {
            case 0:
                Assert.AreEqual(ex.InnerException.Message, "EnvironmentCredential unhandled exception");
                break;

            case 1:
                Assert.AreEqual(ex.InnerException.Message, "ManagedIdentityCredential unhandled exception");
                break;

            case 2:
                Assert.AreEqual(ex.InnerException.Message, "SharedTokenCacheCredential unhandled exception");
                break;

            case 3:
                Assert.AreEqual(ex.InnerException.Message, "CliCredential unhandled exception");
                break;

            case 4:
                Assert.AreEqual(ex.InnerException.Message, "InteractiveBrowserCredential unhandled exception");
                break;

            default:
                Assert.Fail();
                break;
            }
        }
        public async Task ValidateSelectedCredentialCaching([Values(typeof(EnvironmentCredential), typeof(ManagedIdentityCredential), typeof(SharedTokenCacheCredential), typeof(AzureCliCredential), typeof(InteractiveBrowserCredential))] Type availableCredential)
        {
            var expToken = new AccessToken(Guid.NewGuid().ToString(), DateTimeOffset.MaxValue);

            var credFactory = new MockDefaultAzureCredentialFactory(CredentialPipeline.GetInstance(null));

            List <Type> calledCredentials = new List <Type>();

            credFactory.OnCreateEnvironmentCredential = (c) =>
            {
                ((MockTokenCredential)c).TokenFactory = (context, cancel) =>
                {
                    calledCredentials.Add(typeof(EnvironmentCredential));

                    return((availableCredential == typeof(EnvironmentCredential)) ? expToken : throw new CredentialUnavailableException("Unavailable"));
                };
            };
            credFactory.OnCreateManagedIdentityCredential = (clientId, c) =>
            {
                ((MockTokenCredential)c).TokenFactory = (context, cancel) =>
                {
                    calledCredentials.Add(typeof(ManagedIdentityCredential));

                    return((availableCredential == typeof(ManagedIdentityCredential)) ? expToken : throw new CredentialUnavailableException("Unavailable"));
                };
            };
            credFactory.OnCreateSharedTokenCacheCredential = (tenantId, username, c) =>
            {
                ((MockTokenCredential)c).TokenFactory = (context, cancel) =>
                {
                    calledCredentials.Add(typeof(SharedTokenCacheCredential));

                    return((availableCredential == typeof(SharedTokenCacheCredential)) ? expToken : throw new CredentialUnavailableException("Unavailable"));
                };
            };
            credFactory.OnCreateAzureCliCredential = (c) =>
            {
                ((MockTokenCredential)c).TokenFactory = (context, cancel) =>
                {
                    calledCredentials.Add(typeof(AzureCliCredential));

                    return((availableCredential == typeof(AzureCliCredential)) ? expToken : throw new CredentialUnavailableException("Unavailable"));
                };
            };
            credFactory.OnCreateInteractiveBrowserCredential = (_, c) =>
            {
                ((MockTokenCredential)c).TokenFactory = (context, cancel) =>
                {
                    calledCredentials.Add(typeof(InteractiveBrowserCredential));

                    return((availableCredential == typeof(InteractiveBrowserCredential)) ? expToken : throw new CredentialUnavailableException("Unavailable"));
                };
            };

            var options = new DefaultAzureCredentialOptions
            {
                ExcludeEnvironmentCredential        = false,
                ExcludeManagedIdentityCredential    = false,
                ExcludeSharedTokenCacheCredential   = false,
                ExcludeAzureCliCredential           = false,
                ExcludeInteractiveBrowserCredential = false
            };

            var cred = new DefaultAzureCredential(credFactory, options);

            AccessToken actToken = await cred.GetTokenAsync(new TokenRequestContext(MockScopes.Default));

            Assert.AreEqual(expToken.Token, actToken.Token);

            // assert that the available credential was the last credential called
            Assert.AreEqual(calledCredentials[calledCredentials.Count - 1], availableCredential);

            calledCredentials.Clear();

            actToken = await cred.GetTokenAsync(new TokenRequestContext(MockScopes.Default));

            Assert.AreEqual(expToken.Token, actToken.Token);

            // assert that the available credential was the only credential called
            Assert.AreEqual(calledCredentials.Count, 1);

            Assert.AreEqual(calledCredentials[0], availableCredential);
        }
Beispiel #26
0
        /// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/ActiveDirectoryAuthenticationProvider.xml' path='docs/members[@name="ActiveDirectoryAuthenticationProvider"]/AcquireTokenAsync/*'/>
        public override async Task <SqlAuthenticationToken> AcquireTokenAsync(SqlAuthenticationParameters parameters)
        {
            CancellationTokenSource cts = new CancellationTokenSource();

            // Use Connection timeout value to cancel token acquire request after certain period of time.
            cts.CancelAfter(parameters.ConnectionTimeout * 1000); // Convert to milliseconds

            string scope = parameters.Resource.EndsWith(s_defaultScopeSuffix) ? parameters.Resource : parameters.Resource + s_defaultScopeSuffix;

            string[] scopes = new string[] { scope };

            int    seperatorIndex = parameters.Authority.LastIndexOf('/');
            string tenantId       = parameters.Authority.Substring(seperatorIndex + 1);
            string authority      = parameters.Authority.Remove(seperatorIndex + 1);

            TokenRequestContext tokenRequestContext = new TokenRequestContext(scopes);
            string clientId = string.IsNullOrWhiteSpace(parameters.UserId) ? null : parameters.UserId;

            if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryDefault)
            {
                DefaultAzureCredentialOptions defaultAzureCredentialOptions = new DefaultAzureCredentialOptions()
                {
                    AuthorityHost                       = new Uri(authority),
                    ManagedIdentityClientId             = clientId,
                    InteractiveBrowserTenantId          = tenantId,
                    SharedTokenCacheTenantId            = tenantId,
                    SharedTokenCacheUsername            = clientId,
                    VisualStudioCodeTenantId            = tenantId,
                    VisualStudioTenantId                = tenantId,
                    ExcludeInteractiveBrowserCredential = true // Force disabled, even though it's disabled by default to respect driver specifications.
                };
                AccessToken accessToken = await new DefaultAzureCredential(defaultAzureCredentialOptions).GetTokenAsync(tokenRequestContext, cts.Token);
                SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Default auth mode. Expiry Time: {0}", accessToken.ExpiresOn);
                return(new SqlAuthenticationToken(accessToken.Token, accessToken.ExpiresOn));
            }

            TokenCredentialOptions tokenCredentialOptions = new TokenCredentialOptions()
            {
                AuthorityHost = new Uri(authority)
            };

            if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryManagedIdentity || parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryMSI)
            {
                AccessToken accessToken = await new ManagedIdentityCredential(clientId, tokenCredentialOptions).GetTokenAsync(tokenRequestContext, cts.Token);
                SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Managed Identity auth mode. Expiry Time: {0}", accessToken.ExpiresOn);
                return(new SqlAuthenticationToken(accessToken.Token, accessToken.ExpiresOn));
            }

            AuthenticationResult result;

            if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryServicePrincipal)
            {
                AccessToken accessToken = await new ClientSecretCredential(tenantId, parameters.UserId, parameters.Password, tokenCredentialOptions).GetTokenAsync(tokenRequestContext, cts.Token);
                SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Active Directory Service Principal auth mode. Expiry Time: {0}", accessToken.ExpiresOn);
                return(new SqlAuthenticationToken(accessToken.Token, accessToken.ExpiresOn));
            }

            /*
             * Today, MSAL.NET uses another redirect URI by default in desktop applications that run on Windows
             * (urn:ietf:wg:oauth:2.0:oob). In the future, we'll want to change this default, so we recommend
             * that you use https://login.microsoftonline.com/common/oauth2/nativeclient.
             *
             * https://docs.microsoft.com/en-us/azure/active-directory/develop/scenario-desktop-app-registration#redirect-uris
             */
            string redirectUri = s_nativeClientRedirectUri;

#if NETCOREAPP
            if (parameters.AuthenticationMethod != SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow)
            {
                redirectUri = "http://localhost";
            }
#endif
            PublicClientAppKey pcaKey = new PublicClientAppKey(parameters.Authority, redirectUri, _applicationClientId
#if NETFRAMEWORK
                                                               , _iWin32WindowFunc
#endif
#if NETSTANDARD
                                                               , _parentActivityOrWindowFunc
#endif
                                                               );

            IPublicClientApplication app = GetPublicClientAppInstance(pcaKey);

            if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryIntegrated)
            {
                if (!string.IsNullOrEmpty(parameters.UserId))
                {
                    result = await app.AcquireTokenByIntegratedWindowsAuth(scopes)
                             .WithCorrelationId(parameters.ConnectionId)
                             .WithUsername(parameters.UserId)
                             .ExecuteAsync(cancellationToken: cts.Token);
                }
                else
                {
                    result = await app.AcquireTokenByIntegratedWindowsAuth(scopes)
                             .WithCorrelationId(parameters.ConnectionId)
                             .ExecuteAsync(cancellationToken: cts.Token);
                }
                SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Active Directory Integrated auth mode. Expiry Time: {0}", result?.ExpiresOn);
            }
            else if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryPassword)
            {
                SecureString password = new SecureString();
                foreach (char c in parameters.Password)
                {
                    password.AppendChar(c);
                }
                password.MakeReadOnly();

                result = await app.AcquireTokenByUsernamePassword(scopes, parameters.UserId, password)
                         .WithCorrelationId(parameters.ConnectionId)
                         .ExecuteAsync(cancellationToken: cts.Token);

                SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token for Active Directory Password auth mode. Expiry Time: {0}", result?.ExpiresOn);
            }
            else if (parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryInteractive ||
                     parameters.AuthenticationMethod == SqlAuthenticationMethod.ActiveDirectoryDeviceCodeFlow)
            {
                // Fetch available accounts from 'app' instance
                System.Collections.Generic.IEnumerator <IAccount> accounts = (await app.GetAccountsAsync()).GetEnumerator();

                IAccount account = default;
                if (accounts.MoveNext())
                {
                    if (!string.IsNullOrEmpty(parameters.UserId))
                    {
                        do
                        {
                            IAccount currentVal = accounts.Current;
                            if (string.Compare(parameters.UserId, currentVal.Username, StringComparison.InvariantCultureIgnoreCase) == 0)
                            {
                                account = currentVal;
                                break;
                            }
                        }while (accounts.MoveNext());
                    }
                    else
                    {
                        account = accounts.Current;
                    }
                }

                if (null != account)
                {
                    try
                    {
                        // If 'account' is available in 'app', we use the same to acquire token silently.
                        // Read More on API docs: https://docs.microsoft.com/dotnet/api/microsoft.identity.client.clientapplicationbase.acquiretokensilent
                        result = await app.AcquireTokenSilent(scopes, account).ExecuteAsync(cancellationToken: cts.Token);

                        SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (silent) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, result?.ExpiresOn);
                    }
                    catch (MsalUiRequiredException)
                    {
                        // An 'MsalUiRequiredException' is thrown in the case where an interaction is required with the end user of the application,
                        // for instance, if no refresh token was in the cache, or the user needs to consent, or re-sign-in (for instance if the password expired),
                        // or the user needs to perform two factor authentication.
                        result = await AcquireTokenInteractiveDeviceFlowAsync(app, scopes, parameters.ConnectionId, parameters.UserId, parameters.AuthenticationMethod, cts);

                        SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (interactive) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, result?.ExpiresOn);
                    }
                }
                else
                {
                    // If no existing 'account' is found, we request user to sign in interactively.
                    result = await AcquireTokenInteractiveDeviceFlowAsync(app, scopes, parameters.ConnectionId, parameters.UserId, parameters.AuthenticationMethod, cts);

                    SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | Acquired access token (interactive) for {0} auth mode. Expiry Time: {1}", parameters.AuthenticationMethod, result?.ExpiresOn);
                }
            }
            else
            {
                SqlClientEventSource.Log.TryTraceEvent("AcquireTokenAsync | {0} authentication mode not supported by ActiveDirectoryAuthenticationProvider class.", parameters.AuthenticationMethod);
                throw SQL.UnsupportedAuthenticationSpecified(parameters.AuthenticationMethod);
            }

            return(new SqlAuthenticationToken(result.AccessToken, result.ExpiresOn));
        }