There are things we don’t want to make public in an ASP.NET project. The best example is the database credentials that we can often find in configuration files in plain text.

If your website is live it’s easy to end up in a situation where your web.config or some other file where you store sensitive data ends up indexed by a search engine (usually this is due to bad configuration) and then becomes extremely easy to find for someone that knows a few search tricks.

For example if you Google for filetype:config connectionString password , there are many examples of people who inadvertently made their credentials public. A similar search on Github will yield similar results.

If you are thinking that this type of thing will never happen to you, maybe you are right, but ending up in a situation where you’ve exposed your credentials is easier than you think. For example, during development it’s very easy to check-in code to your company’s private repo with your credentials by mistake. Especially at the beginning of a new project where you are only trying things out and you might use your own username and password to connect to a local database. Because almost everyone uses the same password in more than one place, making it available to all your coworkers is not a good idea.

Thankfully ASP.NET Core introduced a new way of handling configuration information. One where your private information and your code don’t live together, and that’s a good thing.

ConfigurationBuilder

ASP.NET Core introduced a new configuration API that enables having several sources of “configuration values”. A configuration value is nothing more than a name/value pair.

Here’s an example where we are reading a list of name/value pairs from a file named appsettings.json:

IConfiguration configuration = new ConfigurationBuilder() .SetBasePath(thePathToWhereAppSettingsIs) .AddJsonFile("appsettings.json") .Build(); string connectionString = configuration["connectionString"];

When you are using the configuration API in an ASP.NET Core project, namely in the Startup.cs file you’ll have access to an instance of IHostingEnvironment which has a property named ContentRoot . You can use that as your BasePath.

This new configuration API has some other neat features, for example:

string connectionString = configuration["database:connectionString"];

Will return the value stored in connectionString under database:

{ database:{ connectionString: "The connectionString" } }

Even though this is an improvement over the old static ConfigurationManager class, it still doesn’t solve the problem of having secrets in files that need to be checked in to source control.

That’s where User Secrets comes in.

User Secrets

One feature of the new configuration API is that you can have several sources of name/value pairs with different priorities. For example you can create an IConfiguration instance that first reads values from environment variables and if they don’t exists tries appsettings.json:

IConfiguration configuration = new ConfigurationBuilder() .SetBasePath(thePathToWhereAppSettingsIs) .AddJsonFile("appsettings.json") .AddEnvironmentVariables() .Build();

The last “source” being added is the one that has the highest priority, in this case it’s the environment variables.

User Secrets is one of these “sources” and it works with a tool that allows you to manage secrets through the command line.

To start using User Secrets first install the Nuget package that enables adding user secrets as a source in ConfigurationBuilder:

dotnet add package Microsoft.Extensions.Configuration.UserSecrets

Now you’ll have to edit your .csproj file to enable the command line tool.

First add a value for UserSecretsId in a PropertyGroup:

<PropertyGroup> <TargetFramework>netcoreapp1.1</TargetFramework> <UserSecretsId>AUniqueString</UserSecretsId> </PropertyGroup>

Here I’ve added it in the same PropertyGroup as the one that defines the TargetFramework , but you can create a new PropertyGroup and add it there if you wish.

Another change we have to do is to add the reference for the command line tool we’ll be using to generate secrets. Inside an ItemGroup add:

<ItemGroup> <DotNetCliToolReference Include="Microsoft.Extensions.SecretManager.Tools" Version="1.0.0-msbuild3-final"/> </ItemGroup>

Again, you can create a new ItemGroup or add the DotNetCliToolReference statement to the existing one.

Unfortunately I don’t know of any other way to enable user secrets in the command line (or any other dotnet cli tool) that doesn’t involve manually editing the .csproj file. You also have to memorize the version number. I believe this is the case even if you are using the full version of Visual Studio (I’m using Visual Studio Code).

After these changes, and making sure you have restored all the Nuget packages you can now write in the command line:

dotnet user-secrets

And you should see a help screen about how to use User Secrets.

Before we go into how to create user secrets, let’s first enable it in ConfigurationBuilder:

IConfiguration configuration = new ConfigurationBuilder() .SetBasePath(thePathToWhereAppSettingsIs) .AddJsonFile("appsettions.json") .AddUserSecrets<Startup>() .AddEnvironmentVariables() .Build()

The important part that was added is .AddUserSecrets<Startup>() . Startup is your startup class in ASP.NET Core, or any other class in the assembly that defines the value of UserSecretsId .

Finally, to create a user secret, for example a “connectionString”, write in the command line:

$ dotnet user-secrets set connectionString "the connection string"

To remove it:

$ dotnet user-secrets remove connectionString

To list all secrets:

$ dotnet user-secrets list

To clear all:

$ dotnet user-secrets clear

The user secrets are saved as a json file located in %APPDATA%\microsoft\UserSecrets\<userSecretsId>\secrets.json in Windows, ~/.microsoft/usersecrets/<userSecretsId>/secrets.json on Linux and Mac. This file is not encrypted, and for this reason there’s a warning in the documentation saying that you should not use it in production. Same applies to environment variables.

Even though this is the case, the only recommended “production-ready” solution is Microsoft Azure Key Vault. If you don’t use Azure, or don’t want to pay for Azure Key Vault you’ll either have to encrypt the secrets yourself or find another solution (currently I don’t know of any that I can recommend). There’s also a mention right at the end of the documentation that hints that user secrets might be encrypted in the future.

Just a last quick note about using user secrets. If you change the value of a user secret while the project that uses it is running, that change will only be visible when you restart the project.

It's only fair to share... Linkedin