During project development, we will inevitable came across secrets such as API Key, connection string or passwords when integrating with 3rd-party services. Here are a few common ways to manage them.
No management
The simplest way is to place the secrets directly in the source code. This can extremely useful for some quick and dirty stuffs but is never a good idea for any serious project.
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer("Server=DESKTOP-AB4CDEF;Database=MyDb;Trusted_Connection=True;TrustServerCertificate=True;"));Using JSON
A custom JSON configuration file can be used for separating the configurables from the source code. It is by no means a great place to store sensitive information too, but a great start to introduce environment specific variables.
First, need to install the Microsoft.Extensions.Configuration.Abstractions package.
dotnet add package Microsoft.Extensions.Configuration.Abstractions --version 9.0.0Then, create a file named settings.json or appsettings.json in the project with some values in it.
{
"ConnectionStrings": {
"Default": "Server=DESKTOP-AB4CDEF;Database=MyDb;Trusted_Connection=True;TrustServerCertificate=True;"
}
}The files need to be registered with AddJsonFile method to load it's values.
builder.Configuration
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{environment.EnvironmentName}.json");There are a couple ways to retrieve a single value from the custom JSON file. The example below shows three ways to retrieve the default connection string from the file.
var connectionString = builder.Configuration["ConnectionStrings:Default"];
// or
var connectionString = builder.Configuration.GetSection("ConnectionString:Default").Value;
// or
var connectionString = builder.Configuration.GetConnectionString("Default");Options Pattern
Multiple values inside the JSON file can be grouped logically in a single object. This promotes readability and cohesion to the values.
For example, given this is a class named MySettings representing some variables required by the application.
public class MySettings
{
public string ConnectionString { get; set; }
public int TimeoutInSeconds { get; set; }
}We can add the following section in the appsettings.json that mirrors the MySettings class.
{
"mySettings": {
"connectionString": "My secure connection string;",
"timeoutInSeconds": 10
}
}After that, we can copy the values into the MySettings object as follows.
var mySettings = new MySettings();
builder.Configuration.GetSection("MySettings").Bind(mySettings);
// or
var mySettings = builder.Configuration.GetSection(nameof(MySettings)).Get<MySettings>();Environment Variables
Environment variables are the variables that exists in the user or machine level outside the project. The .NET Configuration will also read the environment variables into memory during instantiation.
In PowerShell, can retrieve the list of available environments variable.
Get-ChildItem env:* | Sort-Object nameA temporary environment variable can be created as follows.
$env:MYENV=foobarWhen running the .NET project with the same PowerShell session, it is also able to pick up within the app.
var myEnv = builder.Configuration.GetSection("MYENV").Value; // foobarAnother way is to specify in the environmentVariables section of the target profile in the launchSettings.json. This is helpful for AspNetCore Mvc or WebApi projects that uses the launchSettings.json file to launch the project.
{
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:7201;http://localhost:5029",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"MYENV": "foobar"
}
}
}Command Line Arguments
Command line arguments is another way that Configuration will be used.
dotnet run --MYENV foobarWe can provide the command line arguments in the launchSettings.json as well.
{
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:7201;http://localhost:5029",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"commandLineArgs": "--MYENV=\"hello\""
}
}The value can be retrieved from the GetSection method inside the application code as follows.
var myEnv = builder.Configuration.GetSection("MYENV")?.Value; // helloUser Secrets
User secrets takes another approach for storing the data outside the repository, that will not be checked into source control.
First, go to the project directory.
dotnet user-secrets initThis will generate a unique Id for the project and added into the .csproj file as shown below.
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>5bd4c643-9153-4772-aff7-5f92ccbd5739</UserSecretsId>
</PropertyGroup>
</Project>The value can be set with the following syntax.
dotnet user-secrets set "ConnectionString:Default" foobarAll user secrets can be retrieved with the list command.
dotnet user-secrets listThe data is stored in the C:\Users\<username>\AppData\Roaming\Microsoft\UserSecrets\<guid>\secrets.json file, where the <guid> is a directory name that should match the ones designated in the .csproj file.
DotEnv
This is a popular way of managing secrets in the JavaScript projects, where the secrets are placed in a file called .env that is ignored from source control. However, the dummy version of the file, commonly called as .env.example is kept in the source control for users who wants to setup the local environment for reference.
ConnectionStrings__Default="Server=DESKTOP-AB4CDEF;Database=MyDb;Trusted_Connection=True;TrustServerCertificate=True;"The example file should redact any sensitive information before being committed.
ConnectionStrings__Default="<connection-string>"Install the package named dotenv.net.
dotnet add package dotenv.netThen, load the contents in the .env file into the builder.Configurations object by running the following code.
using dotenv.net;
DotEnv.Load();Order of Precedence
For .NET Configurations, different configuration source have different priority, where values defined in the lower priority source gets overwritten by the ones in the higher priority.
| Source | Priority | |
|---|---|---|
| 1. | JSON file | Lowest |
| 2. | User secrets | Low |
| 3. | Environment variables | Medium |
| 4. | .env load | High |
| 5. | Command line arguments | Highest |