November 8, 2021

PBKDF2 Tool in Blazor

I first heard about Blazor a couple of years ago, and it sounded very interesting, especially the part about running .NET via WebAssembly on the browser. Blazor seems to have matured enough now and it's officially part of ASP.NET. Recently, my team needed a tool for PBKDF2 as we integrate with Keycloak, so I tried writing it in Blazor. Here are some of my thoughts on it so far.

You can find the full source in my GitHub repo.

C#

It's actually very refreshing to use just one language – C# (probably my favorite language) – for both the frontend and the backend, in a modern SPA-like paradigm, not WebForms with full page PostBacks like the good old days...? Of course, you're still using HTML and CSS (with support for SCSS and scoped CSS) for the "view" template. You can also call JavaScript via interop, which will be inevitable if you want to use built-in browser functionality such as alert().

WebAssembly

Originally, I wanted to build it as a WebAssembly project so that I can host as a static site and all you'd need is the browser to run it, but sadly, the KeyDerivation library doesn't support WebAssembly platform, so I had to use the Blazor Server hosting model.

The server hosting model uses WebSockets, and the server-side runs the logic, so scalability would need to be considered if writing real business applications with it.

JSX..?

I suppose this is more of a Razor template feature than Blazor, but I like that it's similar to React's JSX. For example, being able to use code blocks within the template, such as using the if statement to wrap around a component. (Never liked how Angular templates handle logic, such as ngIf and ngFor directives, where they are added as element attributes.)

Unit Testing

There's no official Microsoft library for unit testing Blazor, but an open-source project called bUnit seems popular for writing unit tests for Blazor components.

Being able to test the difference between snapshots of a render seems useful, but might get too complicated if testing a large change. Component isolation and keeping them small would help.

You can write tests as a razor component, which means you can use razor syntax for the component-under-test. Visual Studio editor's auto-formatting support still needs some work though.

DisplayName Validation Message Bug?

Perhaps there's a bug when using the DisplayName attribute on the InputText component. For both ValidationSummary and ValidationMessage<T>, setting the DisplayName attribute seems to have no effect on InputText. It just displays the property name, not the DisplayName.

InputNumber works fine though, as can be seen here:

...
validationErrorMessage = string.Format(ParsingErrorMessage, DisplayName ?? FieldIdentifier.FieldName);
...

Maybe it's not supported yet?

It does work if I use the [Display] attribute on the model itself, but nowadays I feel that's not the right place, since it breaks the layer boundary. If the model is serving as a "View Model", then it'd be okay.

Finding out how the [Required] attribute's message validation works was taking longer than I'd like, so I stopped at ValidationAttributeAdapterOfTAttribute.cs. If I can get to spend more time on this and can confirm it, perhaps I'll file a bug report. By the way, did you know that the ASP.NET Core solution has 480 projects? Took a very long time to load it on VS2019...

Dotnet Watch Hot Reload

It sometimes forces a manual reload, but I like the experience. VS2022 will have this feature, and will remain in .NET 6 CLI..

Misc.

RNGCryptoServiceProvider

RNGCryptoServiceProvider is obsolete in .Net 6 Preview. Be aware that a lot of examples on the internet still use this class for generating the salt.

JavaScript PBKDF2

There's actually a JavaScript library for handling PBKDF2, so yes, I could've just done it all in CodeSandbox:

import "./styles.css";
import { pbkdf2Sync } from "pbkdf2";

export default function App() {
  let textToHash = "foobar";
  let salt = atob("b9Txti27LxJWX9BejPSaAQ==");
  let result = pbkdf2Sync(textToHash, salt, 27500, 64, "sha256").toString(
    "base64"
  );

  return <div className="App">Hash: {result}</div>;
}

November 5, 2021

Azure Artifacts and NuGet

Recently, I had to design a solution where a common module needed to be shared between multiple projects. One way to do this in .NET is NuGet. Since our packages are commercial and proprietary, I can't publish them to nuget.org, so here are some notes on using Azure Artifacts with NuGet.

Building and Publishing NuGet Packages to Azure Artifacts

The first step is getting the feed information from Azure Artifacts for NuGet, then creating a nuget.config file in the solution and adding the feed information:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
	<packageSources>
		<clear />
		<add key="[Custom Name]" value="https://pkgs.dev.azure.com/[Organization Name]/_packaging/[Feed Name]/nuget/v3/index.json" />
		<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
	</packageSources>
</configuration>

Note that I also had to add the main nuget.org feed.

For building and publishing, here's a sample azure-pipelines.yml:

trigger:
- master

pr:
- master

pool:
  vmImage: ubuntu-latest

steps:

- task: NuGetAuthenticate@0

- script: |
    SHORT_COMMIT_HASH=${BUILD_SOURCEVERSION:0:7}

    dotnet pack [Path to .csproj] -p:PackageVersion=$(PACKAGE_MAJOR_VERSION).$(PACKAGE_MINOR_VERSION).$(Build.BuildId)+$SHORT_COMMIT_HASH -p:Version=$(PACKAGE_MAJOR_VERSION).$(PACKAGE_MINOR_VERSION).$(Build.BuildId).0
  displayName: 'Dotnet Pack'

- script: |
    dotnet nuget push --source "[Custom Name]" --api-key notused [Path to .nupkg from above, e.g., ./src/ProjectName/bin/Debug/ProjectName.$(PACKAGE_MAJOR_VERSION).$(PACKAGE_MINOR_VERSION).$(Build.BuildId).nupkg]
  displayName: 'Dotnet Nuget Push'
  condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'))

For credentials, all you need is the NuGetAuthenticate task for Azure Pipelines. If your Azure Artifacts are in the same organization as the pipeline, then no parameters are needed. Note that using a PAT (Personal Access Token) doesn't seem to work on Azure Pipelines for Azure Artifacts.

I'm using the dotnet CLI to both pack and publish the project as a NuGet package, and using some custom pipeline variables to set the version number.

It's good to know that NuGet supports SemVer 2.0. One feature from SemVer 2.0 that I'm using is the metadata - stamping the git hash on it. Note that you need to be on NuGet 4.3.0+ to use SemVer 2.0. With .NET 6 coming out soon, NuGet version will be 6.0 as well. Traditionally Microsoft's versioning has 4 parts, so for now I'm keeping the regular version's last part as 0.

Consuming NuGet Packages from Azure Artifacts

To use the NuGet package that we've published, add the feed to the client project's nuget.config, creating the file if necessary. Then in Visual Studio you should be able to browse the feed and install the package. You might be prompted to sign-in to Azure DevOps.

For pipelines, if it's an Azure DevOps pipeline, then just add the NuGetAuthenticate task before your build step and you should be good to go. In my case I also had to consume it from other build environments such as Bitbucket pipelines and Jenkins.

Here's a snippet of bitbucket-pipelines.yml for consuming the package and building a client project:

image: mcr.microsoft.com/dotnet/core/sdk:3.1
...
...
  - dotnet nuget update source [Custom Name] --source https://pkgs.dev.azure.com/[Organization Name]/_packaging/[Feed Name]/nuget/v3/index.json -u notused -p $AZURE_ARTIFACTS_PAT --store-password-in-clear-text
  - dotnet restore
  - dotnet build [Project Name] --no-restore

The $AZURE_ARTIFACTS_PAT is a custom pipeline variable that I've created to store the PAT from Azure DevOps, which in my case only has a read-only permission to the Packaging scope.

You might've noticed --store-password-in-clear-text. I couldn't figure out where NuGet stores the password. It looks like it should update the nuget.config with the specified password, but it is not there after the update or even the add command. On Windows, it's not in the Credential Manager either.

If you don't specify it, you get the following error, running on mcr.microsoft.com/dotnet/core/sdk:3.1:

error: Password encryption is not supported on .NET Core for this platform. The following feed try to use an encrypted password: '[Feed Name]'. You can use a clear text password as a workaround.
error:   Encryption is not supported on non-Windows platforms.

Debug and Release Versions

Since this package will be used by just our teams, it would be nice if both the Debug version and the Release version were included in the NuGet package, so that the team members can debug through the code, if needed, while using the package. It seems such a feature is not supported and there's an open issue for it.

Looks like you can include symbols.

Multiple DLLs

Related to above, as I was developing the library, I wanted to organize it using multiple projects – e.g., one main project that exposes the library's public APIs and other supporting projects that are referenced from the main project (via project-references), but it seems NuGet is designed to contain only a single DLL. There are work-arounds.

Note that any other NuGet packages that you're referencing from your project, need to come from a NuGet source that will be accessible by the client.