Lecture thumbnail 0:01 / 40:25 Previously, when you tried to refresh the cities page after the token is expired,
we were unable to see the effect of token expiration, right?
But actually, I have tried to refresh the page after 10 to 15 minutes,
and this is the result as a part of that.
The JWT token is still present in the browser local storage,
and we are submitting the same as a part of the request.
But as a result, it returns 401 unauthorized response.
It was expired.
But this time delay in expiration of the token
may be because of the difference in the time based on the current working time zone.
OK, we can ignore it.
It should work properly on the production time.
Anyways, the progress for us is
We got the JWT token and refresh token from the application server
and storing the same in the local storage.
Now based on this refresh token, we should be able to get the new JWT token.
So we should make a POST request quoting these two tokens to the server,
and an endpoint on the server should receive these two tokens,
verify both tokens, and generate a new JWT token.
Let’s try such that.
Let us try to create a button within this page,
somewhere near this table, to get the new token.
To do so, go to your Cities component HTML file.
So at the end of this table container,
we are going to add a button called refresh.
And it’s a regular button, but not a submit button.
So button type is button, but not submit.
And apply in the CSS styles for the same.
And when the user clicks this,
I would like to call a method called refreshClicked.
OK, let’s try to create this method in the component.
Go to the Cities component TS file.
At the most bottom, add such method.
Return type is void.
Here we have to make a POST request.
Let me add such a method in the account service first.
So go to the account service in the services folder in the Angular application.
Just like login request.
Add a method called POST generateNewToken.
And there are no arguments.
But we have to get the token, I mean JWT token, from the local storage.
So these are the keys in the local storage.
Token as well as refresh token.
We are getting the same values.
And we are making a POST request
to the mentioned URL that is
localhost 7221 API version number account
slash
the path is
new JWT token
or generateNewToken.
So we have to create an endpoint at this path.
And the object that we are supplying is
it includes both token
as well as refresh token. Both.
So we have to receive this JWT token and refresh token
at this endpoint.
And the return type is any.
Because the return value includes with the new token and
the existing refresh token.
Now copy this path.
Generate new JWT token.
Go to the account controller.
In the account controller, apart from your POST login and POST register methods,
at the end, try to add one more action method.
And this method has to execute upon receiving the POST request
at this path.
So this is the path to which we are making the request.
And for this route path we are receiving the request.
And as a part of these arguments, we should be in a position to read
the token as well as refresh token from the client application.
Let me create a model class for the same.
So go to the DTO folder in the core project.
Add new item.
Select class.
Let it be named as relevant name, for example, token model.
So we have a token model.cs file in the DTO folder
in the core project.
In which we have two properties.
Token as well as refresh token.
Okay.
We will use this model class to receive the data in the account controller.
So switch back to the account controller.
We are receiving the token model object.
Which includes with both tokens.
Now we have to start validating those tokens.
If token model is null, we have to return a response
of bad request. Optionally error message.
Okay. Assuming the
token model is not equal to null, then we will proceed here.
Then we have to get both JWT token
which is also called as access token.
Yes, JWT token is also called as access token.
So we are reading the access token or JWT token
and also refresh token. And then
we have to read this JWT token and verify the user identity.
Because in the payload of the JWT token
already it contains the user ID, right?
So we have to extract the payload from this token and extract the user ID.
And we have to check whether that ID exists in the database.
Means is it a valid ID? We have to check.
In order to do so, let me create another method in the JWT service.
So go to the IJwtService.
Yes, we are shifting different files, but we are following
the logical flow of the data. First we have added essential code
in the cities component and then account-service.ts file.
And there we are making a request, right? So that
request is being received in the account controller at this endpoint.
And here we have to extract the user details
from this JWT token. That is why we are holding the code
here and trying to create a method in the JWT service.
And after that, we will be calling that method here.
So temporarily shift to the IJwtService interface.
I would like to create a method called
GetPrinciple
from JWT token. So it receives the JWT token
and it is going to return an object of the
ClientsPrinciple type. It comes from
system.security.clients namespace. The
ClientsPrinciple object represents the user details.
I mean, an user object based on the user claims
such as user ID, email address, etc.
So if you supply the token as argument for this method,
it has to read the user details from that and return the user object
based on that. So that is why the return type is
ClientsPrinciple. And now we have to implement this method.
Go to the JwtService.cs file.
Add such a method. The same method
signature copied here.
This time it is a public method
since we have to call the same from the account controller.
And also a small change. The token may be invalid
also sometimes. So in that case, we have to return null value.
So return type is null. Even in the interface
also, the return type is nullable of ClientsPrinciple
type. Okay, switch back to the JwtService.
Here the question is, how do you extract the token
and read the payload? Already we have
installed JWT bearer NuGet package. So it provides essential
classes for the same. First we have to define
what are the validations that you have to perform on this token.
To ensure that this token is a valid token, we have to check
some validation conditions. So what are they?
Let me create all of them as TokenValidationParameters.
The TokenValidationParameters class object represents
what validations that must be performed while extracting
the token details. Here, I would like to validate the
audience and what is the actual value of the audience.
You have to mention validAudienceEqualTo
In the configuration, we have
JWT section and there we have audience value.
I mean in the app settings, JWT
audience. This is the value we were referring there.
And also we have to verify this value exists
in the token as issuer. So continue writing
the code in the JWT service. Yes, we have to
validate the issuer. So
validate issuer equal to true.
And it is asking us what is the actual value of the valid
issuer. That is as defined in the configuration.
That’s all. We are writing the configuration value here.
And this value must be matched in the payload.
And also, yes, we have to validate the
signing key. I mean signature must be verified.
And to generate the signature, there must be a key, right?
So we have to supply the same.
IssuerSigningKey equal to. Yes, it is the
symmetric key. And we are getting the key from the configuration.
And converting
the same into byte array. And that byte array will be
supplied as a key value for this class.
So based on this class, the signature gets verified at runtime
as we have discussed earlier. And we
have to write an important property, that is validate
lifetime should be false here.
It is because we are going to call this method with an
expired token. So by the time of calling this method
the token was already expired. That is why we are calling this method first
of all to verify. So we should not validate
the lifetime, means expiration time one more time here.
We already assumed that the token is expired. Actually
it doesn’t matter whether it is expired or not. So we are not
validating the expiration time here, but just verifying the audience
value, issuer value, etc. before extracting
the actual data from payload. That’s fine.
But how can you read user details from an existing token?
For that purpose, you have a predefined class called
JwtSecurityTokenHandler. You can create an object
for the same and this object is responsible to read the
user object based on an existing JWT token.
Hey, JwtSecurityTokenHandler
validate the token
and if that token is valid, return an object of
ClaimsPrinciple class that represents the current working user as per
the token. Yes, I am giving you
the actual token value, that is the parameter that is received
here. And also we have to supply TokenValidationParameters
object that contains what properties have to
be validated and what not.
So that is the second parameter here, TokenValidationParameters
Okay, so far it is trying to
read the existing token based on these validation parameters
so it validates these said properties
and also signature as well as issuer
audience values and it doesn’t validate the
expiration date and returns an object of
ClaimsPrinciple type. And also
it returns one more thing as out parameter, that is
SecurityToken. This SecurityToken
out parameter represents an instance of JWTSecurityToken
class in case if the token is a valid token.
In case if the token is invalid, then
this out parameter represents an object of SecurityToken class
or maybe null when the token is invalid.
So in order to say that the token
is valid, there are a set of conditions. First of all
the SecurityToken out parameter value
in case if it is not an object of JWTSecurityToken class
then it indicates the token is invalid.
Okay, in case if this SecurityToken is
an instance of JWTSecurityToken class
then we have to typecast the same into
JWTSecurityToken and it is referenced here
This is the new reference variable. This is of
JWTSecurityToken type. It automatically
typecast by using pattern matching syntax in C-sharp.
And also we have to check another condition.
If the SecurityToken
contains a header and that contains a property called
algorithm, that is ALG and that
must be equal to HS256
In order to represent the same, instead of hard coding HS256
we are writing security algorithms.
HMAC256 and perform
case instance to comparison. Here HMAC
SHA256 is a constant and its value is
HS256 internally. And we are going
to check that whether this HS256 matches with this
algorithm of the token. Because if you notice
that JWT token, it contains the header and it
must and should contain ALG algorithm property and its
value by default and it contains name of the algorithm
which is used to generate the hash value for the signature
and it is by default HS256 in this case.
So in the actual token that we have received, if
algorithm equal to HS256, then this condition
is true. But in case if it is not, we are adding
not operator here. So if either of these conditions
is true, I mean if the token is not a JWT
token or if it is JWT token but
the algorithm doesn’t match with this HS256
if either of these is true, that means the token
is invalid. So we can throw some exception there.
The suitable exception type for this is
security token exception. It is a predefined exception here.
Some error message.
So if we have received a token
which is not a JWT token or if that JWT
token security algorithm is not matching with this HS256
in either of these cases, the condition will be
true. So we will throw an exception because it is not a valid token.
But in case if it is a valid token,
this principal object contains the actual user details.
Already the payload contains the user details. I mean
user ID, email address etc. So those
details are represented as claims principal. You can say that
the claims principal object represents or reflects
the user details from the payload of the token. So we can
simply return the same over here. Return the principal
object. Okay. So far
we are trying to validate the token.
If the token is valid, we will get the user details from this
principal. And if it is a JWT token
and also the security algorithm is HS256,
then only we will return the principal object. Otherwise, we will throw an exception.
This is the meaning of this get principal
method. Now switch back to the account
controller. Let’s call that method over here. Hey
JWT service, get principal object
based on this existing token. And you are going to return an object
of claims principal type, right?
Make sure you imported that namespace.
System.security.claims namespace.
And it is nullable. So we have
to check if the principal object is null here.
That indicates the user object is not found in the payload.
So we will return a bad request response to the client.
Okay. In case if
the principal object is not equal to null, then we will proceed here.
We have to extract the email address from this claims principal.
What about the payload properties that we have
added earlier while generating the token? All those payload properties
are available in this claims principal. One of them is
user ID, email address as well. So we are
trying to read. Hey principal,
find the first value of which type?
Claim types dot email address.
It is a predefined type and it returns the value
from the payload, the email address value.
And we are extracting the same here.
See, while we were generating the token
as a part of the JWT service,
see here, we have included some claims, right? One of them
is user email address. So we are going
to extract the same over there. Okay, sorry, it is
name identifier, right? We have added the same here.
The email address value is added as name identifier.
So let’s get back the same here. In the account
controller, claim types is name identifier.
So it contains the actual value of email as
added earlier. Then from the user manager,
we have to find the particular user based on
email address and use await to call it because
it is a synchronous method.
So it returns
an object of application user class and
it is nullable. So we have to check a condition here.
If the user object is equal to null, that means
the email address value that you find here is an invalid value.
Okay. In case if it is not equal to null, that
indicates the user object is found based on the email address. That is fine.
And then we have to check another condition. If the
refresh token value is matching or not.
Already we have stored the refresh token value in the users
table. So the same is reflected in the user object
over here. If this doesn’t match with the refresh token
that is received here as a part of the method,
that is from the token model,
we have refresh token, right?
This token model dot refresh token represents the refresh token
that was supplied as a part of the request.
And this user dot refresh token represents the refresh token which was
stored in the database table, in the AspNetUsers table.
So we have to check whether they match.
If they don’t match here, that is not equal
to otherwise or if the
refresh token expiration date time is already completed.
See the JWT token is already expired, I know.
But if the refresh token is also expired, the user
must be able to re-login through login page. So we don’t accept
it here. We cannot regenerate the JWT token
without re-login then. So we have to check
if that refresh token expiration date time is
already completed, that means less than or equal to the
current system time. If either of these
cases happen, then we have to return a bad request result.
See, if no user is found
at this email address which was read from the payload of the token,
that means if the user is null or if the
refresh token from the request doesn’t match with the one from the database
or if the refresh token is also already
expired, in either of these cases, we cannot
regenerate the JWT token based on the refresh token.
So we will return a bad request, optionally error message,
for example, invalid refresh token.
Okay, so if this condition doesn’t match,
means the user object is found and also
the refresh token value was found in the database table,
that is in AspNetUsers table, and also refresh token
is not still expired. In that case only we will
reach here. So here is the correct place for generating
a new token. Hey JWT service, create a new
JWT token based on the existing user object.
So the same process as performed earlier.
Just like at the time of login we have regenerated the JWT
token, the same process happens. So we will get
authentication response there. Of course,
there we are generating both JWT token and
refresh token as well. So we are getting both new tokens.
So that is why we have to re-update the refresh token
in the users table. In the user object, our
new refresh token equal to the newly generated
refresh token, and also its
expiration date time. And also, in order
to reflect these changes back to the actual database table,
we have to call, hey user manager,
update this user. Okay, then the database gets
updated. I mean, in the AspNetUsers table, for the
particular user, the refresh token as well as refresh token
expiration date time, both columns gets updated.
After that, we can return the authentication response
back to the client. So overall, if you
ask me to review the code one more time, we are receiving
a request at this path, verifying whether we have
received the refresh token as well as JWT token.
Okay, these two statements are unnecessary. We could
have write token model dot JWT
token directly. That’s fine. So based on this
JWT token, we are extracting the claims principle object,
which contains all the data from the payload that we have added earlier.
For example, we can expect that it should include
the person name and email address. Of course, this user
ID as well. Particularly, we are trying to read the email address
value from that, and we are trying to fetch
the data from AspNetUsers table based on the email address.
So if that email address is valid,
then we should get the actual user object into this variable, right?
So it should not be null, and also refresh
token should match, and also the refresh token should not be expired.
In case if the user object is null,
I mean if the email address is invalid, or the refresh token
doesn’t match with the one that is received from the request,
or the refresh token is expired already,
if either of these, then we will return a bad request
as response, that is 400 bad request.
But if everything is good, we will reach here,
and we will try to generate a new token, because we don’t
have any reason to reject the request.
After you call this, as a part of this method, we are regenerating both
tokens, both JWT token and a refresh token.
We are going to update the newly generated refresh token
over here in the database table, and then after that
we are giving the same as response.
So this is the process, and we are going to invoke this particular
endpoint at this address from account-service.ts
file. So in the Angular code
we are going to make a POST request, and trying to submit the token
as well as refresh token, both. That is fine.
Now we have to make sure whether we are calling this
POST generate a new token method in that button click.
So go to the cities-component.ts file. Here
we have to call that method. This method executes when the
refresh button is clicked, right?
Oops, here account-service is not injected. Let me inject the same
there. So in the constructor of the cities-component
let us try to inject the account-service.
And then
come back to the same method, refresh clicked.
Now we can access the account-service. Hey account-service
POST generate a new token method
and no need of any arguments. It takes the tokens automatically
from the local storage. Just try to subscribe to the same.
As usual, next, error
and complete. Always make sure you
mention the data type for all the parameters, otherwise you might get
compilation errors for the Angular code. If you don’t
know the data type, you can just write any data type.
So as a response, what are we trying to get?
Importantly, we are getting two tokens, that is the regular JWT
token as well as refresh token.
Just like we will get new tokens after login,
so in the same way, we will store them in the local
storage. That is, token equal to
the token from the response.
Here token represents the JWT token.
Actually, you should write the properties as defined
in the authentication response class. See here, token and
refresh token. So you can read the same.
Okay, we have successfully received
the new tokens and stored the same in the local storage,
overwriting the older tokens. And after that,
we have to refresh the cities data. So we can just call
loadCities method and as the code written in the loadCities
method earlier, it makes a fresh request
to api slash cities. See, this loadCities
method is trying to call getCities and we are making a fresh
get request to api slash cities. And this
time, as a part of this, we will use the newly generated
token. See, we are reading the token.
So this gets the newly generated token from the local storage, which
we have got after refreshing. That’s fine. Run this
application and see. So step one is first login.
Before that, clear all the
things from the application local storage and
login. So we have got the first token
and assume that after some time, this particular JWT
token got expired. So then we will be unable to read
the cities data. If you refresh the page,
assume the page is not working.
Still we have the same token, but the existing JWT token
is expired. Assume that. So
we are needing to get a new token. That is why we are just clicking
on refresh button over here. Oops,
we are getting 400 response. We
made a request to generate a new token,
but we are getting 400 response.
Let’s add a breakpoint there and
see what’s happening. In the account controller,
add a breakpoint in this generate a new JWT token method.
Click refresh. Yes, the breakpoint
hits. I mean the endpoint execution started.
The token model includes with the refresh token and the existing JWT
token both. Now the token model
is not equal to null, so we will not return a bad request
and we are able to read the claims principal object from the existing
token successfully. So it contains the
claims identity, that is the person name
and is authenticated equal to true
and we have got the JTI IAT
ID, audience and other properties
as you can see. So we are reading the email address.
Now this email address value is not fetched correctly.
Let us try to rewrite.
So we are not able to read this email address by using
name identifier. Let’s do one thing.
Switch back to JWT service. Let me add one more
identifier here. I mean another claim
that is claim types.email
and the actual user email value.
So this value gets included as a part of the JWT
token and we will be able to read this
email address value from the token. So in the account controller
the claim types.email, we should be able to
get the email address value. Run again.
First we will try to login.
Clear the local storage first to avoid confusion.
Click login with the right username and password.
So we have successfully logged in and we have got the token.
Remember this refresh token begins with SgM
and after refreshing we should be able to get a new refresh
token as well as a new JWT token.
Remember at least few characters of this JWT token.
For example it contains EYJZ.
OK. Click refresh button here.
Again we made a request to generate a new JWT token.
We have received the JWT token
as well as refresh token. And this time
we are able to read the email address value i.e. harsha at example.com
And based on that email address
we are able to get the user object from the database table
i.e. from AspNetUsers table. And then since the user
object is not equal to NULL and also the refresh token
matched with the database data and also refresh token
is not still expired. It is valid for 60 minutes.
So this condition is false. So this bad request is not
returned. And we are trying to generate a new token.
Yes, we have got the new token this time in this
authentication response. And that gets updated in the database
table. And return the same as response.
Yes. Now we have got a new token.
See this refresh token and
this JWT token differs from the previous one.
So if you notice the network tab, we made
a request to generate a new token. And it’s a POST request.
Response status code is 200.
And the response includes with the person name, email, as well as the
newly generated tokens. That is both
refresh token as well as newly generated JWT token.
And after that, since you called load cities
method in the Angular application, it is trying to make a request to
API slash cities with the newly generated token. So it
includes with the request headers, authorization
and it sends the newly generated token over here.
And it is valid. So that is why we have got the cities
data as response. So in the users perspective,
as soon as the token is expired, he can click on this refresh
button so he can get the new token.
But in the real world production scenarios, we can make the same
process as automatic. For example, as soon as the
user opens this cities page, even without clicking this button
also, the system has to automatically check whether that token
is already expired. In case if it is expired, based
on the refresh token, it has to regenerate a new token. So
the same process should be happened automatically without an explicit button
click. In this application, to avoid the confusion
to see the actual process of refreshing the token, we made
the user interface in this way, that is through an explicit button
click. In real world applications, the same can be processed
automatically, that is regeneration of the token
based on the refresh token as soon as it got expired.
You can do the same with a middleware, for example.
You can add a custom middleware and check the token. If the token
is expired, regenerate the same if the refresh token
is valid. Okay, we are not doing it here.
Overall, this is the way how do you handle the JWT
and refresh tokens to improve security and
handle authorization for the users.