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.0
Then, 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 name
A temporary environment variable can be created as follows.
$env:MYENV=foobar
When 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; // foobar
Another 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 foobar
We 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; // hello
User 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 init
This 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" foobar
All user secrets can be retrieved with the list
command.
dotnet user-secrets list
The 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.net
Then, 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 |