Cloud

Following my previous post of How adding an UI built in Blazor, my focus in this last post will be on improving on the application’s behaviour following clean architecture handling errors, adding logging, authenticating users.

The source code of this post is on GitHub. Because one single post is too long, I have created the following posts:

Goals of this post

So, we have a working API, which is based on good architectural principles, and we have a working Blazor application. Things are moving along nicely for the architecture and reference app we’re creating for MyTicket, but there are still a few things missing in the architecture. Now, I want to show you how improving on the application’s behaviour.

Let’s call them crosscutting concerns that’ll be needed throughout the entire application. Again, my goal with this post not teaching you all the details. We have plenty of other resources available for that. Instead, I want to show you how this can be fitted into the architecture. I’ve selected a few topics that we still need to add into our application architecture.

First, I’m going to go into error handling and how we can bring that in all the different levels. We’ll need to take a look at how we can add them in the core, as well as on the API so that clients know what happened based on the request they have sent in.

Next, I’m going to bring in support for logging, another crosscutting concern that we’ll need in the application. And finally, I’m going to implement a base authentication mechanism to authenticate requests on the API. Let us take a look and start improving on the application’s behaviour.

Handling Errors in the API

As mentioned, let’s see how we are now going to react when some error occurs in the API and other layers. Handling errors correctly and letting clients know what happened is crucial. So, in this part, I’m going to explain how we can, in the architecture that we have set up, handle errors. There are definitely different approaches possible here, but I’ve selected just one. The approach I have taken is actually twofold.

First, we’ll need to look at the core project since that is the place where errors and exceptions can and will occur. In the core application project, I have to find a number of custom exceptions, classes that inherit from the built‑in ApplicationException.

The reason that I’m defining custom classes here is that these classes can also be used from any other project that references the core, so pretty much everywhere. This will include exceptions that happen when we are, for example, asked to retrieve an instance that doesn’t exist, so basically a NotFoundException. We’ll create our own not found exception instead of using the one that comes with ASP.NET Core.

Remember, we’re not talking about the API project here, but we are talking about the Core application project, so these exceptions will be coming out of the application core. Then, the next task is letting the client know what happened, and this I’m going to do by returning ASP.NET status codes, possibly accompanied by information about what happened.

Middleware in ASP.NET Core

The letter could be, for example, validation errors coming back from the Core. I’m going to handle these exceptions in a simple way, namely by adding middleware in the API, which will convert the exception from the application core to an exception known by the clients. The exception classes themselves are pretty straightforward, and you can see an example, the aforementioned NotFoundException.

public class NotFoundException : ApplicationException
{
    public NotFoundException(string name, object key)
        : base($"{name} ({key}) is not found")
    {
    }
}

Say that the client asks for an ID that cannot be found. Well, then the core application project will throw such an exception. The class is, as you can see, inheriting from ApplicationException, a built‑in type in .NET Core. In the API, we’ll use and set middleware.

MIddleware explained

Now, middleware is a typical a ASP.NET Core functionality. It’s basically a piece of software component really that is plugged into the request pipeline. When a request is sent to our ASP.NET Core application, it travels through the pipeline, and while doing so, middleware components can work on the request.

Each of them can inspect the request, and if needed, perform actions with it. It can change the request, it can pass it on to the next component, or even decide that the request shouldn’t go further through the pipeline and thus perform a short‑circuit, sending back a response to the consumer.

Middleware can also work on the response, which is what we will be doing. After execution, it’ll travel back in the opposite order through the pipeline, and middleware can inspect it again. Our custom middleware component that we’ll include will do exactly that.

It’ll see if an exception has happened, and if so, it’ll convert the exception. One thing to watch out for is the order that you apply components in the pipeline. Since the middleware components typically pass the request from the one to the next component in the pipeline, the order that you place them in has an influence on their behaviour.

Request middleware pipeline explained

Setting up the middleware pipeline happens at the Startup.cs of the application in the Configure method.

Request pipeline in the middleware - Improving on the application's behaviour
Request pipeline in the middleware

Here you can see a visual representation of the request middleware pipeline. When a request arrives, it’ll pass through this pipeline on which the middleware components are plugged in. Each component has the ability to inspect a request, act upon it, pass it on to the next component, or simply short‑circuit it.

This happens on the request, as well as on the response coming back from the application code. ASP.NET Core comes with quite a few components built in, and some are already plugged in by default. But it’s also an extension point. You can create your own middleware components.

public class RequestCultureMiddleware
{
    private readonly RequestDelegate _next;

    public RequestCultureMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        ...
    }
}

Now, writing custom middleware is not very difficult. A middleware component is just a class that must have a constructor with a parameter of type RequestDelegate. We’ll plug it into the pipeline in the Configure method, typically using an extension method. The actual functionality will live in a method called Invoke or InvokeAsync, which must accept the HttpContext parameter. This method will be invoked automatically upon execution of the middleware in the pipeline.

Implement handing errors

Now, let us add exception handling in the application architecture and see how this cross‑cutting concern fits in. We need to perform a few tasks, creating the custom exceptions in the core application project to throwing exceptions from the application logic so perhaps when validation fails, and then converting the exception using custom middleware. I have already shown you the custom exceptions that I’ve created in the application project.

So, we had the NotFoundException, which inherits from the application exception and typically it will be used if someone wants to update, for example, an event that doesn’t exist. The BadRequestException, well, that’s more used if the input is, for example, null when we are expecting to update an event. ValidationException, I think the name gives away what that one is going to be used for. It will be used to return to the client validation errors that have been thrown by Fluent Validation. Let’s take a look where I’m using that.

public class UpdateEventCommandHandler : 
        IRequestHandler<UpdateEventCommand>
{
    private readonly IAsyncRepository<Event> _eventRepository;
    private readonly IMapper _mapper;

    public UpdateEventCommandHandler(IMapper mapper, 
        IAsyncRepository<Event> eventRepository)
    {
        _mapper = mapper;
        _eventRepository = eventRepository;
    }

    public async Task<Unit> Handle(UpdateEventCommand request, 
        CancellationToken cancellationToken)
    {

        var eventToUpdate = await _eventRepository.GetByIdAsync(request.EventId);

        if (eventToUpdate == null)
        {
            throw new NotFoundException(nameof(Event), request.EventId);
        }

        var validator = new UpdateEventCommandValidator();
        var validationResult = await validator.ValidateAsync(request);

        if (validationResult.Errors.Count > 0)
            throw new ValidationException(validationResult);

        _mapper.Map(request, eventToUpdate, typeof(UpdateEventCommand), typeof(Event));

        await _eventRepository.UpdateAsync(eventToUpdate);

        return Unit.Value;
    }
}

For example, let’s take a look at the NotFoundException. The NotFoundException, as you can see, is used, for example, from the UpdateEventCommandHandler, that is this one. In here, I’m going to try updating event, and of course, if the event doesn’t exist, well it’s pretty hard to update it, right?

So, I’m going to, after the search using the GetbyIdAsync, see if that event is not null, and if it’s not null, well, then I’m going to throw one of my custom exceptions saying that that event simply can’t be found. Then how are we then handling that in the architecture. And the application code is simply throwing this custom exception, but these exceptions, they aren’t useful for a consumer of the API really.

ExceptionHandlerMiddleware

So, what I’ve done then is I’ve created in the API a piece of custom middleware, and it’s called the ExceptionHandlerMiddleware.

public class ExceptionHandlerMiddleware
{
    private readonly RequestDelegate _next;

    public ExceptionHandlerMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            await ConvertException(context, ex);
        }
    }

    private Task ConvertException(HttpContext context, Exception exception)
    {
        HttpStatusCode httpStatusCode = HttpStatusCode.InternalServerError;

        context.Response.ContentType = "application/json";

        var result = string.Empty;

        switch (exception)
        {
            case ValidationException validationException:
                httpStatusCode = HttpStatusCode.BadRequest;
                result = JsonConvert.SerializeObject(validationException.ValdationErrors);
                break;
            case BadRequestException badRequestException:
                httpStatusCode = HttpStatusCode.BadRequest;
                result = badRequestException.Message;
                break;
            case NotFoundException notFoundException:
                httpStatusCode = HttpStatusCode.NotFound;
                break;
            case Exception ex:
                httpStatusCode = HttpStatusCode.BadRequest;
                break;
        }

        context.Response.StatusCode = (int)httpStatusCode;

        if (result == string.Empty)
        {
            result = JsonConvert.SerializeObject(new { error = exception.Message });
        }

        return context.Response.WriteAsync(result);
    }
}

That ExceptionHandlerMiddleware will take a look at the exception that came back. So, if an exception is being thrown, I’m going to check which type of exception. Those are our exceptions, which of these has been thrown, and then exception is then going to be converted into an HTTP status code. For example, if I’m getting back the NotFoundException, I’m simply going to return an HttpStatusCode.NotFound.

Depending on the exception, I’m also going to be returning an error message and then that message is going to be written to the response. This middleware allows me to basically catch all exceptions coming out of the application code.

[HttpPut(Name = "UpdateEvent")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesDefaultResponseType]
public async Task<ActionResult> Update([FromBody] UpdateEventCommand updateEventCommand)
{
    await _mediator.Send(updateEventCommand);
    return NoContent();
}

That is why, for example, in the Events controller in the update, I’ve specified here that this is typically going to return a 204NoContent, but it can also return a 404NotFound and that is because that middleware is hooked in on the middleware request pipeline, so our middleware is responsible for converting the exceptions coming back from the application.

Now, simply creating that middleware won’t do anything. I still need to plug it into the middleware pipeline that I’m doing by creating another extension method, this time on the IApplicationBuilder that gives me access to the middleware pipeline.

public class Startup
{
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        // ...
        app.UseCustomExceptionHandler();
        // ...
    }
}

Here you see that using the UseMiddleware passing in my exception handler middleware, I can plug that in. This extension method, I’m then calling from the Startup.cs in the configure method. There I now say use the custom exception handler.

Adding Logging to the Architecture

Exception handling was the first cross‑cutting concern that we added into the architecture. Another one that is currently missing is logging. I think just about any application that you build should include logging. Otherwise, it’ll be pretty hard, if not impossible, to know what went wrong with the application when it’s running in production.

Logging in ASP.NET Core

Let’s see how we can add this. Since ASP.NET Core, adding logging has become a lot easier, since it is basically already built into the platform. To enable it, just like with everything in ASP.NET Core, we need to follow a few steps.

Logging is typically configured in the Program.cs class, so not from the Startup.cs class. In the appsettings.json file, we can specify the options we want to apply for the built‑in logging system. In our code, throughout the application code, we can now write log statements.

ASP.NET Core gives us the ILogger in T, and using that, we can write log entries. While we can use the built‑in logging system, I won’t be using this one, and I’ll instead use Serilog, which we’ll also need to configure. Let’s take a look at these steps in a bit more detail.

As said, ASP.NET Core and .NET Core come with logging support built in. We can therefore tap directly into that system. However, it’s a very flexible system. It’s provider‑based, which means that we can configure using a provider model how the logging is handled.

So, for example, where the log messages should be sent. And it’s actually pretty cool, since it’s really an extensible model. ASP.NET Core comes with a number of logging providers built in, including Debug and Console. We can write our own providers or use a third‑party one. It is what I’ll show you in just a minute when we add Serilog.

ILogger

On the logging engine, quite a few things can actually be configured, often done, like I said, through the appsettings.json, where we can specify these settings. One of the most important ones is the LogLevel, which can be seen as a filter to specify which level of messages should be sent to which provider.

private readonly ILogger<CreateEventCommandHandler> _logger;

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

_logger.LogError("Something went wrong...");

Here you can see how to write a message to the log. The ILogger instance will be provided through dependency injection. The code itself is agnostic to where we want to look. We just write logger.LogError, and then all configured providers will be triggered.

Indeed, we can have multiple providers, but this has no effect on the code throughout the application. The LogError method is simply saying log to all providers that are configured to LogError level or above. So, this is where the log level comes into play.

ASP.NET Core’s logging engine comes with six log levels. Trace is the lowest. Then we have debug, then information, warning, error, and critical. From code, we can thus decide what level should be used, and in the configuration of the provider we specify for which level a provider should be triggered.

Why Serilog

Now, we can do things fully with what we’ve seen here. Instead of using the default logging providers, we are going to use Serilog. Serilog is again an open source package that we can bring into our application architecture. Serilog is a third‑party logging framework which offers a number of advantages on top of the built‑in logging. Through add‑on packages, it enables us to write our logs to different log destinations, destinations not available in ASP.NET Core’s default providers. This includes a file, as well as the database.

Also, Serilog offers something called structured logging. Through structured logging, we get control over how log messages are written, and we can use templates to define how a message should be written to the log output. As mentioned, adding Serilog is done, again, through NuGet. We’ll typically need to bring in a number of packages. Writing to a different output typically requires that you bring in another package. It’s very nice to see that Serilog offers a very fine‑grained model and just adds what you want to use. Here you can see for example that I’ve brought in support to write to a file.

Adding Logging to the Application

Let’s return to our application and see how it can add logging. We’ll use Serilog, and we’ll need to configure it properly. Then we’ll write log information from within our application core code. Logging is another cross‑cutting concern I want to enable in my API. I want to make sure that if exceptions happen and I can see what has happened in a log file, that I’ve added support for here using Serilog.

<PackageReference Include="Serilog.AspNetCore" Version="3.4.0" />
<PackageReference Include="Serilog" Version="2.10.0" />
<PackageReference Include="Serilog.Extensions.Logging" Version="3.0.1" />
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="3.1.0" />

As you can see in the project file of my API, I have added support for Serilog, and I’ve brought in also a couple of extra packages. This one in particular allows me to also write to a log file, which is Serilog.Sinks.File.

How do I configure this cross‑cutting concern logging then?

Logging is one of those things that Microsoft recommends you do not do from the Startup.cs, but instead you do it from the Program.cs class.

  "Serilog": {
    "Using": [],
    "MinimumLevel": {
      "Default": "Information",
      "Override": {
        "Microsoft": "Warning",
        "System": "Warning"
      }
    },
    "WriteTo": [
      {
        "Name": "Console"
      }
    ],
    "Properties": {
      "ApplicationName": "Serilog.WebApplication"
    }
  }

So, in here in the static void main, I’m configuring the logger. First, I’m going to read out the app settings. The app settings contain settings for Serilog on how to log and that’s what you see here. We’re not going to take you through all these settings. You can find that on the Serilog website.

public class Program
{
    public async static Task Main(string[] args)
    {
        var config = new ConfigurationBuilder()
            .AddJsonFile("appsettings.json")
            .Build();

        Log.Logger = new LoggerConfiguration()
            .ReadFrom.Configuration(config)
            .WriteTo.File("Logs/log-.txt", rollingInterval: RollingInterval.Day) 
            .CreateLogger();

        var host = CreateHostBuilder(args).Build();
           
        host.Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .UseSerilog()
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

Let us focus on how we fit this into the architectural. This logger is the Serilog logger. I’m configuring it by pointing it to that configuration that we’ve just read out from app settings, and I’m also specifying that it should write to a file with a rollingInterval of Day. You can see in the file system or Solution Explorer that a new log file is created per day in a specific format also containing the date, and then I create that logger. With that done, logging is configured in my application; Serilog has been added now as a logging provider. I don’t need to do anything else for this to work.

ILogger in EmailService

Then, I can go to my application code in, for example, the email service. The email service, if you remember, is part of infrastructure. I now can simply use here the ILogger in EmailService.

public async Task<bool> SendEmail(Email email)
{
    var client = new SendGridClient(_emailSettings.ApiKey);

    var subject = email.Subject;
    var to = new EmailAddress(email.To);
    var emailBody = email.Body;

    var from = new EmailAddress
    {
        Email = _emailSettings.FromAddress,
        Name = _emailSettings.FromName
    };

    var sendGridMessage = MailHelper.CreateSingleEmail(from, to, subject, emailBody, emailBody);
    var response = await client.SendEmailAsync(sendGridMessage);

    _logger.LogInformation("Email sent");

    if (response.StatusCode == System.Net.HttpStatusCode.Accepted || 
                response.StatusCode == System.Net.HttpStatusCode.OK)
        return true;

    _logger.LogError("Email sending failed");

    return false;
}

The logger can now be used to log whether or not sending the email was successful. And do you remember the CreateEventCommandHandler? Well, there, too, I’m injecting a logger. And when the email sending actually didn’t work, I don’t want the application to break, I don’t want an exception to be thrown. I just want to log this, and I’m again using the logger here.

Authenticating Users

In the final part of this post, we are going to bring in support for authentication, again, a cross‑cutting concern we’ll often need in our applications. In the MyTicket application architecture that we have created thus far, this hasn’t been added, so let’s see how we can add this now. I do want to stress that authentication is a complex topic. The only thing I want to do here is adding a simple mechanism in the API, as well as in the client app so we can ensure that the requests coming in from the Blazor app are effectively authenticated.

Authentication on the back end

Let’s first take a look at the authentication on the back end. At the front of the back end, so on the receiving end, we have the API, and that one will be used in my setup here to authenticate requests. Now an API is stateless, meaning that we cannot rely on server‑side functionality to secure it.

Each API request that will therefore be coming in should be validated, and that’s going to be a task for the API itself. Now, how are we going to perform this authentication then? Are we going to be sending a username and password entered by the user in the Blazor app every time?

JWT Token

I don’t think, even with encryption enabled, that is a great idea. Instead, we’ll rely on a token to be included. In this sample, we’ll use the industry standard, a JWT token, or a J‑W‑T token. Using a token, we can, on the API, validate if the request is authenticated and if the user has access to the resources they are trying to access.

In the client application, we’ll first need to get a hold of a token. This will happen after successful authentication. Once authenticated, the token will be sent back to the client and typically stored also by the client. Then, when a request needs to be made to the API, the token will be included with the request, and it’s typically included in the header.

Now, as that authentication is a large topic and can be done in several ways, what I’m going to show you here is an approach to add authentication on the API using ASP.NET Identity. Other options include IdentityServer, a commonly used library in this context, which is also supported out of the box from a ASP.NET Core. You can also rely on an external provider and configure this to be used from your application. But, as said, we’ll use a ASP.NET Identity.

Add ASP.NET Identity

We have the Blazor application, which will be first redirected to a login page. On that page, the user needs to enter their credentials, typically a username and password. These are sent to an authentication endpoint that will validate these against the database. And we are using the ASP.NET Core Identity APIs.

If the credentials are valid, we’ll generate a token, which is then sent back to the client as the result of the authentication call. The token is a JSON Web Token, a J‑W‑T, or a JWT token. This token is now stored and included again with every subsequent request made to the API. The API will then verify that the request has the token validated and allowed to request through if the token was valid.

ASP.NET Core comes with the well‑known Authorize attribute to secure access to the controller. We’ll configure the application for authentication in the Startup.cs class. If our API gets in a request for an action on a controller secured by the Authorize attribute, in the Startup.cs class, we’ll use the add authentication, specifying that we’ll want to use JWT tokens.

Adding Authentication Using Tokens

First, I will show you the API where I have added the AccountController, a new controller, that is. We’ll take a look at the Startup.cs where I have now configured the API for using JWT tokens, and we’ll see how I’ve secured the access to other controllers. So, let us now take a look how I’ve now added support for securing the API through Identity.

You’ll see under infrastructure in the finished code for this module that I’ve now added also an Identity project. An identity is really infrastructure code. It really has to do with an implementation of that security. So, it really belongs under infrastructure. Nobody’s in there. Let’s take a look. I have in here added another DbContext. The MyTicketIdentityDbContext, which is going to be using the ApplicationUser.

Authentication service

ApplicationUser inherits from the regular IdentityUser, but just adds first name and last name. That application user you can find over here. Now, the most important logic is here under the AuthenticationService. And the AuthenticationService will use plain ASP.NET Identity classes to register and sign in a user.

I’m injecting the userManager and the signInManager, and I’m also going to use an instance of JWT settings, which is a class I created myself that will contain the settings that I’m reading it from the AppSettings file. And this class just has two methods, AuthenticateAsync and RegisterAsync, and it actually has a third one, but it’s a private one.

Register an user

Let’s first take a look at RegisterAsync, which will get in a RegistrationRequest. The RegistrationRequest is actually defined under application models. In here, I have all the classes related to data, requests and response, basically coming in and going out of the API in relation to Identity. In the RegisterAsync I’m doing pretty regular Identity stuff. I’m going to register a user.

Authenticate an user

First, I’m going to see if that username already exists. If that is the case, well, then I’m going to say that the username already exists. Otherwise I’m creating a new application user. I’m going to see if the email already exists. And if that is not the case, well, then I’m going to register the user. In the AuthenticateAsync, I’m going to get in an AuthenticationRequest, which will contain the email and the password.

This will be called to try and authenticate the user, and as a result, I’m going to send back a token. So, I’m going to again try and find the user. If the user is found, I’m going to try signing him or her in, and if that succeeds, then I’m going to generate a token. That’s going to be a JWT Security Token, a built‑in type of ASP.NET Core.

GenerateToken code

private async Task<JwtSecurityToken> GenerateToken(ApplicationUser user)
{
    var userClaims = await _userManager.GetClaimsAsync(user);
    var roles = await _userManager.GetRolesAsync(user);

    var roleClaims = new List<Claim>();

    for (int i = 0; i < roles.Count; i++)
    {
        roleClaims.Add(new Claim("roles", roles[i]));
    }

    var claims = new[]
    {
        new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
        new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
        new Claim(JwtRegisteredClaimNames.Email, user.Email),
        new Claim("uid", user.Id)
    }
    .Union(userClaims)
    .Union(roleClaims);

    var symmetricSecurityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSettings.Key));
    var signingCredentials = new SigningCredentials(symmetricSecurityKey, SecurityAlgorithms.HmacSha256);

    var jwtSecurityToken = new JwtSecurityToken(
        issuer: _jwtSettings.Issuer,
        audience: _jwtSettings.Audience,
        claims: claims,
        expires: DateTime.UtcNow.AddMinutes(_jwtSettings.DurationInMinutes),
        signingCredentials: signingCredentials);
    return jwtSecurityToken;
}

In the GenerateToken method, I’m going to generate the token containing a few claims, including the username, email, and the GUID. In the response, I then include the username, the email, and the token, and I sent that back to the caller. Also, still in the Identity project, I’ve added this Extensions class, which will contain the configuration for Identity.

Notice that I’m reading out the section JwtSettings from the configuration, and I register the DbContext, I specify that I want to use Identity using ApplicationUser, and I’m going to use for that my DbContext. I register my AuthenticationService, and finally, I configure how authentication should be done. Then, I specify that a token, a JWT token, is required. So, I specify old parameters for that.

Also, I configure how the different authentication events should be handled. So with that, my configuration of identity should be fully done. Let’s now go to the API, and in there, we’ll now find a new controller. And that will be the accountController. The accountController uses the AuthenticationService.

Register and Authenticate services

For that, I now have an interface defined again in application contracts. And now I have a subfolder Identity, which then contains that interface. That contains the AuthenticateAsync and RegisterAsync. That is injected through dependency injection. We registered that in the Extensions method. We still need to add that to our Startup.cs.

Let’s not forget about that. And in here, I just have two methods, two POST methods, one for Authenticate and one for the Register endpoint. They are going to be using the AuthenticateAsync and RegisterAsync methods. In the AppSettings, you’ll see that I’ve added the settings for the token generation.

  "JwtSettings": {
    "Key": "84322CFB66934ECC86D547C5CF4F2EFC",
    "Issuer": "MyTicketIdentity",
    "Audience": "MyTicketIdentityUser",
    "DurationInMinutes": 60
  }

In the Startup.cs, I’ve now added the call to also bring in my identity configuration. Then, and I’ve now configured Swagger to also know that it will require a bearer token.

services.AddIdentityServices(Configuration);

And of course, here we have now specified that we are going to use Identity.

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

So we have specified here UseAuthentication. I’ve also brought in UseAuthorization because I’ve now specified that these calls are now only accessible if the call is authorized. This authorize attribute will make ASP.NET Core search for the token in the request header, validate it against the specifications that you specified, and only if the token is valid will this call be allowed. This is not have added identity in our application architecture. It’s again a cross‑cutting concern that we’ve added.

Adding Authentication to the Blazor App

So, I’m going to show you a way to bring this authentication into the client app. That means, I’m now going to show you how our Blazor application is now using this token. I’ve in fact, on the home page, created a login and register functionality.

I’ve already created the user, so if I click here to login using a username and password, this will now be sent to the AuthenticateAsync endpoint, and if the username and password combination exists, and I will get back a token. And then I can still navigate to the Categories, for example, and that still works because of the fact that my token was included with the request.

public class AuthenticationService : BaseDataService, IAuthenticationService
{
    private readonly AuthenticationStateProvider _authenticationStateProvider;

    public AuthenticationService(IClient client, ILocalStorageService localStorage, 
        AuthenticationStateProvider authenticationStateProvider) : base(client, localStorage)
    {
        _authenticationStateProvider = authenticationStateProvider;
    }

    public async Task<bool> Authenticate(string email, string password)
    {
        try
        {
            AuthenticationRequest authenticationRequest = new AuthenticationRequest() { Email = email, Password = password };
            var authenticationResponse = await _client.AuthenticateAsync(authenticationRequest);

            if (authenticationResponse.Token != string.Empty)
            {
                await _localStorage.SetItemAsync("token", authenticationResponse.Token);
                ((CustomAuthenticationStateProvider)_authenticationStateProvider).SetUserAuthenticated(email);
                _client.HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", authenticationResponse.Token);
                return true;
            }
            return false;
        }
        catch
        {
            return false;
        }
    }
}

Let’s see how I’ve done that in the Blazor app. I’ve included another Service.Authentication service, that is going to accept the username and password, and it’s going to build up an AuthenticationRequest. Now where’s that AuthenticationRequest coming from the?

Authentication with bearer token explained

Well, it’s actually coming from the service client. Because of the fact that I executed NSwag again, it’s AuthenticationRequest type expected by my Authentication endpoint is also included in the generated code. So I’m passing in email and password, and then I simply call AuthenticateAsync on my client, which is going to go out to my API, passing in that AuthenticationRequest. That is going to get back an authenticationResponse, which should, if successful, contain a token.

If that token is included, then I will store that in the local storage. Blazor can actually access the local storage of the browser and can use that to store an authentication token. On subsequent requests, for example, on the CategoryDataService, I will now go out to the API, only including the BearerToken. In the GetAllCategories, what you see here is that I’m calling the AddBearerToken method.

public class CategoryDataService : BaseDataService, ICategoryDataService
{
    private readonly IMapper _mapper;

    public CategoryDataService(IClient client, IMapper mapper, 
        ILocalStorageService localStorage) : base(client, localStorage)
    {
        _mapper = mapper;
    }

    public async Task<List<CategoryViewModel>> GetAllCategories()
    {
        await AddBearerToken();

        var allCategories = await _client.GetAllCategoriesAsync();
        var mappedCategories = _mapper.Map<ICollection<CategoryViewModel>>(allCategories);
        return mappedCategories.ToList();

    }
}

In the AddBearerToken, I’m going to see if we have indeed, in local storage, a token, and if that is the case, I’m going to include that in the AuthorizationHeader as the BearerToken.

public class BaseDataService
{
    protected readonly ILocalStorageService _localStorage;
        
    protected IClient _client;

    protected async Task AddBearerToken()
    {
        if (await _localStorage.ContainKeyAsync("token"))
            _client.HttpClient.DefaultRequestHeaders.Authorization = 
                new AuthenticationHeaderValue("Bearer", 
                await _localStorage.GetItemAsync<string>("token"));
    }
}

That token is then going to be included on the request sent to our API. Our API can validate the token, and this can distinguish between authenticated and not authenticated requests. And the rest of the Blazor application, I’m not going to show you, you saw how we can do the actual authentication, but take a look at the finish code if you’re interested in more.

By Enrico

My greatest passion is technology. I am interested in multiple fields and I have a lot of experience in software design and development. I started professional development when I was 6 years. Today I am a strong full-stack .NET developer (C#, Xamarin, Azure)

5 thoughts on “Improving on the application’s behaviour”

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.