🤔 The Problem with Blazor WebAssembly
A while back, I wrote a Japanese article about how to manage API keys for error monitoring services like Raygun and Sentry in a Blazor WebAssembly app.
https://qiita.com/jsakamoto/items/e72bbf0681994307d07c
In that article, I touched on how to handle configuration values during development. Here is one passage I wrote:
I think it would be a good idea to exclude
appsettings.Development.jsonfrom version control using a.gitignorefile, but I will skip the details in this article. If you were using a project other than Blazor WebAssembly, User Secrets would be a convenient and reasonable option. Unfortunately, Blazor WebAssembly does not support User Secrets, so usingappsettings.Development.jsonseemed like the safest approach.
Yes, Blazor WebAssembly does not support User Secrets. And this turns out to be more of a problem than it first appears.
🔍 The Three-Layer Configuration Problem
In .NET apps, I think most developers want to manage configuration values in three layers, where each layer overrides the one before it:
- Release configuration: values for the production environment. Committed to source control and shared with everyone.
- Shared development configuration: values for the development environment, shared with all team members. Also committed to source control.
- Personal developer configuration: values specific to each developer's local environment. Not committed to source control.
In Blazor WebAssembly, appsettings.json (the first layer) and appsettings.Development.json (the second layer) cover the first two cases nicely.
The problem is the third layer.
In that earlier article, I mentioned excluding appsettings.Development.json from source control using .gitignore. But if you think about it, doing that removes the place where you would normally store shared development configuration (the second layer). Trying to keep both "shared team settings" and "personal settings" in a single file, and then excluding that file from source control, is a dead end.
In a regular ASP.NET Core project, User Secrets (via the dotnet user-secrets set command) can serve as this third layer. It lets each developer save personal settings locally without committing them to the repository. But since Blazor WebAssembly does not run on the server side, this mechanism simply was not available.
💡 I Thought, "Why Not Build It Myself?"
While writing that article, I had a sudden idea.
"What if I intercepted requests to the dev server and merged the User Secrets values into the response for appsettings.*.json files?"
But when I thought about how to implement that, it felt a bit wasteful to build just a dedicated tool for merging User Secrets. If I could build a general-purpose mechanism for extending the dev server, it could be useful for all kinds of purposes in the future too.
So I decided to take a two-part approach:
- Extensible Dev Server: a Blazor WebAssembly dev server that can be extended with custom middleware
- User Secrets extension: an extension that runs on the Extensible Dev Server and merges User Secrets values
The Extensible Dev Server is designed as a drop-in replacement for the standard Blazor WebAssembly dev server NuGet package (Microsoft.AspNetCore.Components.WebAssembly.DevServer). The mechanism for plugging in custom middleware is built on top of the ASP.NET Core Hosting Startup feature. Hosting Startup lets you inject middleware from a separately built assembly into an existing ASP.NET Core app, without touching the app itself. I wrote a separate article on dev.to explaining this feature in more detail:
Extending an ASP.NET Core App with External Middleware Using "Hosting Startup"
jsakamoto ・ Feb 14
I then implemented User Secrets merging as the first extension for the Extensible Dev Server and released both as NuGet packages.
🔧 What I Built
Blazor WebAssembly Extensible Dev Server
https://github.com/jsakamoto/Toolbelt.Blazor.WebAssembly.ExtensibleDevServer
User Secrets Extension for Blazor WebAssembly Extensible Dev Server
https://github.com/jsakamoto/Toolbelt.Blazor.WebAssembly.ExtensibleDevServer.UserSecretsExtension
⚡ How to Use It
🛠️ 1. Add the Packages
Add the following two packages to your Blazor WebAssembly project.
dotnet add package Toolbelt.Blazor.WebAssembly.ExtensibleDevServer
dotnet add package Toolbelt.Blazor.WebAssembly.ExtensibleDevServer.UserSecretsExtension
You no longer need the existing Microsoft.AspNetCore.Components.WebAssembly.DevServer package, so remove it.
dotnet remove package Microsoft.AspNetCore.Components.WebAssembly.DevServer
🛠️ 2. Set Up User Secrets
After that, set up User Secrets the same way you would in any other .NET project.
In Visual Studio, right-click the project and select "Manage User Secrets".
From the CLI, run these commands:
dotnet user-secrets init
dotnet user-secrets set "SomeSection:SomeKey" "my-custom-value"
🚀 3. Run the App
Then just run the app as usual with dotnet run or through Visual Studio. No extra code is needed, and there is nothing else to configure.
Your app's configuration will automatically include the values from User Secrets, merged in. It almost feels too easy 😅
🔰 How It Works
The Extensible Dev Server intercepts HTTP GET requests for appsettings.*.json files. The User Secrets extension handles those requests and does the following:
- Read the original
appsettings.*.jsonfrom thewwwrootfolder - Read the User Secrets configured for the project
- Merge them together and return the result as the HTTP response
The files on disk are never modified. Only the HTTP response content is changed.
The configuration priority order looks like this:
appsettings.json (lowest priority)
overridden by
appsettings.Development.json
overridden by
User Secrets (highest priority)
⚠️ User Secrets Values Are Not Actually Secret
Before using this package, please understand one important point.
When developing a Blazor WebAssembly app with this package applied, values stored in User Secrets are returned as plain text in the HTTP response to the browser. In other words, they are not actually secret.
This is probably why Microsoft has not added official support for User Secrets in Blazor WebAssembly. In a regular server-side .NET app, User Secrets stores values in a safe location separate from the compiled code. Applying that same idea to Blazor WebAssembly would cause confusion: the values would be fully visible in the browser despite the word "Secrets" in the name. I imagine Microsoft decided that supporting such misleading behavior was not the right call.
This package intentionally works around that limitation. It does not treat User Secrets as a vault for sensitive information. Instead, it borrows the User Secrets storage mechanism purely as a third configuration layer.
So please do not store passwords, authentication tokens, or any other information that must not be exposed. This package is intended for configuration values that are safe to be public, such as:
- Local dev server URLs
- Flags to enable personal or debug features
- API keys for error monitoring services like Raygun and Sentry (as discussed in the original article)
🎉 Summary
With this package, you can now manage personal developer configuration in Blazor WebAssembly using dotnet user-secrets, just like in any other .NET project.
For example, API keys for error monitoring services like Raygun and Sentry (the very problem from the original article) can now be stored in each developer's own User Secrets. You can keep shared team settings in appsettings.Development.json and separate each person's personal settings into User Secrets. This cleanly solves the problem of having "settings you want to share" and "settings you do not want to commit" living in the same file.
Just add the packages and you are ready to go. No extra code needed.
If you try it out, or if you have any questions or feedback, feel free to share them in the comments! 👇
❤️ Happy coding!
Top comments (0)