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); }
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); } }
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)); }
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); } }
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); }
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); }
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")); } }
public override TokenCredential CreateManagedIdentityCredential(DefaultAzureCredentialOptions options) => new ManagedIdentityCredential(new ManagedIdentityClient(Pipeline, options.ManagedIdentityClientId));
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}"); } } }
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; } }
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)); }
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); } }
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); }
/// <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)); }