Over the past few videos, we've been setting up
an ASP.NET Web API server that has authentication based on JWTs - JSON Web Tokens. And then we
started using that, but we were only using it really through Swagger, which is a tool for
developers to help us develop the Web API, but it's not something that a real user would
use. So let's actually put a real front end on this. And we can do that in all sorts of
different technologies, but the first one we're going to do is do that in Angular. And so if
we take a look at what I've got running here, you can see that we've already got this Angular
application running and we've got a couple of the endpoints from our Web API web service. So we've
got the all reviews, we've got the summary, and I've also put together a login page. But there's
no functionality behind that there - that's just taking in the data. What we're going to see in
this video is how we can make all of that work. Now just a couple of things to say about the
server itself. If we look at the code for that, I've made a couple of changes. One is I've had to
add CORS on to the server - Cross Origin Resource Sharing - because we're now accessing this
service from a web site that's been served up from a different server. And so you've got
this issue, of CORS. We talked about CORS on an earlier video quite a while back. So have a look
at that, if you want a bit more detail. Just has to be said this is very, very relaxed in terms of
the rules, of CORS, just to get it working. You should probably be a bit stricter than allowing
everything through. The other change I've made is on the controller, I actually temporarily
commented out that [Authorize] attribute, which is why we can currently, on the front end,
actually access the data, even though we're not logged on. So let's change that one. So we'll
stop that, put that back on, run it up again. And we get the Swagger, but we're not interested
in that - we're looking at the real front end. And now in here we'll see that if I try and look at
either of those, we're getting an error: ‘Failed to retrieve data’. Let's take a quick look at how
that's working in Angular. And so what I've done is I've created this BookReviewRepository. And
if we look at the code for that, it's injecting the HttpClient. And then we've got a couple of
methods, getReviews and getSummary for those two pages. And they use the injected HttpClient
to go to the endpoints on our web service, we've got the actual route there just set up in
the environment by way of configuration. And as we saw again in many earlier videos, that returns an
Observable of the array of BookReviews that we're getting back. We’ve then got the ReviewsComponent.
And what that does is it reads in some data from the route, which is either going to be ‘summary’
or it's going to be ‘all’ - that's what we default into. So we can use the same component to either
call the getSummary or the getReviews that we just saw on that injected repository that we've got up
there. And then we do a bit of configuration of that - we do some sorting, we blank out the error
message. And then we have a CatchError. And that's where we're getting that ‘Failed to retrieve
data’. So I'm not looking in very much detail at the error. We can actually see what's going on
if we look at the console there. You should see if we bring up console and look in there, there's the
more detailed error that it was a 401. Not quite sure why it's giving us an ‘OK’ there because
401 is not okay, but it's definitely 401. It's definitely failing. And then finally, just in the
template for that there, we've got the <table> that is hidden if there's an error message,
loops through the reviews on the Observable, and then we've got the error message at the
bottom. So that's just the basics of it working. Nothing new there. Various earlier videos to look
at to see how that works. Now let's get our login working. And the first thing we're going to do is
we're going to add an AuthenticationService. So at the moment, if we look at the LoginComponent,
we've got the login method, which is what's being called when we submit the form we can see there.
And we've got the username and password that are bound to those fields on the form. But we need
to get this working, so what I'm going to do is create a new service. I'm going to do ‘ng g’ -
for generate – ‘service’ is the type of thing I want to generate - and I'm going to call this
‘authentication’. And then I'm going to say ‘—flat=false’ so that it gets its own folder. If
you don't put that on there, you'll just get it in the main app folder and it gets really messy
on a bigger applications. So we'll do that and let it generate that code. And then if we go in
there into that ‘authentication’ and have a look at that, we've just got that basic service put
together. So what do we need on there? Well, the first thing we're going to need is to have a login
method. So let's put that in there. We're going to call it ‘login’ and it's going to take the
‘username’ which is going to be a ‘string’ and the ‘password’ which is going to be a ‘string’. And
it's going to return an ‘Observable’ for a ‘Date’. If you remember what we got back when we
log in to our server, we get back the JWT, and we also get the DateTime telling us when it's
going to expire. So we're going to display that expiry time. And that's why we're coming back
with the date. We need to get hold of the import for that, that's easy enough, and then what we're
going to do here is we're going to need to make use of the HttpClient so that we can talk back
to the server. So that means in our constructor, we're going to have to have a ‘private’ just
call it ‘httpClient’ of type ‘HttpClient’. We've already configured that in the AppModule,
because we've already been using that for the ReviewRepository that we were looking at to get
the data. So that's fine in terms of that. So that's what we've got there. And so now we've
got to make a request on that, and we're going to do a little trick that I always do when I'm
working with Observables. Although ultimately, we want to return the Observable, because the type
of an Observable keeps changing, whenever you do a ‘pipe’ operation on it, it gets quite confusing
in terms of the errors. So I find it much easier to actually just create a local variable so we
can see what type it is as it goes by. And then once we finish this off, and that will be of type
‘Observable<Date>’, then we can just return it instead. That just makes the coding bit easier. So
I'm going to say ‘ = this.httpClient’. So there's the httpClient we injected. And then remember,
it was a POST. So we can always check up with the Swagger to see that this is going to be the
login. And we've got the ‘post’ there. So all that data is there if you want to look it up. And
so it's going to be a POST. It's going to return, well, we've actually got to put in a class,
and we're just going to in here - in this DTOs folder where we’ve already got the model for
the BookReview - let's just add a new file. And we're going to call this ‘login-response.ts’.
And in there, we're just going to say ‘export interface LoginResponse’. And that's
going to contain the ‘jwtToken’ which is going to be a ‘string’, and also the
‘expiration’ which is going to be a ‘Date’. And that is just mimicking the structure that we
had in our C#. So over here, if we take a look at the ‘LoginResponse’ there - exactly the same
thing. It's got the JwtToken, the expiration. So that's going to be serialised to JSON, and
then deserialized back in our front end. And so in the AuthenticationService, that's going
to be the return type from the POST. Then we're going to need to put in the URL. So we can get the
basic URL from our environment file. So down here, you'll see we've got the ‘environment’.
And so that's just taking us back to our development server. So we can in here, just
say ‘environment.serverUrl’ and then on to the end of that we say ‘/api/authentication/login’.
So that's the path we had there. And so that's the basic path, we then have to pass in the
parameters. And remember what we're taking as the parameters going in there. Again, we can
look at this in the C# or in Swagger. But we can see that we've got the LoginModel. And that's
just the ‘username’ and ‘password’. And actually, we don't really need to create a type for that in
the TypeScript, we can just do that on the fly. So we've got a second parameter in here, which is
just going to be ‘username’ and ‘password’, which are the parameters coming into this login. And by
the magic of JavaScript that will create an object with properties of those names. So that'll work
fine. And let's just take a look at ‘o’ we can see it currently is an ‘Observable<LoginResponse>’.
And this is a problem I was saying earlier: it's not yet of the right type. So it gets a bit
confusing if we try and return it, because what we're now going to do is, we're going to put some
pipeable operators in there. And I'm going to do a couple of things. First thing I'm going to
do is I'm going to ‘map’ that get hold of the import for ‘map’. And then I'm going to say ‘r’
- for response – ‘=> r.’ and then we wanted the ‘expiration’. And so that now means our ‘o’ is the
right type - it's the ‘Observable<Date>’, which we have there. But we're also going to do something
else. We've also got to record the JSON web token, because we're going to be sending that back in
future requests. Now, that raises the question, where do we store it? There's a number of options
for this. You could simply store it in memory, so as a variable within our Angular application. The
problem with that would be it would be very, very short-lived. If you did a refresh on your browser
that would reload the application and you'd lose all the data and you'd have to log in again. You
could store it in a cookie, and that used to be done a while ago, but that's a bit unnecessary,
because cookies get sent back and forth between the server and the client, and we don't need that.
We just need to store this on the client. And so in more recent HTML, we've got a couple of options
for that. We've got either session storage, or local storage, which actually store them in the
browser and are accessible only by applications that are running on this website. The difference
between them is that when you do local storage, it persists even when you shut the browser down.
Whereas session storage, although that will persist over a refresh or anything like that, if
you're actually close the browser and reopen it, you lose your session storage. And I think that's
probably the best one to go with. It's a bit irritating, because it means your login won't
persist for very long, but it's safer, because if you're on a public machine, and you forgot
to log out, even if you shut down the browser, somebody else could come along, go to the same
site and use your authentication. So we're going to store it in session storage. So what I'm going to
do is put a property on this AuthenticationService which is going to allow us to read and write the
JWT into session storage. So the very first thing we'll do actually is just declare as a constant,
the key we're going to use in session storage. So I'm going to say ‘private static readonly’
and then we're going to just call this ‘key’. And we're going to just call it the ‘JWT_KEY’.
So that's how we identify this particular item that we're storing in session storage.
Then I'm going to have a get property, so we'll do ‘get jwt’ returns ‘string’. And then
that's simply going to say ‘return’ and then ‘sessionStorage.getItem’. Then we put in that
key, so ‘AuthenticationService.key’. And then we've got a slight problem that if that's empty,
it'll send back a null string. So we'll just change that to a blank if it's empty. We're
then going to have a setter. The setter we're going to make private because we don't want
outsiders to do this. We're going to call it, again, ‘set jwt’. It's going to take a
‘value’ which is going to be a ‘string’. Doesn't have any return. And in this one, we
again say ‘sessionStorage’ and then this time, we've got a ‘setItem’ and again, we can
take that ‘key’ and also pass the ‘value’. So now we've got our getter and our setter. And in
this pipe, we're then going to use a ‘tap’, which, remember, doesn't modify the Observable,
it simply does something as it's being processed. And so in there, we say ‘r =>’ and then we're
going to say ‘this.jwt = r.jwtToken’. Okay, so that's going to be calling the setter, and
therefore storing into the session. So we've got the two items of information on the response.
We've got the token that we're storing, and we've got the expiration that we're returning. And so
now that's all working, we can simply change that, and put the ‘return’ in there and the type
matches up. And that works fine. And then finally, all we need to do is hook that up to the
LoginComponent. So simply enough here, we need to give this constructor and we're going to inject
into that our ‘authenticationService’ of type ‘AuthenticationService’. And
then in the login, we can say ‘this.authenticationService.login’ and then just
pass in ‘this.username’ and ‘this.password’. So all easy there. Now that of course is not going
to do anything as yet, because we haven't actually started observing that Observable. And remember,
one of the key things about Observables is they do nothing until you actually subscribe to them
in some way. So what I'm going to do here is I'm going to put on here a public field called
‘expiration’. And I'm going to put ‘$’ on the end of there - simply as a naming convention, so
that we know that this is an Observable. And then I'm going to type it to be our ‘Observable<Date>’.
And then also, I'm going to put a question mark on there, because it's not going to be immediately
there and so we've got to give it the option of being undefined. And then all we’ve got to do on
here is say ‘this.expiration$ =’ the result of the AuthenticationService, which remember was that
Observable<Date>. Then in the HTML, I'm just going to start consuming that. So what I'll do here
is down the bottom, I'm just going to put on a paragraph. And I'm going to give this a ‘*ngIf’
and then if that ‘expiration$’ exists. So just put that on there. And then in there, we're going to
say ‘You are logged in until’ and then we'll bind to the expiration. So we'll have our ‘expiration$’
then because that’s an Observable, we use the ‘async’ pipe, and then also the date we use the
‘date’ pipe and configure that to a predefined one called ‘longTime’. So I'll give us the full
time, but not the date on that. And so that should display the result. And then the other thing
we'll do is on the form itself, let's disable that if we've got that ‘expiration$’. So once we've
clicked the login button, that's when we'll get the expiration set to being defined. And so that
should all work. So if we save all of that - and that looks like it's built successfully - so
if we now go up here, go to the login page, and remember, the username was ‘Jasper Kent’ and
then ‘Pa$$w0rd’ for the password. Log that in. And you can see what happened there, the
result appeared, ‘You are logged in until’, but then there was a slight delay because we had
to get the result back. And you can see that set to three hours from now, because that's what we
set in earlier videos as the expiration. And what we can also see is, if we bring up the Dev Tools
and look in Application and go to Session Storage, there you can see we've got the JWT stored
under ‘JWT_KEY’, which is where we put it. And so that's all worked fine. So we've managed
to login, now we've got to use that JWT and send it back on the other requests. So that when
we do these things, we're not getting the 401 coming in there. So that's the next step to do.
And we do that with an Angular feature called an interceptor. And that means that rather than
having to set up the header containing the JWT, specifically on each one of the calls - so in
the BookReviewRepository, we'd have to set it up on this get and this get and the more calls
we did, the more fiddly that will get. But with an interceptor, we can just define a way on
every HTTP request to have some modification done to that request. In our case, putting in the
appropriate header. So that's what we're going to do next. So what I'm going to do is add a new
folder that I'll call ‘Interceptors’. And then in there, I'm going to have a new file, which I'm
going to call ‘jwt-authentication-interceptor.ts’. And then we can start putting our interceptor in
here. So what we're going to do is we're going to have this as injectable, so we just put
‘@Injectable’ on there to use that decorator. And then it's going to be an ‘export class’
and ‘JwtAuthenticationInterceptor’. And that implements an interface called ‘HttpInterceptor’.
And then let's implement that interface. So there, we've got the ‘intercept’ method, which
is going to do all the work. But then another thing we need to do is we're going to need to
use the AuthenticationService that we just wrote, which is where we get the JWT from. So we're going
to put on here a constructor that's going to have a ‘private authenticationService’
of type ‘AuthenticationService’. Okay, so that's what we've got in there. Now
we can see on this ‘intercept’ we have a couple of things happening. We've got the incoming
request, and then we've got the ‘next’ which is the handler. So the idea of these is, you may
potentially want to build up a chain of them, each of which does its own little bit to the
request. So the request comes in, we do whatever we're going to do with it, and then call the
‘next’ so that it can do its processing as well. So what we're going to do in here, we're going
to need to do a couple of things. Firstly, we've got to check that we're actually logged in at all,
because if you're not logged in, then obviously we can't send anything. So we're going to actually
have to put on to the AuthenticationService another property that is going to tell
us that. So what we're going to do here is very simply just have a ‘get isLoggedIn’. It's
going to return a ‘boolean’. And then all we're going to return is ‘!!this.jwt’. So that ‘!!’
is getting around the problem that the JWT is a string. Now although a string can be truthy and
falsy - that's to say a null string or an empty string is falsy - the type wouldn’t work for that.
So by doing ‘!!’, you can turn something that's truthy or falsy into something that's true or
false, and is therefore a Boolean. So there is our ‘isLoggedIn’. So back in the interceptor,
we're now going to say ‘const isLoggedIn =’ and then ‘this.authenticationService.isLoggedIn’.
Another thing we need to check is that we're actually sending this request to the appropriate
server that we're logged into. Because if this website got complicated, it could be using all
sorts of different web services, we don't want to be sending the JWT for one web service to a
different one. That would be disastrous in terms of security because they can then use that to get
into the other ones. So we're going to say ‘const isToServer’ and then in this one, we're going
to get off that request, so we can say ‘req.url’ and then ‘.startsWith’ and then from the
environment, we can get that ‘serverUrl’ again. And so that's checking that we're going to the
one that was configured for the requests. And so we can make sure, and then having done that, we're
now going to say, ‘if (isLoggedIn && isToServer)’. So we only want to be putting these in under both
those circumstances. And then what we have to do is we say ‘req = req.clone’. So we don't modify
the request directly, we actually make a copy of it. But then with this clone, we can define what
the modifications are. And what we're going to do is we're going to say ‘setHeaders’ and then what
we're going to set is the ‘Authorization’ header. So just the same as we saw we were setting in
Swagger or in Postman in the previous video. And then we say, remember, ‘Bearer ’ - exactly as we
did before - and then we put in the key, so we're going to say ‘AuthenticationService.jwt’. And
that will be the JWT that we've already set up. So that's modified it if necessary. And then
as I say, all we need to do is call the next one. So we simply say ‘return’ and then on that
‘next’ that's been passed in, we say ‘.handle’ and then just pass the request through. And
so that will do all the things we need to do. So that's our interceptor doing what needs to
be done. Last thing we got to do is register that interceptor. So in the ‘AppModule’ in here,
where we've got our providers… So we can see we've already got the RepositoryService. And then the
AuthenticationService, I should just point out, we did by just leaving that ‘providedIn: ‘root’’.
So you'd probably have wanted to be consistent, put it all the AppModule, but certainly for
this one, what we need to do in the AppModule is we say, ‘{ provide:’ and then there is a
predefined token, which is ‘HTTP_INTERCEPTORS’ and then we say ‘useClass’ and then the name
of the interceptor that we've just created, so our ‘JwtAuthenticationInterceptor’. The other
thing we put on here is ‘multi’ set to ‘true’. That's to basically say we can have multiple
injected objects for this token HTTP_INTERCEPTORS. Because remember, I said, you might want to have
multiple ones doing different bits of processing to your request. So because we said ‘multi’, we
could put multiple ones in there and they would be chained together in the order that they were
declared in this providers list, so if you want to do something like that. But having done all
that, that should now be working. So if we do a save … and we can actually see that is already
working, because remember, our JWT was stored in the session. We haven't actually closed
down the browser, and so we’ve still got that in there. But if I were to … well, the easiest
way is just to delete that one. And so now we'll see that we're getting the ‘Failed to retrieve
data’ there. Now if we log in, once again, again we've got that in there. And now we're getting
those reviews and those summaries. And if we just take a look at the Dev Tools in there,
and go to Network and just do another request, then we can see that ‘summary’ coming in there.
And if we click on that, scroll down to request, and we can see there, there's the Authorization
header that's been sent with that bearer token. So that's all of that basically working. We've
provided a login component, we're storing the JWT in session storage – we might store it in other
places, but that's what I've decided to go for here. And then we've used an interceptor so that
we can send that in the header when we're making a request, and it was picked up and you can see we
can get in there. Still got a few more things to do. We've got to do a bit of error handling. We've
got to have the ability to log out and things like that. But we'll look at those in the next video
where we can see some more enhanced features. So I hope you enjoyed that one. If you did, do click
like. Do subscribe and I'll see you next time.