Authentication & Authorization

Introduction

When we initially created the project in Visual Studio we chose the option to use 'Individual Accounts' for 'Authentication Type' (Visual Studio - Project Setup and YouTube Video 'Part 3') but haven't implemented any authentication or authorization so far, other than determining the user email for the purchase order header (Orders - Part 1 and YouTube Video 'Part 10').  In this post we will be ensuring that a user is registered and logged in before they can view, or maintain, any orders, and they are in an 'Admin' role to view and maintain Tax Rates, Suppliers and Products.

What follows is based largely on a YouTube video by Carl Franklin (Basic Authentication and Authorization in Blazor Server), and I would recommend viewing the video for a better understanding.

  • Authentication is knowing who the user is
  • Authorization is granting permission to view data or perform some action

YouTube Video

Identity Manager

By selecting to create the Purchase Orders project with Authentication we already have forms for registering a new user, a login form, as well as a form to allow the user to make (some) amendments to their account.  These were part of the Blazor server project template.  However there are no administration forms that would allow a system administrator to create and assign roles.

To keep things as simple as possible we are going to use an existing application from GitHub to provide the administration we need.  As mentioned by Carl Franklin the application we will be using has been deprecated, but it still works. This is a separate application that we will run alongside the purchase order application, although I imagine it would be possible to integrate it (or its successor) into purchase orders. Open a browser and go to https://github.com/mguinness/IdentityManager and download the zip file and save in a suitable location.  

  • Extract the zip file and run the solution in Visual Studio by double-clicking on the .sln file.
  • Change Startup.cs to use SqlServer, not SqlLite
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
  • Open appsettings.json and change the DefaultConnection so it is exactly the same as the DefaultConnection in  the PurchaseOrders appsettings.json. (Copy & paste is the best way to ensure they are the same!)

Run the Identity Manager application and you will see any users already created.

Add a Role, called 'Admin'.

Select your user and add the Admin role to your user.

Roles

Return to PurchaseOrders in Visual Studio.

To be able to use Roles we need to amend Startup.cs by adding the following in the 'ConfigureServices' method

 .AddRoles <IdentityRole>()

Setting Authorization

We will start by restricting the menu so that non-admin users cannot see the options to maintain Tax, Suppliers and Products.  Select NavMenu.razor and surround these options with the following:

Run the application again.  Assuming you have added yourself to the 'Admin' role, you will be able to see the Tax Rates, Suppliers and Products menu items.  Log out and add a new user by clicking 'Register' and filling in the form; click the confirmation link and log in using the new user.  You will be able to see the main purchase order list, but not the other menu options.

However, there's a problem.

You can enter the route to the Tax Rates, Suppliers and Products pages in the browser address bar and they will open.  To prevent this we need to add the following to the top of these pages:

@attribute [Authorize(Roles = "Admin")]

As an aside, to permit a page to be accessed by multiple roles a list of roles may be entered, separating each role by a comma, e.g. 

@attribute [Authorize(Roles = "Admin, Manager")]

But, there's still problem

The next problem is that the list of purchase orders is visible to anyone, before they login.  In addition, if an authenticated user (or non-authenticated user!) of the system knew the route to a particular page e.g. https://localhost:44377/purchaseorder/{POHeaderID}, they could type the address into the browser address bar and gain access.  The simplest method of preventing access globally is to add the following to _Imports.razor.

@attribute [Microsoft.AspNetCore.Authorization.Authorize]

To allow access to a particular page for a non-authenticated user, the following can be added to the page.  (For now, add this to the top of PreviewOrderPage.razor)

@attribute [Microsoft.AspNetCore.Authorization.AllowAnonymous]

And it's not very pretty!

Making these changes will prevent the Index page opening, but isn't very friendly...

Perhaps we can do better than this, and display a helpful message telling the user to login, or register.  We can do this in code by making the following changes to Index.razor.

At the top of the file enter the following.  This is because it appears that the attribute we entered in the _Imports.razor file takes precedence over other authentication code.

@attribute [Microsoft.AspNetCore.Authorization.AllowAnonymous]

Now, at the top of the HTML enter the following:

    <AuthorizeView>
        <NotAuthorized>
            <h5>Please Log in</h5>
            <h6>or</h6>
            <h5>Register to use Blazor Purchase Orders</h5>
        </NotAuthorized>
        <Authorized>

and closing </Authorised> and </AuthoriseView> tags after the end of the grid.

We now have a system that restricts access to authenticated users and hides Tax Rates, Suppliers and Products from users who are not assigned an 'Admin' role.

Project Code

The code for all the files changed in this post are shown here.