Identity and Dynamic routing in Blazor

Identity and Dynamic routing in Blazor

08 May 2021

Blazor

Buy Me A Coffee

Welcome!

This is the debut of the Blazor category on my website and I start it by describing how to build a dynamic route based on current user identity.

The issue.

If you create a website like a photo gallery, file system, or internet shop, you need a dynamic route (controller/action), similar to CMS.

Example: User enters http://sample.com/root/folder1/folder2/folder3/file34 and then asked for the controller or action at runtime. To decide what to return with this path I need to know the user identity, because different users may have the same folder structure.

I am using the DynamicRouteValueTransformer routing endpoint feature, creating a TestControllerTransformer inherit DynamicRouteValueTransformer. But in TestControllerTransformer I was not able to obtain the User Identity, later on, the User Identity controller is available.

public class TestControllerTransformer : DynamicRouteValueTransformer
    {
        private ILogger<TestControllerTransformer> _logger;

        public TestControllerTransformer(ILogger<TestControllerTransformer> logger)
        {
            _logger = logger;
        }

        public override ValueTask<RouteValueDictionary> TransformAsync(HttpContext httpContext, RouteValueDictionary values)
        {
            if (httpContext.User.Identity.IsAuthenticated)
            {
                _logger.LogTrace("User Authenicated");
                var fileId = GetFileId(values, httpContext.User.FindFistValue(ClaimsType.NameIdentifier);

                values["controller"] = "Files";
                values["action"] = "GetFile";
            }
            else
            {
                _logger.LogTrace("User NOT Authenicated");
            }

            return new ValueTask<RouteValueDictionary>(values);
        }
    }

I was waiting for httpContext.User.Identity.IsAuthenticated to be true in TestControllerTransformer after login user, but I received false.

Solution

When I investigated the issue, I saw a lot of suggestions to inject IHttpContextAccessor, and it actually works, but not in Blazor. Blazor apps run outside of the context of the ASP.NET Core pipeline. The HttpContext isn't guaranteed to be available within the IHttpContextAccessor, nor is it guaranteed to be holding the context that started the Blazor app. You can read more on Microsoft Docs.

The solution for Blazor (and it works for ASP.NET Core as well) is much easier.

The main issue was in the configuration. You have to understand how ASP.NET Core middlewares work and the responsibility of each of them. You can read more on Microsoft Docs.

So the default order looks like this:

Middleware pipeline

As you can see the Routing middleware executes before Authentication middleware, where actually User sets. So to fix the issue you just need to put Authentication middleware before Routing middleware:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
		{
			if (env.IsDevelopment())
			{
				app.UseDeveloperExceptionPage();
			}
			else
			{
				app.UseExceptionHandler("/Error");
				app.UseHsts();
			}

			app.UseHttpsRedirection();
			app.UseStaticFiles();

			app.UseAuthentication();
			app.UseRouting();
			app.UseAuthorization();

			app.UseEndpoints(endpoints =>
			{
				endpoints.MapControllers();
				endpoints.MapBlazorHub();
				endpoints.MapFallbackToPage("/_Host");
			});
		}

The issue source: link

Buy Me A Coffee

Related:

Microsoft Identity Platform Authentication in Blazor Web Application

Adding Microsoft Entra ID B2C in .NET 8 Blazor WebApp.

An unhandled error has occurred. Reload

🗙