Over the last couple of videos, we've been looking
at how to make an Angular front end that can log in to an ASP.NET web service using JWTs - JSON
Web Tokens. And in this video, I'm going to do exactly the same thing in terms of front end. But
this time, I'm going to do it in Blazor so that we've got .NET, C# on the front end, as well as
on the backend server. And I've done quite a lot of work in putting that together. So if we just
take a look at the application we've already got, then it's got really just like we saw in the
Angular. We can list all the reviews, we can list a summary of reviews, and I've got a login page that
… at the moment it’s just set up the form, but it doesn't actually do anything. That's what we're
going to look at in this video. And at the moment, if we look at the server, we can see that I have
cheated that. I have turned off the authorization. So let's change that, so now we do need to login,
and let that run up. And so now what we'll see is if I try to look at all the reviews, we get an
error ‘Failed to retrieve data’ in both cases. So we've got to do the login. So let's take a look at
the code that's making all of that work. And so if we just take a quick look round, you can see what
I've got is a Reviews Razor component. And that's where we've got the <table>, loops through all the
reviews in the list. And you can see that we're using this repository that's been injected, you
can see up there, and the repository that you can see there uses an HttpClientFactory to get hold of
the HttpClient. And we've configured that in the Program here. So that's where we've configured
the client just called ‘ServerApi’ and given it the base address, which is coming from the
appsettings file. Just one quick thing to notice about the appsettings file, when you're doing in
Blazor Web Assembly, it has to go in the wwwroot, so we can see it up there, and that's where
we're defining the address of the backend server. But it has to go there, because that's been
downloaded and read by the code in the browser, it's not being read on the server, like you'd
have in a normal appsettings file. So that's just one slight thing to notice. But that's where
we getting that from. And then the ReviewRepository uses that to get the data from JSON - either all the
reviews or gets the summaries. That's what gets put back into the Reviews.razor. And then whether
we're looking at summaries or at all the reviews, is handled by the fact that we've got the
AllReviews that simply loads up reviews with the ‘style’ of ‘all’, or we've got ReviewSummary
that loads up with the ‘style’ of ‘summary’. So that's how we distinguish those. So that's the
base application we've got. And then of course, we've got the login, which, as I say, is just
the form. Then we've got the ‘Submit’ method, but we haven't done anything there. So let's get going
on that. And the first thing we need to do is to put in an AuthenticationService - so
very much like we did in Angular. So we'll go to Services and we'll add in there a new
class that we’ll call AuthenticationService. And then let's put some useful methods in there.
So the first thing we're going to have in there is the Login. And so we're going to have this as
a ‘public’. It's going to have to be asynchronous, for reasons we’ll see in a moment. So we'll have
an ‘async Task’, and if you remember the way we've done this before, we want the login simply
to return a ‘DateTime’ which is going to be the time at which the JWT expires. And we'll call
this ‘LoginAsync’ because it's an async function. And that's going to take a ‘LoginModel’, which
I've already defined. That's the model that we're using in the form inside the Login component and
we can see that just contains the username and the password, you can see there. So that's what
we'll have coming in there. And then what do we do with that? Well, we're going to use, again,
the HttpClient that we're going to get from the HttpClientFactory. So let's set that up. So what
I'm going to do is have a constructor and that's going to take in this IHttpClientFactory, that
we’ll call ‘factory’. And then we'll have a member of type HttpClient and we can set that up by just
saying ‘_httpClient = factory.CreateClient’ and then remember, in our Program, we had named the
particular client - so we’re allowed multiple clients doing it this way - but we're going to go
for that ‘ServerApi’ that we have there. And so that's created our client. And then in here, the
very first thing we'll do is we'll say ‘var’ and then we'll call this ‘response = await’ and then
on that '_httpClient', we're going to remember that this is a POST, so we’ll do a ‘PostAsync’
and that's going to go within the site to ‘api/authentication/login’. And then it's going to
take a body which is going to be that LoginModel. So the nice thing about that, that exactly matches
the LoginModel that we are receiving on the other side, because these are both C# now. And so
we'll just use this thing called ‘JsonContent’, which allows us to convert things to JSON.
We'll just get hold of the namespace for that and then on that we say ‘.Create’ and
we pass in the ‘model’ that we're going to have in there. So that's what that call there
is going to look like. Then having done that, we've got to just put in a guard clause. So we're
just going to say ‘if’ and then it's saying if the response is successful, but actually, we want
to get out, so we're going to put a ‘!’ on there. And then we can simply do a ‘throw’. And then
we'll have a ‘new UnauthorisedAccessException’ and then just simple message ‘LoginFailed’. But
if it succeeds, we then need to get hold of the content of that. So now we're going to say ‘var
content =’ another ‘await’ and then we're going to say ‘response.Content’. We're not going to
‘ReadAsString’, we're going to ‘ReadFromJsonAsync’ and then at that point, we're going to need to
get the model for the data that's coming back when we log in. Now, conveniently, we've already
got that because that's exactly the same data structure as is being sent from the server. So we
look here, we've got a class called LoginResponse, which is what it's sending when we do the login.
And this is one of the really nice things about doing this with Blazor: you've got C# on both
ends, so you can use exactly the same code. Now we're kind of assuming here that we're developing
the Blazor front end completely separately from the back end - different departments, different
organisations even - so we don't actually have the code shared. We could if we wanted to actually
just share the DLL that contains this and then exactly the same code would be working on both
the front and the back end. But I'm going to just kind of cheat that and just do a copy and
paste. So if we copy that, and then if we go back to the client, and what we'll do is in the Models
we'll add a new class, call that ‘LoginResponse’ and then just paste in the code that we had. So
now we've got exactly the same on the front end and the back end. Now let's go back here and
finish this off. So that's going to be our ‘LoginResponse’ in there. And then again
that could fail, so we're just going to do ‘if (content == null)’ then we'll ‘throw’
a ‘new InvalidDataException’ - not going to worry too much about the details of that. And
then finally, having done all that, we need to, from that content, get hold of the JWT and store
it in session storage. And then we hit a problem, because session storage is not something that is
available through Web Assembly and therefore not directly available in Blazor. We would have to use
JavaScript interop to get hold of that. And that's something we looked at in an earlier video, so
we could write that all directly for ourselves with JavaScript interop. But fortunately, like
with so many things you need to do with interop, it's already being provided in a NuGet package.
So we'll add that in there to save us a bit of effort and save as reinventing the wheel. So if we
just get hold of the NuGet packages here, and then we're going to go for thing called ‘Blazored’ and
you can see that ‘Blazored.Session’ – there’s also one for local storage, but we're going to get the
session storage one. And that just wraps up all that JavaScript interop for us so we don't have
to worry too much about doing that for ourselves. So now in here, what we can do is we're going
to have to inject that. So up here, we're also going to have a ‘private ISessionStorageService’
that we've just got from what we added as a NuGet package there. And then let's just add that
into the constructor. And so now we've got both of those created in the constructor. And that
means that now down here, I can say again ‘await’. So you'll notice this access to the session
storage is asynchronous - we didn't have to worry about that when we're doing it in Angular.
The reason being, it's not session storage itself that's a synchronous, it's always when you do
JavaScript interop it's a synchronous. So that's why that's got to be like this. And so on that
one, we can just do the ‘SetItemAsync’ and then we're going to need a key. So let's set that up as
well. So up here, let's say ‘private const string’ and then ‘JWT_KEY =’ and then typical thing we
may do here, do a ‘nameof (JWT_KEY)’, so it just sets that constant to a string which is its own name.
And so that's what we do in the SetItemAsync’. So we pass in the ‘JWT_KEY’ and then not the
full content, just the content and then the JSON Web Token. And then finally, remember, we're
returning a DateTime here, so we ‘return’ and then ‘content.Expiration’. So that's that first
bit done. But while we're in here, let's do a few other bits and pieces. Let's also have a
method that allows us to get hold of that JSON Web Token. And in fact, what I'm going to do here
to save that call to the session storage every time we want to get hold of it, I'm also going
to cache it inside the AuthenticationService. So we're going to have a ‘private string?’
that I'm just going to call ‘_jwtCache’, and so that will default to null. And then I'm
going to have a ‘public async’ and this is going to return a ‘ValueTask’. We can do that for a
little bit of a performance benefit and I did a video on that quite a while ago, if you want
to take a look at that. And it's going to return a ‘string’ - that's going to be the JWT. We're
going to call this ‘GetJwtAsync’ and then the first thing we do is we check to see whether we've
got it cached. So we can say ‘if (string.IsNullOr…’ I don't think we want ‘Whitespace’ there, we're
just worried about ‘Empty’ in that case. But if we haven't got anything in the _jwtCache, then
we're going to say, ‘_jwtCache =’ and again we've got to now make the call to the session storage.
So ‘_sessionStorageService’ and that's going to be the ‘GetItemAsync’. That one is going to be
type ‘string’, and it just takes that ‘JWT_KEY’. So that's what we've got there. So simple
enough, if it's not cached, we get hold of it, and then return the cached version, whether
that's been there for a while or just been fetched. Next thing I want to do here is have a
logout, so let's put that in as well. So again, it's going to be ‘public async’ nothing particular
to return so we just have the ‘Task’ there. We'll call it ‘LogoutAsync’ and just like we saw
before, all we need to do is we simply need to remove it from the session storage. So we'll
say ‘await _sessionStorage’ and then we've got the ‘RemoveItemAsync’ and again passing the key.
But then also, we've got to remember to clear the cache. So we'll say ‘_jwtCache = null’. Let's
get everything done in here while we're here, rather than jumping back later on. So there
was another thing we wanted to do, remember, which is get hold of the username from the JWT
so that we can display it in a friendly way. So let's put that in there. And what I'm going to do,
I'm going to write firstly a private method for this. So ‘private static string’ and it's going
to be called ‘GetUsername’ and so the idea is, we're going to pass in the JWT and
get the username out of that. Now, when I did this in Angular, I did this in quite a
low level way, using just the standard JavaScript ways of decoding the Base64 and that sort of thing.
I could do that here, but it's a bit more fiddly, and also there is a library already available that
we've actually used, that is there for exactly this sort of thing. So what I'm going to do here,
I can simply say ‘var jwt =’ and then ‘new’ and then a thing called a ‘JwtSecurityToken’ - which
I need to get hold of the package for, so let's just instal that - and then pass in the textual
form of it, so the token we've got there. And that will generate the actual token object. And we've
seen that before, as I mentioned, because again, if we go and have a look at the backend code, and
if we take a look in the AuthenticationController, then that was exactly what we did when we
logged in. So when we logged in, remember, we created this JwtSecurityToken and passed in all
of the bits and pieces directly - the claims and so forth. But now we're creating exactly the same
thing, but in the reverse way from the JWT string we have. So that's got that there. And then we
can simply read off the claim that we want. So we can say ‘return’ and then ‘jwt.Claims.’ and
then we'll just go for the ‘First’ that matches the particular thing we want, that's going to
be ‘claim => claim.Type == ClaimTypes.Name’. And then we have to get the ‘Value’ of that. So
that's done our GetUsername. And then there's one last thing we've got to do, we need to from this
AuthenticationService be able to inform people as to when the status of login and logout has
changed. So what I'm going to do finally, is put on here a ‘public event Action’ and then it's just
going to have a ‘string?’ string as a parameter, and we'll call this ‘LoginChange’. So whenever the
login status change, we're going to send this off, and that string is going to contain the username
or null if the person's logged out. And then we just need to fire those, so we fire them in two
circumstances. On the logout we're going to say ‘LoginChange?.Invoke’ and then we pass ‘null’
there because we're logged out. On the login we're going to do the same thing and that's where we're
going to call that ‘GetUsername’ and pass in the ‘content.JwtToken’. And so that's going to
give us the behaviour there. So that's our AuthenticationService fully put together. Let's
just also on there, give it an interface because this is going to have to be injectable. So do an
extract interface, all the public things we want to go in there. And then if we go to our Program,
then in here, we need to do the configuration. And so we need to do a couple of things. We need
to do a ‘builder.Services’ and then here I'm going to do an ‘AddSingleton’ because I want
that AuthenticationService to be a singleton, because remember it had that cached data for
the JWT. And if we didn't make it a singleton, then we would sometimes lose that. So
better to have it like that. So that's going to be ‘IAuthenticationService’ and
then ‘AuthenticationService’. And that's all on there. But also, we need to configure
the injection of that session storage object. And so we'll do another one, ‘builder.Services.’
and then we can do ‘AddBlazoredSessionStorage’ and then this one, again, has to be a
singleton. So there's actually a method there called ‘AsSingleton’. And I'll just get to
get hold of the namespace for that. So there we've got it from BlazoredSessionStorage. So you could
either go for ‘AddBlazoredSessionStorage’, but that will be scoped, which wouldn't be compatible
with that being a singleton. So we go for it as a singleton. And then next thing we're going to do
is in the Login component, we've actually got to make use of that. So there we've got our Task
with SubmitAsync and so what I'm going to do in here is, let's have a ‘private DateTime?’
and that's going to be our ‘expiration’. And then we're going to have a ‘private string?’
that we’ll call ‘errorMessage’. And then in the SubmitAsync we'll put in ‘try’ block. And we’ll
now make use of our AuthenticationService, which we need to inject. So up here, let's say
‘@inject’ and then ‘IAuthenticationService’. Get hold of the namespace for that. And then
we'll just call that ‘AuthenticationService’. And then back down here, we'll make use of
that that. We’ll say ‘expiration = await’ and then ‘AuthenticationService.’ and then
‘LoginAsync’. And we can see we already had, remember, the LoginModel that was bound on
to the form, so we can just pass our ‘model’ in there. And that will give us our expiration.
And if that works successfully, then we can set the ‘errorMessage’ to ‘null’. On the other hand,
if something goes wrong - remember, if anything went wrong with the login, we were throwing an
exception - so we'll catch an exception here and we'll set the ‘expiration’ to
‘null’ and we'll set the ‘errorMessage’ to ‘ex.Message’. Then we've just got to do some
binding to put that expiration and errorMessage on the form. So what we'll do is down the bottom, I'm
going to say at ‘if (expiration is not null)’ then we'll just put in a paragraph, ‘You are logged in
until’ and then ‘@expiration?.ToLongTimeString()’. And so that we'll put that information in
there. And then we'll also have an ‘@if (errorMessage is not null)’, then we'll simply
have a paragraph we use a ‘class’ that I'm already using elsewhere for ‘error’. And then
in there, we can just put ‘@errorMessage’. And the last thing I'll do - just like we had before
in the Angular - I'm going to say that ‘disabled’ if we don't have an ‘expiration,’ so we'll say
‘@(expiration is not null)’. And that will just grey it out when we're finished. So that should be the
basic login all working. So let's run that up. And obviously it can’t retrieve the data. Will go
to the login. Let's try it with some invalid login data and click on that, give it a second, and then
we get the ‘Login failed’, so that's working. Now let's try the correct login. So ‘Jasper Kent’ and
then ‘Pa$$w0rd’. Login for that onea And we're getting ‘You are logged in until’ three hours
from now, because that's the time we set. And we can just verify that this worked correctly,
because if we look in the session storage there, we've got our JWT_KEY with the token in there. So
that's all working. But what we haven't done yet, obviously, is passed that back. So we're still
getting ‘Failed to retrieve data’ on the all reviews and summaries. So let's do that next.
Now the way we do that in Angular was to use what Angular have called an HTTP interceptor. In
Blazor - very similar concept - but it's called an HTTP Handler. So let's put that in there now.
So let's put in a new folder called ‘Handlers’. And then in there, I'm going to add a
class called ‘AuthenticationHandler’. And then what we need to do
in here, we derive this from a standard class called ‘DelegatingHandler’.
And so that's what we have there. Then we're going to need a couple of things injected in
here. So we're going to need to have a ‘private readonly’ and then we're going to use the
‘AuthenticationService’ that I've already written. So pop that in there. And then we can
generate the constructor for that one. And then also, we're going to need
some configuration information. So I'm going to have a ‘private readonly’ and then
‘IConfiguration’, and we'll add that to the existing constructor. So that's those two setup.
Then what we need to do is we do an ‘override’ of a method from that delegating handler, and that
is going to be the ‘SendAsync’. So we could do either ‘Send’ or ‘SendAsync’ - we are going to have
to do some asynchronous code in here, so that's what we're going to have. And then again, if you
remember what we did with the Angular interceptor, there are a couple of safety checks we needed
to put in here. We need to make sure that we've got a JSON Web Token - so that we are logged in.
But also, we need to make sure that this request is being sent to the server for which that
JWT applies, not to any other, because this will intercept all of the requests that are being
sent. So first thing to do, we just say ‘var jwt = await’ and then on our ‘_authenticationService’
we've got that we wrote earlier, the ‘GetJwtAsync’. So that's easy enough. And then
also, we're going to say ‘var isToServer’, so this is going to check whether we're going back to the
correct server. And on this, we're going to say, ‘request’ - so we can see, we've got a request
coming in here, so this is the request that we want to add extra information to the header
in the end. So we're going to look at that and we're going to look at the URI and we're going to
check that the AbsoluteUri’ from that ‘StartsWith’ and then we want to make sure that it's got the
address of what we're looking at. And remember, we had that address, because we have that in our
appsettings. So that was just the development one we have, but that's what we want to get a hold
of. And that's why I needed to do inject the configuration. So what we're then going to do is
in here, we'll say ‘_configuration’ and then we'll just look up that ‘ServerUrl’. And we're going
to have a few situations of nulls coming through there, so I'm going to put a ‘?? ""’ and also a
‘?? false’ there. And that will mean basically that we've got true or false if it is to the
server just by looking at what's happening there. Then we need to check that both of those are
true. So check it ‘isToServer’ but also that we've got something in that ‘jwt’. And if
that's all true, then we can finally put the JWT into the authorization header of the
request that we're sending. So finally, we can say ‘request.Headers.Authorization = new’ and then
it's going to be ‘AuthenticationHeaderValue’ - get rid of that namespace and pop it at the top
- and then that takes the ‘Bearer’ that we've been seeing all over the place for this and then
finally, we can pass in that ‘jwt’. And then once again, very much like the Angular interceptors,
these things can be chained together, if you've got more than one thing you want to do. We don't
actually have that, but it means we're going to call the base class, which will hand it on to any
subsequent handlers we've got. And then we just ‘await’ that. And that should be that. So we've
written our AuthenticationHandler. Now, we just need to make sure that that is used. And so we go
to the Program and here where we had set up the HttpFactory. So that's where that was configured.
On the end of that, I'm going to say ‘.’ and then we'll just say ‘AddHttpMessageHandler’ and then
the ‘AuthenticationHandler' that we just written. But that also itself needs to be registered
for injection, so we're going to say ‘builder.Services’ and this one ‘Transient’
is going to be good enough. And then we can just put in the ‘AuthenticationHandler’ that
we've got there. So that should have configured that. And so now, without needing to change the
actual ReviewRepository or anything like that, when the ReviewRepository makes its requests
through the HttpClient, then it will go through that handler, and it will add the JWT. So
let's run that up and see what happens. And we can see that we're getting an error. And
if we bring up the console and look at that error, it is not a very easy one to see what's going
on. And when I hit this first time, it took me quite a while to work out what's going on. But the
problem is, we've actually put together a cyclic dependency in terms of our dependency injection,
because if you look at what happens when we, let's say, create our AuthenticationService,
the AuthenticationService in its constructor, creates an HttpClient. But when we create
an HttpClient, because we've just added that AuthenticationHandler, we create an
AuthenticationHandler. And when we have an AuthenticationHandler, in its constructor
we inject the AuthenticationService. And we're already in the constructor of the
AuthenticationService at this point, so we're in real trouble. So that's actually
why we had to do this with an HttpClientFactory, not just the plain old HttpClient, so that we
can defer that creation. Because what I'm going to do is go into the AuthenticationService. And
rather than using the factory to create the client directly in the constructor, I'm actually going
to make it so that what we're going to store is the HttpClientFactory. So we'll just call that
‘_factory’. And then in here, we're just going to say ‘_factory = factory’. And then the bit
of code that actually created the HttpClient, we're not going to have in the constructor because
that's what was causing the problem. We're only going to have it when we actually need it, which
was down here in the LoginAsync, which will happen a lot later on if it happens at all. So
that's where we're going to create an HttpClient, just as and when we need it. And then that should
solve the problem. So now what we're going to see if we run it up, not logged in at the moment,
but if we log in, so we'll put that in there, log in, ‘Logged in’ okay. And if we now go to all
reviews, or summary, we can see that that is now valid, because that AuthenticationHandler has
put the JWT in the header, it's being received, and everything's working fine. Okay, one last
thing to do, we've got to do the logout. And we've got to do the message welcoming the
user that we did in Angular. So let's just finish it off like that. So what I'm going to
do now is add another component. So in Pages, let's add a Razor component and I'm simply going
to call this User. And this is going to make use, once again, of the AuthenticationService. So
we'll do an ‘@inject IAuthenticationService’, get hold of a namespace and
call it ‘AuthenticationService’. And then in the code, we will have a ‘private
string?’, which is going to be ‘username’ and a ‘private bool’ called ‘isLoggeIn’ which
is simply going to say we're logged in if we've got a valid username, so ‘username is not null’.
Okay, so that's simple enough. We're then going to have to have an override of ‘OnInitialized’ -
don't need to do anything async here, so that will make life a bit simpler. But here, we're going
to subscribe to that event that I put in the AuthenticationService. So ‘AuthenticationService’
and then ‘.’ and then remember we've got this ‘LoginChange’. And so we're going to put a handler
on that. Remember, that gives us the username, so we'll just call that ‘name’ as the parameter.
And then what we're going to do is just say ‘username = name’. One slight problem here,
something you'll often encounter when you're using Blazor, if you do something like that changing
one of the fields in response to an event, the Blazor system itself is not going to know that
that's made a change, and therefore it's not going to refresh the DOM and give us the new display.
And whenever you're in that sort of situation, you're going to need to do a ‘StateHasChanged’.
So calling a method on the Component base class to do that. Normally, I would try and get away
with that and see if it works, but very often you'll see it's not working and I know this isn’t
going to work without that StateHasChanged. So that's what we've got. And then the other thing
we'll put in here is we will have a ‘private async Task’ that we're going to call ‘LogoutAsync’. And
that's where we're going to call the logout on the AuthenticationService. So we'll ‘await’ and then
‘AuthenticationService. LogoutAsync’. So that was the method that we'd written earlier as well. So
that's now used all of the features we had in the service. Let's just put some markup in for that.
It's going to be pretty simple. We're going to say ‘@if (isLoggedIn)’, so if we're logged in, we'll
have a ‘<span>’ that's going to say ‘Welcome’ and then let's put in that ‘username’. And then also
we'll put in an anchor that we’ll call ‘Logout’ and that's simply going to call
the logout that we've just written, so there's our ‘LogoutAsync’. So that's it
for logged in. And if we're not logged in, then we're going to need to just have the menu
item that takes us to the login. So if we go to our layout page, so there's our MainLayout. So
that's what the menus currently looking like. So let's cut that. Let's pop in our User component
at that point, because that's where we want it to go. And then back in here, we'll just paste in
that Navlink for login that we've got there. And so now that should be everything working. So if we
run that up … so we're not logged in. We'll go to the login page, put in the correct details, click
on login. And then you can see we've got ‘Welcome Jasper Kent’. We've got the ‘Logout’ button.
At the moment we're logged in, so we can see all those things. We click ‘Logout’, we go back
there, and now we’re getting the error when we go in there. So exactly the same functionality as
we had in the Angular example last time, but with some slightly different techniques because of the
different ways that things are done in Blazor and in C# compared with Angular and TypeScript. Okay,
so we've seen two front ends there. Next time, it's going to be a different front end - different
technology just to see how all of these work - and so we're going to be doing that in React.
So I hope you enjoyed that one. If you did, do click like and to keep up with the next one,
do click subscribe, and I'll see you next time.