How to store app secrets for your ASP .NET Core project
Tags: dotnet, webdev, beginners, tutorial
Published: 2020-07-16 20:47:45

---
title: How to store app secrets for your ASP .NET Core project
published: true
description: Your secrets don't belong in the source code. Manage them using a command-line tool when developing.
tags: dotnet, webdev, beginners, tutorial
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/3rzf9m5spu6h18dgg96h.jpg
canonical_url: https://techcommunity.microsoft.com/t5/apps-on-azure/how-to-store-app-secrets-for-your-asp-net-core-project/ba-p/1527531
---

Follow me on [Twitter](https://twitter.com/chris_noring), happy to take your suggestions on topics or improvements /Chris

> This article is for you that is either completely new to ASP .NET Core or is currently storing your secrets in config files that you may or may not check in by mistake. Keep secrets separate, store them using the Secret management tool in dev mode, and look into services like Azure KeyVault for production.

Let's talk about app secrets and configuration and why we need a tool to manage it. There are a few reasons why this is something that needs to be managed and preferably by a tool:

- **Separate config/secrets from source code, your configuration is sensitive**, configuration strings may contain passwords or API keys or other secrets. Having this information exposed may leave your system vulnerable. You want to avoid storing any of the data in source code as your source code will most likely end up in a repo on GitHub or a similar place. Even though it's a private repo it may be exposed. Better to store this elsewhere.
- **Values are different in different environments**, the values you use for DEV, Staging, and Prod are hopefully different when it comes to connecting to a database or API. You need to acknowledge what's different so you can separate this out as configuration that needs to be replaced per environment
- **OSs are different**, you might think that it's enough to make all secrets into environment variables, and you are done. However, you might have so much configuration that you need to organize it in a hierarchy like so `api:<apitype>:<apikey>`. One problem though, `:` isn't supported on all OSs but other characters are like `___`. The point being is that you want an abstraction layer to organize your secrets/config.

## References

- [Secret management](https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-3.1&tabs=linux&wt.mc_id=devto-blog-chnoring)
- [Configuration API](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1&wt.mc_id=devto-blog-chnoring)
- [Sign up for a free Azure account](https://azure.microsoft.com/en-us/free/?wt.mc_id=devto-blog-chnoring)
- [Deploy Web Api to Azure](https://docs.microsoft.com/en-us/aspnet/core/tutorials/publish-to-azure-webapp-using-vscode?view=aspnetcore-3.1&wt.mc_id=devto-blog-chnoring)
- [Dependency Injection](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-3.1&wt.mc_id=devto-blog-chnoring)
- [Original post at Microsoft Tech Community blog](https://techcommunity.microsoft.com/t5/apps-on-azure/how-to-store-app-secrets-for-your-asp-net-core-project/ba-p/1527531?wt.mc_id=devto-blog-chnoring)

## Secret manager tool

When you install .NET Core you get a built-in tool to help you managing configuration and secrets. It addresses a lot of the concerns that we covered in the last section. However, there are some things you should know before we continue:

- **The tool is great for local dev**, The secret manager tool is great for local development but that's where it should stay.
- **Environment variables are not safe**, your machine might be compromised and Environment Variables are plain text and not encrypted. So even though it's tempting to rely on Environment Variables and store those in AppSetting in Azure you want to look into more safe ways of handling secrets like [KeyVault](https://docs.microsoft.com/en-us/azure/key-vault/general/tutorial-net-create-vault-azure-web-app).

The secret manager tool is a command-line tool that stores your secrets in a JSON file. Once you **initialize** the tool for a specific project it generates a `Secrets Id` and creates a JSON file in a place that's OS-dependent:

For MAC

```bash
~/.microsoft/usersecrets/<user_secrets_id>/secrets.json
```

For Windows

```bash
%APPDATA%\Microsoft\UserSecrets\<user_secrets_id>\secrets.json
```

The idea is that you *initialize* in the root of an ASP .NET Core project and the `Secrets Id` is placed in the project file. It then works with .NET Core and some provider code to make it easy to retrieve and store secrets through code.

## DEMO manage secrets

Let's try to cover the following areas:

- **Initialization**, this is how you generate an id and creates a file that will contain your secrets
- **Setting a value**, this is about setting a value, either from a terminal or from code. 
- **Accessing a value**, this can be done from both terminal and code.
- **Removing a value**, it's good to know how to remove a value when you no longer need it. It can be removed from both terminal and code.

### Initialization

1. First, create a .NET Core Web API project by typing the following command in the terminal:

   ```bash
   dotnet new webapi -o api --no-https
   cd api
   ```

1. Type the following command

   ```bash
   dotnet user-secrets init
   ```

   The terminal should give you an output like so:

   ```output
   Set UserSecretsId to '<secret-id>' for MSBuild project '/path/to/your/project/project.csproj'.
   ```

1. Open up your project file that ends in `.csproj`. Locate an entry under `PropertyGroup` looking like so:

   ```xml
   <UserSecretsId>secret-id</UserSecretsId>
   ```

   This `UserSecretsId` is how the secrets JSON file is connected to your app.

### Setting a value

Before we create a secret let's learn how to list the secrets we currently have (there should be none at this point). Type the following command in the terminal:

```bash
dotnet user-secrets list
```

You should get the following output:

```output
No secrets configured for this application.
```

Next, let's create a secret.

1. In the terminal type the following:

   ```bash
   dotnet user-secrets set "ApiKey" "12345"
   ```

1. List the content of the secret store again:

   ```bash
   dotnet user-secrets list
   ```

   Now you get the following output:

   ```output
   ApiKey = 12345
   ```

You can also create a namespace with secrets for when you want to group things that go together like:

```bash
ProductsUrl
ProductsApiKey
```

1. Type the following command to create *grouped* secrets:

   ```bash
   dotnet user-secrets set "Products:Url" "http://path/to/product/url"
   dotnet user-secrets set "Products:ApiKey" "123abc"
   ```

   You should get the following output:

   ```output
   Successfully saved Products:Url = http://path/to/product/url to the secret store.
   Successfully saved Products:ApiKey = 123abc to the secret store.
   ```

1. List your secrets content:

   ```bash
   Products:Url = http://path/to/product/url
   Products:ApiKey = 123abc
   ApiKey = 12345
   ```

   The above might look exactly like when you stored `ApiKey` but there is a difference when accessing. Let's try to access next.

### Accessing a value

The `Configuration` API will help us retrieve our secrets in source code. It's a powerful API that is capable of reading data from various sources like `appsettings.json`, environment variables, KeyVault, Command-line, and much more, with the help of dedicated providers that can be added at startup. It's worth stressing this API helps us only in development mode when it comes to reading secrets. The secret management tool is only meant for development so that works for us.

1. Open up `Startup.cs` and note how the constructor already inject it like so:

   ```csharp
   public Startup(IConfiguration configuration) {}
   ```

1. Locate `ConfigureServices()` method and add the following code to retrieve and display the secret:

   ```bash
   public void ConfigureServices(IServiceCollection services)
   {
      ApiKey = Configuration["Products:Url"];
      Console.WriteLine(ApiKey);
      services.AddControllers();
   }
   ```

1. Build and run your project by running the following commands:

   ```bash
   dotnet build && dotnet run
   ```

   You should get the following output at the top:

   ```bash
   http://path/to/product/url
   ```

   Great our secret is listed where it should be. What if we want to access these values from somewhere else other than `Startup.cs`, like from a controller or a service? Yea we can do that, by using the built-in dependency injection.

1. We are about to register a *singleton*, this is something we can use and inject it into a service or controller. The singleton will contain the API keys or other secrets we might need to access.

   1. Create a file `AppConfiguration.cs` and give it the following content:

      ```csharp
      namespace webapi_secret
      {
        public class AppConfiguration
        {
          public string ApiKey { get; set; }
          public Product Product { get; set; }
        }
      }
      ```

   1. Go back to `Startup.cs`, locate the `ConfigureServices()` method and add the following lines:

      ```csharp
      var config = new AppConfiguration();
      config.ApiKey = Configuration["Products:Url"];
      services.AddSingleton<AppConfiguration>(config);
      ```

      Now we have a singleton we can use anywhere :)

   1. Create a `ProductsController.cs` and ensure it looks like so:

      ```csharp
      using Microsoft.AspNetCore.Mvc;

      namespace webapi_secret.Controllers
      {
        [ApiController]
        [Route("[controller]")]
        public class ProductsController : ControllerBase
        {
          AppConfiguration _config;
          public ProductsController(AppConfiguration config)
          {
            this._config = config;
          }

          [HttpGet]
          public string Get()
          {
            return this._config.ApiKey;
          }
        }
      }
      ```

      Note how we *inject* `AppConfiguration` in the constructor and assign the instance `this._config = config;`. Note also how we construct a method `Get()` and return config key:

      ```csharp
      [HttpGet]
      public string Get()
      {
        return this._config.ApiKey;
      }
      ```

   1. Build and run this with the following command:

        ```bash
        dotnet build && dotnet run
        ```

   1. Navigate to `https://localhost:5001/products`

   Note, you can do it like this and have a configuration singleton that you use where you need it or you can create your services and register them to the DI container with the config passed through the constructor, like so:

1. Create a file `HttpService.cs` and give it the following content:

   ```csharp
   namespace webapi_secret
    {
      public class HttpService
      {
        private string _apiKey;
        public HttpService(string apiKey)
        {
          this._apiKey = apiKey;
        }

        public string ApiKey { get { return _apiKey; } }
      }
    }
   ```

1. Go to the file `Startup.cs` and locate the `ConfigureService()` method and the following line:

   ```csharp
   services.AddSingleton<HttpService>(new HttpService(Configuration["ApiKey"]));
   ```

   Now you can inject `HttpService` wherever you need it and know that it is configured with an API key.

Whether you want a configuration instance that you inject or if you want services created with the necessary keys is up to you.

> Wait, what those keys we created `Products::ApiKey`, all that talk of a namespace?

Yea we can deal with those in a very elegant way.

1. Create a file `ProductConfiguration.cs` and give it the following content:

   ```csharp
   public class ProductConfiguration
   {
     public string Url { get; set; }
     public string ApiKey { get; set; }
   }
   ```

1. Go to `Startup.cs` and add the following code to `ConfigureServices()`:

   ```csharp
   var productConfig = Configuration.GetSection("Products")
                          .Get<ProductConfiguration>();
   services.AddSingleton<ProductConfiguration>(productConfig);
   ```  

   The `GetSection()` method allows us to grab a namespace and then map everything on that namespace and map it into a type. This is great now we can have dedicated parts of our secrets mapped to dedicated configuration classes

   Just like with `AppConfiguration` we can inject this where we please. Update `ProductsController.cs` to look like so:

   ```csharp
   using Microsoft.AspNetCore.Mvc;

    namespace webapi_secret.Controllers
    {
      [ApiController]
      [Route("[controller]")]
      public class ProductsController : ControllerBase 
      {
        AppConfiguration _config;
        ProductConfiguration _productConfig;
        public ProductsController(AppConfiguration config, ProductConfiguration productConfiguration)
        {
          this._config = config;
          this._productConfig = productConfiguration;
        }

        [HttpGet]
        public string Get()
        {
          // return this._config.ApiKey;
          return this._productConfig.ApiKey;
        }
      }
    }
   ```
  
### Removing a value

Lastly, how do we clean up? Use the command `remove` and give it the name of the key, like so:

```bash
dotnet user-secrets remove "ApiKey"
```

There's also a `clear` command that removes all keys, be careful with that one though:

```bash
dotnet user-secret clear
```

## Summary

We discussed why it's a bad idea to have secrets in your source code, i.e you can check it in by mistake. Additionally, we talked about how the secret manager tool can help you while developing to keep track of your secrets. Then we showed how to *manage* secrets and thus covering:

- Adding secrets
- Reading secrets from command-line and from code
- Configure DI instances to be populated by secrets
- Remove secrets

I hope this was helpful.