Secure Your .NET 5 Blazor Server App with MFA

Blazor is an exciting new technology from Microsoft that will allow developers to bring C# to clients. Server and client components are written in the same language and can be used and re-used interchangeably. Blazor comes in two flavors, server and client apps. In this tutorial you will be working with Server Blazor apps, where the C# code is run on the server, and messages are exchanged using SignalR.

This is a companion discussion topic for the original entry at

I’m doing WASM 6.0.1 and I see my razor pages will honor a policy name like this…
@attribute [Authorize(Policy = "SomePolicyName")]

when I have a simple policy like this…

	options.AddPolicy("IsTrue", policy => policy
		.RequireAssertion(_ => true)

And yet this fails…

	options.AddPolicy("IsJohnDoe", policy => policy

Instead I have to write something like this…

	options.AddPolicy("IsJohnDoe", policy => policy
		.RequireAssertion(_ =>
			var username = _.User.Claims.SingleOrDefault(_ => _.Type == "preferred_username")?.Value;
			return "".Equals(username, StringComparison.OrdinalIgnoreCase);

And while this works…

	options.AddPolicy("HasGroups", policy => policy

this will fail…

	options.AddPolicy("HasGroup1", policy => policy
		.RequireClaim("MyGroupArray", "Group1")

and so I do this…

	options.AddPolicy("HasGroup1", policy => policy
		.CustomRequireClaim("MyGroupArray", "Group1")

with this extension method…

	public static class CustomAuthorizationPolicyBuilder
		static Func<Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext, string, string[], bool> _CustomRequireClaim
			= (ctx, claimName, allowedValues) => {
				var jsonArray = ctx.User.Claims.SingleOrDefault(_ => _.Type == claimName).Value;
				var userValues = JsonSerializer.Deserialize<string[]>(jsonArray).ToHashSet<string>(StringComparer.OrdinalIgnoreCase);
				return allowedValues.Any(allowedValue => userValues.Contains(allowedValue));

		public static Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder CustomRequireClaim(
			this Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder me,
			string claimName,
			params string[] allowedValues
			=> me.RequireAssertion(ctx => _CustomRequireClaim(ctx, claimName, allowedValues));

I didn’t have any of these challenges while securing WebAPI endpoints with policies.
Perhaps I should have installed the Okta.AspNetCore nuget package in my WASM project (I have that in my WebAPI)?