Category Archives: Asp.NET Core

Viewing Server Variables in Asp.Net Core

In Asp.Net Core, you can view a list of data known as Server Variables. These are available when hosting inside IIS, and they give information about the request call and security.

You can view any one of these by adding some middleware to your Asp.Net Core project:

    app.Use(async (context, next) =>
    {
        var url = context.Features.Get<IServerVariablesFeature>()["URL"];

        await next.Invoke();
    }

You’ll need to be hosting inside IIS, and the code should go in the routing zone.

There is also a helper method for this, meaning you can do this instead:

    app.Use(async (context, next) =>
    {
        string a = context.GetServerVariable("URL");

        await next.Invoke();
    }

I then thought, why not just enumerate all the variables. You can see the list here.

I created a list of these:

    public static class ServerVariables
    {
        public static string[] Variables = new[]
        {
            "UNENCODED_URL",
            "URL",
            "QUERY_STRING",
            "REMOTE_ADDR",
            "ALL_HTTP",
            "AUTH_USER",
            "AUTH_TYPE",
            "REMOTE_USER",
            "SERVER_ADDR",
            "LOCAL_ADDR",
            "SERVER_PROTOCOL",
            "ALL_RAW",
            "REMOTE_HOST",
            "SERVER_SOFTWARE",
            "HTTP_RAW",
            "HTTP_COOKIE"
        };
    }

And then, in the middleware, just looped through them:

            app.Use(async (context, next) =>
            {                
                await context.Response.WriteAsync("<div>");
                foreach (var variable in ServerVariables.Variables)
                {
                    string result = context.GetServerVariable(variable);
                    await context.Response.WriteAsync($"<p>{variable}:    <b>{result}</b></p><br />");
                }

                await context.Response.WriteAsync("</div>");
                await next.Invoke();
            });

Remember that if you run this hosted in Kestrel, you’ll get nothing back.

References

https://docs.microsoft.com/en-us/previous-versions/iis/6.0-sdk/ms524602(v=vs.90)

Partial HTML

In Asp.Net Core server-side rendered HTML, you have the ability to use something called Partial Views. Essentially, these give you the ability to separate your View into, what some JS frameworks may call, components.

The way it works is incredibly simple. In your main page, you define some HTML, and reference the partial view – like so:

<div class="text-center">
    <div>
        <p>Merry Christmas - here's my partial view:</p>
    </div>
    <div>
        @await Html.PartialAsync("_MyPartial", Model.MyData)
    </div>
</div>

In your partial view, which is simply another cshtml file, you define the HTML fragment, along with the model; for example:

@model IEnumerable<MyDataType>

@foreach (var item in Model)
{
    <p>item.Data</p>
    <br />
}

References

https://docs.microsoft.com/en-us/aspnet/core/mvc/views/partial?view=aspnetcore-5.0

Tag Helper Not Working?

I recently had an issue where I was trying to use a tag helper that wasn’t correctly rendering. The HTML that I had (which was trying to call MyController.MyAction) was this:

            <form asp-controller="My" asp-action="MyAction" method="post">
                <input type="submit" value="Do Crazy Stuff!" />
            </form>

This wasn’t working. The first step when a tag helper isn’t working is always to check the rendered HTML. Tag Helpers should be converted to proper HTML – if they’re actually sat on the page as a tag helper then nothing will happen, because Chrome and Firefox don’t know what the hell a tag helper is.

The rendered HTML should look something like this

<form method="post" action="/My/MyAction">
    <input type="submit" value="Do Crazy Stuff!">
    <input name="__RequestVerificationToken" type="hidden" value="requestverficationtokendsldjlcjsoihcnwoncwoin">
</form>

However, I was seeing the folllowing:

<form asp-controller="My" asp-action="MyAction" method="post">
    <input type="submit" value="Do Crazy Stuff!">
</form>

Like I said – this is no good, as browsers don’t know what to do with “asp-controller” et al.

But Why?

Having done some research, it looks like (pretty much) the only reason for this is that you (or I) don’t have the following line in the _ViewImports file:

@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

Or, if you do have it, then for whatever reason, the _ViewImports file itself can’t be located.

For any Regular Readers* of this blog, you may have noticed this post regarding setting up a project to use feature folders.

One of the interesting features about _ViewImports, is that is applies to the folder that it’s in, and any sub folders; my folder structure looked like this:

- Feature1
-- Index.cshtml
-- Feature1.cshtml
- Feature2
-- Index.cshtml
-- Feature2.cshtml
- Views
-- _ViewImports
-- Shared

As a result, the _ViewImports will only apply inside the Views folder.

The Solution(s)

I can see three possibilities here. Each has benefits and drawbacks.

Create a Symbolic Link

One possibility is to Create a symbolic link between the _ViewImports in the views directory and the feature folder. Launch a command prompt (as admin):

C:\Project\src\MyApp\Feature1>mklink _ViewImports.cshtml ..\Views\_ViewImports.cshtml

symbolic link created for _ViewImports.cshtml <<===>> ..\Views\_ViewImports.cshtml

This works fine, but symbolic links can cause issues; especially if you’re copying code around (for example, with a source control system).

Just copy the file

Obviously, you could simply copy the _ViewImports file to the relevant directory. The benefit of this is that each folder only has the relevant imports for that folder; the downside being that you need to maintain all of these (in practice, the file shouldn’t be huge and complex, so this may be the easiest option).

Parent Directory

Finally, you could simply introduce a parent folder; for example:

- Features
-- _ViewImports
-- Feature1
--- Index.cshtml
--- Feature1.cshtml
-- Feature2
--- Index.cshtml
--- Feature2.cshtml
- Views
-- Shared

This is probably my favourite solution – it gives you the best of both worlds, but it does mean that you need to re-structure your solution.

References

https://docs.microsoft.com/en-us/aspnet/core/mvc/views/layout?view=aspnetcore-3.1

Notes

* I’m curious as to whether anyone does read this on a regular basis. Most of the traffic seems to come from Google. If you are a regular reader then please drop a comment.

Asp.Net Core Routing and Debugging

I recently came across an issue whereby an Asp.Net Core app was not behaving in the way I expected. In this particular case, I was getting strange errors, and began to suspect that the controller that I thought was reacting to my call, in fact, was not, and that the routing was to blame.

Having had a look around the internet, I came across some incredibly useful videos by Ryan Novak. One of the videos is linked at the end of this article, and I would encourage anyone working in web development using Microsoft technologies to watch it.

The particularly useful thing that I found in this was that, In Asp.Net Core 3.x and onwards, there is a clearly defined “Routing Zone” (Ryan’s terms – not mine). It falls here:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    …
    app.UseRouting();

    // Routing Zone

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

    // End

    app.UseEndpoints(endpoints =>
    …
}

This means that middleware and services that make use of routing should sit in this zone, but also that you can intercept the routing. For example:

    app.UseRouting();

    // Routing Zone

    app.Use(next => context =>
    {
        Console.WriteLine($"Found: {context.GetEndpoint()?.DisplayName}");
        return next(context);
    });

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

    // End

    app.UseEndpoints(endpoints =>

This little nugget will tell you which endpoint you’ve been directed to. There’s actually quite a lot more you can do here, too. Once you’ve got the endpoint, it has a wealth of information about the attributes, filters, and all sorts of information that makes working out why your app isn’t going where you expect much easier.

References

https://docs.microsoft.com/en-us/aspnet/core/mvc/views/overview?view=aspnetcore-3.1

https://www.youtube.com/watch?v=fSSPEM3e7yY

Change the Default Asp.Net Core Layout to Use Feature Folders

One of the irritating things about the Asp.Net Core default project is that the various parts of your system are arranged by type, as opposed to function. For example, if you’re working on the Accounts page, you’re likely going to want to change the view, the controller and, perhaps, the model; you are, however, unlikely to want to change the Sales Order controller as a result of your change: so why have the AccountsController and SalesOrderController in the same place, but away from the AccountsView?

If you create a new Asp.Net Core MVC Web App:

Then you’ll get a layout like this (in fact, exactly like this):

If your web app has two or three controllers, and maybe five or six views, then this works fine. When you start getting a larger, more complex app, you’ll find that you’re scrolling through your solution trying to find the SalesOrderController, or the AccountsView.

One way to alleviate this, is to re-organise your project to reference features in vertical slices. For example:

There’s not much to either of these, but let’s just put them in for the sake of completeness; the View:

@{
    ViewData["Title"] = "Wibble Page";
}

<div class="text-center">
    <h1 class="display-4">Wibble</h1>    
</div>

And the controller:

namespace WebApplication1.Wibble
{
    public class WibbleController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }
    }
}

The problem here is that the engine won’t know where to look for the views. We can change that by changing the ConfigureServices method in Startup.cs:

        public void ConfigureServices(IServiceCollection services)
        {
            . . . 
            services.Configure<RazorViewEngineOptions>(options =>
            {
                options.ViewLocationFormats.Clear();
                options.ViewLocationFormats.Add($"/Wibble/{{0}}{RazorViewEngine.ViewExtension}");
                options.ViewLocationFormats.Add($"/Views/Shared/{{0}}{RazorViewEngine.ViewExtension}");
            });
        }

Let’s also change the default controller action (in the Configure method of Startup.cs):

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            . . . 
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Wibble}/{action=Index}/{id?}");
            });
        }

There’s more than a few libraries that will handle this for you (here’s one by the late Scott Allen), but it’s always nice to be able to do such things manually before you resort to a third-party library.

Asp.Net Policy Authorization Based on a DB Field on the User Table

If you read this, you’ll learn how to create authorisation, based on a policy. Specifically, they use the example of the user’s age, and create a restriction to say that only users over the age of 21 can access a resource.

The age was something that they got from a claim, stored against the user. But what if your requirement is a little more complex? For example, what if you have a situation, such as a popular question and answer site that you may recognise, where you are trying to restrict access to a resource based on something that can change during the user session. In this post, I’m covering how you can follow the same policy structure, but force the program to go back to the DB to get details about the user, each time they try to access the restricted resource.

As a background, the particular use case here is that a user of my site can approve something, but only where they have a sufficient rating.

Basic Set-up

The first thing that you’ll need is a custom Authorization Handler. For my project, I’ve created a sub directory called Authorization (I’ve even spelled it wrong to be consistent):

The Authorization Handler inherits from an abstract class, which forces you to override a single method: HandleRequirementAsync. Essentially, you tell the handler about your requirement (we’ll come to the requirement next), and then it passes this back to you in the method; let’s have a look at some code:

    public class ApproverAuthHandler : AuthorizationHandler<ApproverAuthRequirement>
    {
        protected override Task HandleRequirementAsync(
            AuthorizationHandlerContext context, 
            ApproverAuthRequirement requirement)
        {

The Requirement is just a class to hold the relevant data that you need. In our example, the requirement would hold the user’s rating; here’s the requirement code:

    public class ApproverAuthRequirement : IAuthorizationRequirement
    {
        public int UserRating { get; set; }

        public ApproverAuthRequirement(int userRating)
        {
            UserRating = userRating;
        }
    }

IAuthorizationRequirment is what is known as a marker interface. To be honest, I hadn’t come across the term before, but I have used the pattern before. Essentially, this is an empty interface: its purpose it to allow you to pass the class type as a strongly typed interface; but in reality, the class can me anything.

Anyway, let’s get back to the Authorization Handler. Inside HandlerRequirementAsync, you can do anything you choose; should your check be successful, you call context.Succeed(requirement), otherwise, do nothing. In the above linked code, they check a claim against the requirement; we’re going to just get some information from the DB, and check that against the requirement:

        protected override Task HandleRequirementAsync(
            AuthorizationHandlerContext context, 
            ApproverAuthRequirement requirement)
        {
            int? userRating = _userService.GetSubjectRating(context.User);            
            if (!userRating.HasValue)
            {
                return Task.CompletedTask;
            }

            if (userRating >= requirement.UserRating)
            {
                context.Succeed(requirement);
            }

            return Task.CompletedTask;
        }

User service is simply a service that calls into a repository, and returns the user rating.

Unit Testing

All well and good, and this looks eminently testable. In fact, it is: you simply mock out the service, and the test looks like this:

            // Arrange
            var requirements = new[] { new ApproverAuthRequirement(100) };            
            var user = new ClaimsPrincipal(
                        new ClaimsIdentity(
                            new Claim[] { },
                            "Basic")
                        );

            var userService = Substitute.For<IUserService>();
            userService.GetSubjectRating(Arg.Any<IPrincipal>()).Returns(100);

            var context = new AuthorizationHandlerContext(requirements, user, null);
            var sut = new ApproverAuthHandler(userService);

            // Act
            await sut.HandleAsync(context);

            // Assert
            Assert.True(context.HasSucceeded);

No doubt, you could check for the exact service principal under test, although you’re testing the Authorization Handler, so I think this is sufficient.

Resource Based Handler

In this article, there is a similar process discussed; it warrants some further investigation on my part, but I don’t currently see any real difference between my implementation, and this abstraction (although, admittedly, this version looks easier to test).

References

https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-3.1

https://stackoverflow.com/questions/51272610/unit-test-authorizationhandler

https://docs.microsoft.com/en-us/aspnet/core/security/authorization/resourcebased?view=aspnetcore-3.1

My object won’t deserialise using System.Text.Json

I imagine this is a very common scenario, so I thought it worth documenting.

Let’s take the following controller method in a bog standard Asp.Net Core API:

        public async Task<DataContainer> Get()
        {
            var data = Enumerable.Range(1, 5).Select(index => new MyData
            {
                Value1 = index,
                Value2 = "some other value"
            })
            .ToList();

            return new DataContainer()
            {
                Data = data
            };
        }

Here’s a method that consumes it:

            var client = _clientFactory.CreateClient();
            var result = await client.GetAsync($"https://localhost:22222/endpoint");

            using var responseStream = await result.Content.ReadAsStreamAsync();
            var data = await JsonSerializer.DeserializeAsync<DataContainer>(responseStream);

So, you’ve expect this to work, right? It makes no difference what DataContainer looks like, because you’re serialising and deserialising using the same technology. In fact, it just returns a correctly structured object, but every value will be null.

I’ll tell you what does work, is this:

            var client = _clientFactory.CreateClient();
            var result = await client.GetAsync($"https://localhost:22222/endpoint");

            var responseString = await result.Content.ReadAsStringAsync();
            var data = Newtonsoft.Json.JsonConvert.DeserializeObject<DataContainer>(responseString);             

If you look at the JSON, it looks kosher enough (again, the exact structure is irrelevant). So, what’s the issue?

Interestingly, when the JSON is serialised, it is serialised into camelCase; so if you have a variable such as MyData, it will get serialised as myData. Newtonsoft deals with this, because it’s a common use case in .Net; however, System.Text.Json.Deserialize* assumes that the casing will match the object!

This is odd considering it changes it on the way out!!

Okay, so what’s the fix?

You just tell Deserialize* to use camelCase:

            var client = _clientFactory.CreateClient();
            var result = await client.GetAsync($"https://localhost:22222/endpoint");

            using var responseStream = await result.Content.ReadAsStreamAsync();

            var options = new JsonSerializerOptions()
            {
                PropertyNamingPolicy = JsonNamingPolicy.CamelCase
            };
            var data = await JsonSerializer.DeserializeAsync<DataContainer>(responseStream, options);

Annoying, eh?

References

https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to#table-of-differences-between-newtonsoftjson-and-systemtextjson

https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-how-to#use-camel-case-for-all-json-property-names

Using HttpClientFactory

In .Net Framework (prior to .Net Core), HttpClient was something of a pain. You essentially kept it around in a static variable. If you didn’t, and used too many of them, you could end up issues such as socket exhaustion.

I think most people got around this by implementing their own version of a HttpClientFactory… but now you don’t need to.

In this post, I’m covering the simplest possible use case inside an Asp.Net Core application. The articles linked at the bottom of this post go into much more detail.

Usage

The first step to using the HttpClientFactory is to add it to the IoC:

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews();
            services.AddHttpClient();
        }

Then, simply inject it into your controller; for example:


        public HomeController(ILogger<HomeController> logger,
            IHttpClientFactory clientFactory)
        {
            _logger = logger;
            _clientFactory = clientFactory;
        }

Finally, you request a client like this:

var client = _clientFactory.CreateClient();

Then you can use it as a normal HttpClient:

var result = await client.GetAsync($"https://localhost:22222/endpoint");

References

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-3.1

https://www.stevejgordon.co.uk/introduction-to-httpclientfactory-aspnetcore

Building a list with Asp.Net Core

I’ve recently been working with Asp.Net Core to build some functionality, involving building a list of values. Typically, with Asp.Net Core using Razor, you have a form that may look something like this:

@using (Html.BeginForm("MyAction", "ControllerName", FormMethod.Post)
{
    @Html.AntiForgeryToken()
    <div class="form-group">
        @Html.LabelFor(model => model.MyValue)
        @Html.TextBoxFor(model => model.MyValue)
    </div>

    <div class="form-group">
        <button type="submit">Submit</button>
    </div>

This works really well in 90% of cases, where you want the user to enter a value and submit. This is your average CRUD application; however, what happens if, for some reason, you need to manipulate one of these values? Let’s say, for example, that you want to submit a list of values.

For the sake of simplicity, we’ll say that the controller accepts a csv, but we want to build this up before submission. You can’t simply call a controller method for two reasons: the first is that the controller will reload the page; and the second that you don’t have anywhere to put the value on the server. If this was, say, a method to create an entry in the DB, the DB entry, by definition, couldn’t exist until after the submission.

This all means that you would need to build this list on the client.

A solution

Let’s start with a very simple little feature of Html Helpers – the hidden field:

@Html.HiddenFor(model => model.MyList)

This means that we can store the value being submitted to the user, without showing it to the user.

We’ll now need to display the data being added. An easy way to do this is a very simple table (you can load existing values into the table for edit scenarios):

    <div>
        <table id="listTable">
            <tbody>
                @if ((Model?.ValueList ?? null) != null)
                {
                    @foreach (var v in Model.ValueList)
                    {
                        <tr>
                            <td>@v</td>
                        </tr>
                    }
                }
            </tbody>
        </table>
    </div>    

Pay particular attention to the Table Id and the fact that the conditional check is inside the tbody tag. Now let’s allow the user to add a new piece of data:

    <div class="form-group">
        @Html.LabelFor(model => model.NewValue)
        @Html.TextBoxFor(model => model.NewValue)
    </div>
    <div>
        <button type="button" id="add-value">Add Value</button>
    </div>

Okay, so now we have a button and a field to add the value; we also have a method of displaying those values. We’ll need a little bit of Javascript (JQuery in this case) to append to our list:

@section Scripts {
        $('#add-value').click(() => {

            const hiddenList = $('#MyList');
            const newValue = $('#NewValue');

            if (!hiddenList.val()) {
                hiddenList.val(newValue.val());
            } else {
                hiddenList.val(hiddenList.val() + ',' + newValue.val());
            }
            
            $('#listTable > tbody:last-child').append('<tr><td>' + newValue.val() + '</td></tr>');            
        });

On the button click, we get the hidden list and the new value, we then simply add the new value to the list. Finally, we manipulate the table in order to display the new value. If you F12 the page, you’ll notice that the Razor engine replaces the Html Helpers with controls that have Ids the same as the fields that they are displaying (note that if the field name contains a “.”, for example: MyClass.MyField, the Id would be MyClass_MyField).

When you now submit this, you’ll see that the hidden field contains the correct list of values.

References

https://stackoverflow.com/questions/16174465/how-do-i-update-a-model-value-in-javascript-in-a-razor-view/16174926

https://stackoverflow.com/questions/171027/add-table-row-in-jquery

https://stackoverflow.com/questions/36317362/how-to-add-an-item-to-a-list-in-a-viewmodel-using-razor-and-net-core

Policy Server with Asp.Net Core

It took me a while to collate my notes for this; hopefully this will be the first in a vaguely related series on using Policy Server.

Policy Server is a product from a company called Solliance, allowing you to control authorisation within your application (as opposed to authentication – the same company produces a sister product, called Identity Server). The idea is that, the user is authenticated to determine they are who they say they are, but then authorised to use certain parts of the application, or to do certain things.

This post discusses setting up the open source version of Policy Server. There is a commercial version of this.

The ReadMe on the above GitHub page is quite extensive, however, when I was trying this out, I had some areas that I thought weren’t too clear. This post will explain that to me in the future, should I become unclear again (a common occurrence when you get to a certain age!)

It’s worth bearing in mind that authorisation and authentication need to be done on the server; whilst you may choose to also do that on the client, the code on the client is, well, on the client! If you have some Javascript code sat in the web browser that’s stopping me from seeing the defence plans, then that’s just not secure enough.

Installing and Configuring Policy Server

The first step here is to install the NuGet package:

Install-Package PolicyServer.Local

Next, add Policy Server in startup.cs:

    public void ConfigureServices(IServiceCollection services)
    {
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

            // In production, the React files will be served from this directory
            services.AddSpaStaticFiles(configuration =>
            {
                configuration.RootPath = "ClientApp/build";
            });

            services.AddPolicyServerClient(Configuration.GetSection("Policy"));
    }

Amazingly, at this stage, you’re basically done. The next stage is to add the rules into the appsettings.json. There is an example on the page above, but here’s a much simpler one:

  "Policy": {
    "roles": [
      {
        "name": "salesmanager",
        "subjects": [ "1" ]
      },
      {
        "name": "driver",
        "subjects": [ "2" ]
      }
    ],
    "permissions": [
      {
        "name": "ViewRoutes",
        "roles": [ "driver" ]
      },
      {
        "name": "CreateSalesOrder",
        "roles": [ "salesmanager" ]
      },
      {
        "name": "Timesheet",
        "roles": [ "salesmanager", "driver" ]
      }
    ]
  }

This is, in fact, the part that I didn’t feel was clear enough in the GitHub readme. What, exactly, are “subjects”, and how do I associate them to anything useful? I’ll come back to this shortly, but first, let’s see the rules that we have set up here:

The Sales Manager can create a sales order, and complete their timesheet.

The Driver can view routes and complete their timesheet.

The Sales Manager can not view routes; nor can the driver create a sales order.

Add Policy Server to a Controller

The next stage is to inject Policy Server into our controller. For the home controller, all you need to do is inject IPolicyServerRuntimeClient:

        private readonly IPolicyServerRuntimeClient _policyServerRuntimeClient;

        public HomeController(IPolicyServerRuntimeClient policyServerRuntimeClient)
        {
            _policyServerRuntimeClient = policyServerRuntimeClient;
        }

This can now be used anywhere in the controller. However, for testing purposes, let’s just circle back round to the subject question. The subject is the identity of the user. You may have noticed that I’ve started this post with PolicyServer, so I don’t have any authentication in place here. However, for testing purposes, we can force our user to a specific identity.

Let’s override the Index method of the HomeController:

        public async Task<IActionResult> Index()
        {
            var identity = new ClaimsIdentity(new Claim[]
            {
                new Claim("sub", "2"),
                new Claim("name", "TestUser")
            }, "test");

            User.AddIdentity(identity);

            var homeViewModel = await SecureViewModel();
            return View(homeViewModel);
        }

We’ll come back to the ViewModel business shortly, but let’s focus on the new ClaimsIdentity; notice that the “sub” (or subject) refers to the Subject ID of the driver above. So what we’ve done is created a driver!

The SecureViewModel method returns a ViewModel (which, as you can see, is passed to the view); let’s see what that might look like:

        public async Task<HomeViewModel> SecureViewModel()
        {
            var homeViewModel = new HomeViewModel();            
            homeViewModel.ViewRoutes = await _policyServerRuntimeClient.HasPermissionAsync(User, "ViewRoutes");
            homeViewModel.CreateSalesOrder = await _policyServerRuntimeClient.HasPermissionAsync(User, "CreateSalesOrder");
            homeViewModel.Timesheet = await _policyServerRuntimeClient.HasPermissionAsync(User, "Timesheet");

            return homeViewModel;
        }

You can play about with how this works by altering the “sub” claim.

How does this fit into Identity Server (or an identity server such as Azure B2C)?

The identity result of the authentication, should map to the “sub” claim. In some cases, it’s “nameidentitifier”. Once you’ve captured that, you’ll need to store that reference against the user record.

That’s all very well for testing, but when I use this for real, I want to plug my data in from a database

The first thing you’ll need to do is to ‘flatten’ the data. this StackOverflow question was about the best resource I could find for this, and it gets you about 90% of the way there. I hope to come back to that, and linking this in with Asp.Net Core Policies in the next post.

References

https://auth0.com/blog/role-based-access-control-rbac-and-react-apps/

https://github.com/IdentityServer/IdentityServer4/issues/2968

https://stackoverflow.com/questions/55450393/persist-entitys-in-database-instead-of-appsettings-json

https://stackoverflow.com/questions/55450393/persist-entitys-in-database-instead-of-appsettings-json