Author : HASSAN MD TAREQ

App Secrets in aspnet core

  • Caution:
    • Passwords or other sensitive data should not be committed to source control
    • Production secrets shouldn’t be used for development or test
    • Secrets shouldn’t be deployed with the app
  • Solution: Secret Manager
    • For storing and retrieving sensitive data during development of an ASP.NET Core app on a development machine
    • Never store passwords or other sensitive data in source code (hardcoded, appsettings.json)
  • Development Time
    • Secret Manager create secrets.json file that would not be committed into VCS
    • Configurations in secrets.json is available via IConfiguration Configuration (DI) Production:
    • Use Secrets should be made available in the production environment through a controlled means
    • Use Environment variables
    • Or use Azure Key Vault

Social login providers assign Application Id and Application Secret tokens during the registration process. The exact token names vary by provider. These tokens represent the credentials your app uses to access their API. The tokens constitute the “secrets” that can be linked to your app configuration with the help of Secret Manager. Secret Manager is a more secure alternative to storing the tokens in a configuration file, such as appsettings.json.

User Secrets in Development

  • Used for development (for production, use Azure KeyVault)
  • Right Click on the project > Manage User Secrets
  • VS will create and open secrets.json
  • Right Click on the project > Edit Project Files > UserSecretsId section should be added

secrets.json

{
  "Foo": {
    "ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=Movie-1;Trusted_Connection=True;MultipleActiveResultSets=true",
    "ApiKey": "12345"
  }
}

Accessing User Secrets

  • User secrets configuration source is automatically added in development mode when the project calls CreateDefaultBuilder to initialize a new instance of the host with preconfigured defaults
  • CreateDefaultBuilder calls AddUserSecrets when the EnvironmentName is Development
  • When CreateDefaultBuilder isn’t called, add the user secrets configuration source explicitly by calling AddUserSecrets

User secrets can be retrieved via the Configuration API:

public class Startup
{
    public IConfiguration Configuration { get; }
	
    private string _apiKey = null;

    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }


    public void ConfigureServices(IServiceCollection services)
    {
        _apiKey = Configuration["Foo:ApiKey"];
    }

    public void Configure(IApplicationBuilder app)
    {
        // use _apiKey here if needed
    }
}

See

Configuring Azure App Services for Azure KeyVault

  • Managed Identity will be usued to access KeyVault
  • What will happen:
    • When Managed Identity is activated, Azure App Services is registered in Azure AD
    • When deployed app (deployed to Azure App Services) tries to access Azure KeyVault, KeyVault will ask for authentication
    • Then, Azure App Services will use Azure AD credential and therefore deployed app will be able to get secrets from Azure KeyVault

Following setting is for activating Managed Identity in Azure App Services

Azure App Services Configuration for Azure KeyVault with Managed Identity Step 1

Azure App Services Configuration for Azure KeyVault with Managed Identity Step 2

Azure App Services Configuration for Azure KeyVault with Managed Identity Step 3

Using Azure KeyVault in Production

When using (Azure App Services) managed identity for KeyVault, then all you need to do:

  • Install Nuget package: Microsoft.Extensions.Configuration.AzureKeyVault
  • Configure in Program class

Then app will use secrets:

  • Locally (debuging in Visual Studio): from User Secrets
  • Azure (deployed to App Services): from KeyVault

Notes:

  • Secret identifier key should be same for both User Secrets(locally) and KeyVault(Azure)

Installation

Install-Package Microsoft.Extensions.Configuration.AzureKeyVault

appsettings.json

{
  "AzureKeyVaultName": "hovermind-blazor-demo-vault",
  
    "...": { ... },
}

Program.cs

public class Program
{
	public static void Main(string[] args)
	{
		CreateHostBuilder(args).Build().Run();
	}

	public static IHostBuilder CreateHostBuilder(string[] args) =>
		Host.CreateDefaultBuilder(args).ConfigureAppConfiguration((context, config) =>
		{
			if (context.HostingEnvironment.IsDevelopment())
			{ return; }

			var builtConfig = config.Build();

			config.AddAzureKeyVault($"https://{builtConfig["AzureKeyVaultName"]}.vault.azure.net/");
		})
		.ConfigureWebHostDefaults(webBuilder =>
		{
			webBuilder.UseStartup<Startup>();
		});
}

The above configuration does following (behind the scene):

//Authentication
var tokenSvc = new AzureServiceTokenProvider();

//Key Vault Client
var keyVaultClient = new KeyVaultClient(new
KeyVaultClient.AuthenticationCallback(tokenSvc.KeyVaultTokenCallback));

//Add Configuration Provider
config.AddAzureKeyVault("https://your-vault-name.vault.azure.net/", keyVaultClient, new DefaultKeyVaultSecretManager());

Accessing KeyVault Secrets in Program class

  • appsettings.json will contain KeyVaultBaseUrl (value will be empty)
  • KeyVaultBaseUrl will be overriden by DevOps pipeline (pipeline will provide actual value)

appsettings.json

{
  "KeyVaultBaseUrl": "",
}

Program.cs

namespace Hovermind.HoverApp
{
    public class Program
    {

        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
             Host.CreateDefaultBuilder(args).ConfigureAppConfiguration((context, configBuilder) =>
             {
                 var Configuration = configBuilder.Build();

                 var isProduction = context.HostingEnvironment.IsProduction();
                 if (isProduction)
                 {
                     var keyVaultUrl = Configuration["KeyVaultBaseUrl"];
					 
                     configBuilder.AddAzureKeyVault(keyVaultUrl);

                     Configuration = configBuilder.Build();  // KeyVault secrets are added to Configuration
					 
                     var fooSecret = Configuration["FooSecretKey"];

                     if (!string.IsNullOrEmpty(fooSecret))
                     {
                        // Use fooSecret here
                     }
                 }
                 else
                 {
                    // Development-time settings
                 }
             })
             .ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup<Startup>())
             .UseSerilog();
    }
}

```

Testing Azure KeyVault using Azure CLI

  • Download and Insatll Azure CLI
  • Comment out return in Program.CreateHostBuilder() => in previous section of this page: if (context.HostingEnvironment.IsDevelopment()){ //return; } )
  • Run app => there should be exception
  • Open cmd (Search: cmd > open “Command Prompt”)
  • Execute: az --version to check Azure CLI is working
  • Execute: az login
    • Default browser should be open
    • Login to Azure using credentials (same credentials that used to create App Services Plan)
  • Run app => app should be able to get secrets from KeyVault