Asp.net Core Authentication With JWT(Json Web Token) & Refresh Tokens

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
welcome to azureteach.net in this video let us discuss about jwt tokens and refresh tokens in asp.net core we are going to develop web api application which uses jwt tokens and refresh tokens we will use ef core for the database operations and swagger to test the code i am going to give in detailed explanation about everything that i am going to write let us check it out here i have created a new web api project using dot net file and added required nougat packages this package and this package are needed as we are going to use jwt authentication this package and this package are needed as we are going to use entity framework this is needed as we are going to use entity framework migrations this package is added by default or asp.net for web api project as it uses swagger by default i made this video into chapters if you already know jwt tokens and want to learn refresh tokens only skip this part of the video hover the mouse on the progress bar of the video and navigate to the refresh tokens chapter if you don't know what are jwt tokens or refresh tokens and why they are required i recommend you to watch this video you can find this video by clicking the i symbol that is there at the top right corner of this video i gave clear explanation on these tokens with few example scenarios you can find the workflow of these tokens as well in that video that is just a 10 minute video you can get good knowledge on these two concepts it is very hard to understand the code when we write chunk by chunk so i'll explain the code as i write and also after finishing the entire project let us do some entity framework related stuff first and create our tables here i'm adding a new folder and i'm naming this as context and inside this i'm adding a new class and naming this as application db context and inheriting this class from db context press ctrl dot over here we have to use this namespace and here i'm writing the constructor and it should accept db context options as we have to get the connection string from the startup class so here i'm writing options we have to pass this to the base class so here i'm writing base of options here i'm creating a new folder and naming it as entities inside this i'm adding a class user and in this i'm adding few fields public string username gate set publisher password get set and here i'm writing public int user id get set and i want to make this as a key column press control dot over here and we have to use data annotations namespace now add the entity framework related configuration inside the startup.cs class here i am writing services dot add db context and here i'm mentioning the class that we have created application db context press control dot over here and choose the required name space and inside this i'm writing options options dot use sql server press ctrl dot over here add this name space and to this we have to pass connection string we can get the connection string from configuration dot get connection string and do this we have to pass our app setting name so here i'm going to the app settings.json and here i'm writing connection strings and inside this i'm naming my connection string name as app db context and here we have to give the connection string i'm going to server explorer right clicking on the data connection add a connection and here i'm writing local db slash ms sql local db so it will list all the databases inside my local server i'm choosing demodb advanced and copying the connection string from here and pasting the connection string over here and now we have to give this name inside our startup.cs now let us add a db set related to users inside our context so here i'm writing public db set and i'm mentioning it as user press control dot and add required name space and i'm naming it as users get set now let us run the migrations so here i'm writing dot dotnet here migrations ad and i am naming my migration as initial create press enter it is completed and if you see here it created this migrations folder and added the initial create related migration and also the db context snapshot and now we have to run the update command to move these changes to the database here i'm writing dotnet ef database update it is done now let us verify it in the database it created a table inside the database let me insert a user insert into dbo dot users values i'm giving the username as test1 and password as password press f5 one row affected actually i don't prefer to use database for my tutorials unless it is needed as per the specifications of jwt refresh tokens must be preserved in data store it is the reason we are using database here in the channel there is another version of jwt tutorial which is more simple but has very clear explanation on the concepts and i have not used database there if you want to see that click on the i symbol for that video the i symbol is there at the top right corner of this video watch that video and come back here for refresh tokens part here i am creating a folder with name models and inside this i'm creating a modem i'm naming the model as auth request and inside this i'm adding public string username get set public string password get set and these are required so here i'm writing required press ctrl dot and add that annotations name space here also i'm adding the required attribute now let me add one more class over here and i'm naming it as auth response and here i'm adding a property public string token get set we are going to modify this model later for now let us have only one property that is token now let us add account controller right click on the controllers and add controller and here i am choosing apa api controller empty add i am naming my controller as account controller and here i'm adding an action public a action result i want to use async task of i action result auth token this should return the token here i'm adding from body and here i'm giving auth request press ctrl dot over here and add the models namespace auth request and inside this we are going to add jwt code to return the token for now let us return null here i'm adding http post and i want to go with the action name so here i'm writing action here i'm adding a new folder and i'm naming it as services and inside this i'm adding an interface and naming it as i jwt service and inside this i'm making this interface as public and i'm adding a method task of trink and i'm naming it as get token async and it should accept auth request press ctrl dot and add the name space auth request and now here i'm creating a class which implements this interface and i'm naming it as jwt service and here i am implementing i jwt service press control dot implement the interface now let us define our jwt config inside our app settings.json so here i'm writing jwt settings and inside this i'm writing key so i'm giving some random text over here i'm writing random key so as this is a demo i'm writing this but in real time we use this kind of keys which is non-human readable and hard to remember this key should not be disclosed to anybody now in the jwd service class we have to validate the user credentials those are passed inside this auth request and if the credentials are valid then we have to use the key that is configured in the app settings.json to generate the auth token so as we have to validate the users we need to use the database context so here i'm writing private read-only application db context press control dot over here and here i am writing context as the variable name and also as we have to read the key from the app settings.json here i'm writing private read only i configuration press ctrl dot and add the required name space and here i'm writing configuration now let us generate a constructor to accept these two select this right click quick actions and refactoring generate a constructor here we have to validate the user credentials so here i'm writing various is equal to underscore context dot uses dot first or default x x dot user name dot equals auth request dot username and x dot password dot equals earth request dot password if user equals equal to null that means the credentials are wrong we are returning task dot from result of string and inside this and passing null so as we have decided to use async over here here i am writing a sync and we can await on this task we have to read the key that we have defined in the app settings.json here so here i am writing jwt key is equal to underscore configuration dot get value of string and do this we have to pass the path so i'm going to app settings.json and copying this pasting it over here and going here again and copying this key and pasting it over here and we have to convert this key to the bytes as we have to use the bytes when we are generating the token so here i am writing where key bytes is equal to encoding dot ascii dot get bytes and here i'm passing jwd key from here i'll write the code first and after that i'll explain so here i'm writing where token handler is equal to new jwt security token handler so this is the class that will drive the entire show of the tokens so i'm pressing ctrl dot over here and adding the required namespace and after that to generate the token we have to pass the required parameters so we can pass it using a security token descriptor so here i'm writing where descriptor is equal to new security token descriptor press ctrl dot and add the required namespace and here we have to pass the subject subject means for which user we are going to create and what kind of roles the user has like if you want to pass the user id you can pass it over here or if you want to pass the claims like admin business user you can pass it over here if you want to pass the mail id you can do that over here so here it is a claims identity let me add this namespace at top because we have to use it later also so here i'm adding using now this accepts a array of claims so here i'm writing new claim and here i'm writing new claim and the claim type dot name identifier and passing user dot username and if you want to pass any other claims related to the rule you can pass it as with claim types dot rule you can pass if the user is admin or a business user but for this demo we are just passing the username only here we have to give the expiration time so i am giving expires equal to day time dot utc now dot add seconds of 60 so we can use add minutes or i had hours also but for this demo i want to use the add seconds and the date time must be in the utc format now here we have to add the algorithm that we want to use to encrypt the token so here i'm writing sending credentials is equal to new defining credential and to this we have to pass the metric security key and it expects the bytes so we already got the key bytes over here and we can pass it over here and i want to use the algorithm as security algorithms dot hmesc sha-256 now we have to generate the token using the token handler so here i'm writing web token is equal to token handler dot create token and here we are passing the descriptor and here we are returning await task dot from result and inside this we are writing token handler dot right token to this we have to pass the token so let us discuss what's going on here so here we got the key and we converted that to the bytes and we have to generate the token based on certain parameters so we have to define the parameters in some object and that object is security token descriptor and here we have defined the parameters those are needed to generate the token so we have defined subject expires and signing credentials and after that here we are calling this create token method that will give us the dotnet object and after that here we are calling right token that will serialize the dotnet object and returns the token as a string once after generating the token i'll show you the decoded token then you can understand all these parameters easily now let us add the dependency mapping of the service and interface over here so i am writing services dot add transient and here i am writing i jwt service press ctrl dot and add name space jwt server now i'm going to jwt service and copying this code as we need this code there as well i know i'm duplicating the code but please bear with me i'm a bit lazy so here i'm adding that code and adding the name space for encoding class and we have the configuration object over here and here we have to define the token validation parameters in the startup.cs class so that the framework will use these parameters to validate the token so here i'm writing to invalidation parameters press control dot over here and choose the namespace token validation parameter is equal to new token validation parameters and we have to pass certain details over here so here we have to pass issuer sign key is equal to new symmetric security key and to this we have to pass the bytes we will write almost similar code over here so we are writing key bytes and after that here validate lifetime so we have to validate the type lifetime so i'm writing the true and validate audience so we don't have any audience so i'm giving the false audience means the recipients of the token so if you use any dedicated permissions this is required like if you don't know the delegated permissions in my channel there is a video on the dedicated permissions of azure graph api you can see that so here i'm writing validate issuer is equal to false so why this is required like if you use any as your kind of environment there is a strong chance that the same token can be generated to multiple tenant people so just to distinguish that we can define an issuer and we can use it but here we don't have that kind of architecture so i'm just giving this validate issuer as false and here i'm writing clocks queue is equal to time span dot 0 why this is required by default if the token is expired jwt adds 5 minutes grace period i do not want that grace period so i am giving this as zero and we need this token validation parameters later so that's the reason i want to bind it to the services so here i'm writing services dot add singleton and i'm passing token validation over here and now we have to tell the framework that we need authentication so here i'm writing services dot add authentication and here we are passing all options and inside this we are mentioning auth options dot default authentication scheme that is jwt bearer defaults dot authentication scheme and earth options dot default challenge scheme it is also the same awt bearer defaults dot authentication scheme and here we have configure the jwt bearer so here we are writing the add jw tube error and here we are writing jwt options and inside this we are writing awt options dot token validation parameters is equal to the token validation parameters that we have created above and after that here we have to write app dot use authentication now let us go to account controller and here let us add ijwt service press control dot over here it should be private read only as we are going to get this as a dependency underscore jwt service select this right click quick action send refactoring generate constructor and inside this controller where token is equal to underscore jwt service dot get token asset and today's we have to pass out request this is an async method so here we are writing await and after that here if token is equal to equal to null we are returning unauthorized otherwise here we are returning okay of new auth response and inside this we are passing the token so now our setup is ready but still we have to make few more changes related to swagger in the startup class but before that let us run this project and see what it is showing run this it opened us the swagger ui so here i'm clicking on try it out and here our user name is test1 and password is password so here i'm writing test1 and password as password clicking on execute it is giving an error that the key size should be 128 bits so let us change our key size over here go to app settings dot json and here random non-human readable key so that the bit size will be increased so i'm running this again now i'm clicking here and clicking on try it out and here the username is test1 and password is password click on execute so we got the token let us decode this token i'm copying this and i have opened jwt dot io here i am pasting every jwt token will have three parts the first part will be called as the header and the second part will be called as the payload and the third part is called as the signature so if you see on the right hand side in the header part we have the algorithm that is used to create this token and the type of token that's the jwt token and in the payload here we have the name id that's the test one that's the username because when we have created the token descriptor we gave the name id in the name id in the claims here we give the token descriptor so here we have mentioned the claims and also here we have defined the expiration time and here we give the algorithm so based on all these things the token is created and the same was decoded and here we have not before time here it is showing some number over here but this is actually a timestamp this is number of seconds after 1970 january 1st so that's how the jwt specifications were defined so here we have the expiration time so expiration time is 60 seconds for us we did not give any not before time in our token descriptor so that's the reason here it took the current timestamp so if you see there are 60 seconds gap between these two numbers and here we have issued a time that's the current timestamp and the third part is verify signature so our application uses this kind of logic to decode the token and validate it now let us discuss with a real-time token here i have pasted a token from azure if you don't know how to create this kind of token you can see one of my tutorials where i have used postman to fetch token of azure so you can view that video so here if you see here we have more parameters over here like it uses rsa256 algorithm and type and there are so many other things like audience audiences this id this is something specific to azure and here we have the issuer so this is the url of the azure this is the r server which issued this token and we have issued a time not before time and expiration and there are so many other things like if you see here we have the email id and also the family name given many other things are there so it depends on the requirement what kind of values or the claims that you want to keep inside the jwt token if you see on the swagger we don't have any option to use authorization so for this we have to add some logic inside the startup class here i'm adding a new controller and and giving the name is namescontroller and here i'm adding an action public async task of i action result get names and here i'm writing where names is equal to await task dot from result new list of string and here i'm giving some names like adam robert and here i'm returning ok of names and i'm giving http get over here and i want to go with the action name so i'm giving it like this and here i'm decorating it with authorize attribute press ctrl dot add the name space and now go to startup.cs class here we have to add swagger related configuration so here i'll write the code you will understand this when i run the solution so please bear with me so here i am writing c dot add security definition here i'm writing c dot add security definition and here we are going to use barrel token and here i'm writing open apa security scheme here we have to pass few parameters so here i'm writing description so i'm writing this site uses better tokens and you have to pass it as bearer space token we will understand this when i run the solution i am giving the name as authorization [Music] and in so the parameter has to be passed in parameter location.header and the type type is security scheme type dot api key and after that here we are mentioning the scheme so that is bearer token and after that here we have to add one more thing that is p dot add security requirement and here we have to pass new open api security requirement and do this we have to pass new open api scheme open api security scheme and inside this we have to pass reference is equal to new open apa reference and inside this we have to pass type equal to reference type dot security scheme and id equal to where you will understand this when i run the solution better and here we have to enclose this as a dictionary i have to use the dictionary kind of syntax over here and after that here we need to write scheme is equal to r2 and after that here i'm giving the name as bearer you can get this information from the swagger documentation also in is equal to parameter location.header and here i'm just passing new list of string let me format this code so what's going on here is here we are telling swagger that we want to use authorization based on this definition and based on this configuration the swagger will use the token that is passed from the ui and it will give the token to our controller before we run this code i noticed two type errors over here it should be small and here it is misspelled it should be authorization now save it and run it it opened the swagger ui and if you see here we have the authorize button so if we click on this it opens this popup and here if you see here we have this information and this information we have defined inside the startup class security definition so here we have defined that information but of course here we give less than space greater than but it seems like swagger has an issue with the encoding and it remove this part now close this popup click on post click on try it out and give the username test1 and the password password and generate the token so we got the token over here copy it and go to authorize and here paste it click on authorize close and go to our get names method and try it out if you click on execute it gives the result what is refresh token and why do we need it as the name states refresh token is another type of token that is used to regenerate the expired access token let us understand this with the help of this diagram here the user sends user name and password to the authorization server and the authorization server returns jwt token and refresh token and the client uses the jwt token and it access the resource server in subsequent requests but once the token is expired the client sends expired token and refresh token to the authorization server server validates those details and returns back new token and refresh token here the new token is a jwt token and a refresh token can be any guid or any encrypted random number so here guid is not preferred way for the refresh token because it is easy to decode as per the jwt specifications refresh tokens must be preserved inside the database so that it will be easier to validate so here in the entity folder i am creating a new class and i am naming it as user refresh token and i am decorating this class with table attribute because when we run the migrations this class will be converted into a table with a pluralized name and i do not want it to happen it already happened with the user table that we have created but of course we can know that because there is an object inside the sql server with username so that is okay but here i do not want to create a pluralized table for this user refresh token here i am adding a property public end user refresh token id set it's a key column so here i'm decorating this with key column press ctrl dot and add the name space and inside this i'm writing public string token gate set and also we have to preserve the refresh token refresh token get set and it should have a created public date time created date get set here i'm adding one more property public date time expiration date because we have to preserve the expression date also and after that here i'm writing bool is active this should be a non-mapped column that means we do not want to move this to the database but this is a kind of read-only property so how we are deciding on this is return expiration date is less than date time dot utc now so if it is less than correct date time that means that is not expired here we have to preserve the ip address also that is easier to validate the authenticity of the user so here i am writing public string ip address get set and after that here i'm writing public bool is invalidated because if the token is generated and it is not expected but assume there are some situations where we want to deactivate this token we can use this flag and here i am writing public and user id as this class need a foreign key relationship with the user table here i'm writing public virtual user user and here i'm writing get set and this is a kind of reverse navigation that we may use if needed so here as this is a foreign key relationship i am decorating it with the foreign key and inside this i am mentioning the foreign key caller name that is the user id i have zoomed out the visual studio if you want to see all the properties in one screen you can view it now now we have to add this relationship in the user table also so here i am writing public list of user token or the user refresh token user refresh tokens get set and also we have to add this in the db context so here i am writing public db set user refresh token user refresh tokens get set now let us run the migrations here i'm writing dot net ef migrations add refresh token migration i'm giving the name as refresh token migration press enter the migrations are completed let us see if the file is generated or not so here if you see the file it's generated with the newly created table details and now we have to run the database update to move all these changes to the database so here i'm writing dot net ef database update it is completed here the table is created now we have to make few changes in the jwt service and also in the controller because earlier we were just returning jwt token as a string but now we are going to return an object that consists of jpwt token and refresh token let us do that here in the earth response model i am adding a new property public string refresh token as we have to return the refresh token as well on auth response now we have to modify our interface in the jwt service to return the auth response so i'm making a change over here auth response and also here also we have to make the change of response here we have to return the auth response and here i'm making few changes because i want to refactor this code we need this code for generating the refresh token also so here i am writing string token string is equal to i'm removing this task from result and here i'm writing written new auth response and for this i'm passing token as token string of course we have to make few more changes over here i just want to extract this token generation because it is needed for the refresh token also so here i am right clicking quick actions and refactoring extract method and i am naming this as generate token apply from this object we are just using the username so i am making a change over here string username and here we are passing username why we have written like this because it makes easy to write the generate refresh token method for now let us keep it as is so here i am writing username now this logic is almost similar to the logic that we have written before now here we have to generate the refresh token refresh token can be any guid or a random encrypted number i'd prefer using a random encrypted number convert that to a base64 string so here i'm writing private string generate refresh token and inside this i want to generate a 64 byte random encrypted number so here i am declaring byte r is equal to new byte of array of 64. it is very easy to generate a random number so for that we have to use a random encryption provider that is rng to service provider so here i'm writing crypto provider is equal to new rng crypto service provider press ctrl dot and add the name space and inside this we can call crypto provider dot get bytes to this we can pass byte array and now we have to convert the byte array to the base64 string this method will fill this empty byte array with random numbers and here we are converting that to a base64 string so here i'm writing return convert to base64 string and to this we are passing byte array here we have to call the new method so here i'm writing string refresh token is equal to generator refresh token and after that we have to preserve these details into the database so here i'm writing user refresh token is equal to new user refresh token and to this we have to pass few required parameters that's the created date here i'm writing date time dot utc now and after that we have to pass the expiration date i'm hard coding the minutes as five minutes but if you want to give days months years that is absolutely fine it depends on the requirement for this demo i am giving five minutes and after that here ip address this we have to get from the controller so for now let us keep this as empty string and here is invalidated so it should be false and refresh token so here we can get the refresh token from the variable that we got over there and after that token we can give token string and after that here we have the user id we already got the user object over here so we can give user dot user id and after that we have to save this to the database so here i'm writing await underscore context dot user refresh tokens dot add async and to this i am passing user refresh token and after that we have to call await underscore context dot save changes async here we have to pass the refresh token inside the auth response also here i'm writing refresh token and we already got the refresh token in that variable over here here we have to get the ip address and we can get it from the controller so for that reason here i'm writing string ip address as a parameter so here i'm removing this empty string and writing ip address and we have to modify the interface as well so here i'm writing string ip address now we have to modify the controller to pass the ip address so we can get the ip address from the http context dot connection dot remote ip address dot tostring and after that here i'm just renaming it so instead of token i'm writing auth response and we are just returning the auth response from here here i am renaming it earth response and here also instead of returning the new auth response we can use the returned value of the service before we run the solution let us go over here and increase this timeout to 180 seconds that is three minutes because 60 seconds is too less and it will be timed out soon so that's the reason we made this as 180 seconds and let us run the setup and test our code now i am clicking on this method and try it out and here i am giving the username as test1 and password as password and i am clicking on the execute we should get two tokens refresh token and jwt token and we got both of these tokens let us test our logic so i am copying this token and let us test it if we could access this method without passing any token i'm clicking on try it out clicking on execute so it gave us the 401 error now go here and click on authorize and write and paste the token over here click on authorize close this pop-up now go over here and let us click on the execute so we should get the response so if you see we got the 200 response now the jwt workflow is fine now if you see the sql server the refresh token details got inserted successfully and here we have the ip addresses colon colon one because we are running on the local host that is the reason we got colon colon one as the ip address now we have to write code for the refresh token so as we have to accept expert token and also the refresh token when we are regenerating the tokens let me define a model over here i am naming my model as refresh talk and request and inside this i am adding public string token or expire token and public string refresh token and these two are required so i am decorating this with required data annotation and here also i am doing the same required and in this method we did not validate the models and here i'm writing if not model state dot is valid written bad request of new auth response so here we have to pass the reason why we are failing and also whether this response is success or not of course by the status quo the user can get the answer whether the request is success or not but we have to pass a reason code so that they can easily understand so inside the auth response i'm adding few more parameters public bool e-success get set and here i'm writing public string reason get set and here inside this i'm writing is success equal to false and reason as user name and password must be provided so so this is the validation for the auth token so let us write the action for generating the refresh token so here i'm writing public async task of a action result and here i'm writing refresh token so we have to get the model from the body so i am writing from body and here i'm writing refresh token request request and i'm decorating this method with http post and i want to go with the action name that we have so here i'm writing action and here we have to do some validation because we can get the refresh token request from the actual client or if somebody get a hold of those refresh tokens even they can send a refresh token request so we have some validation logic so here we have to do some validations so first let us check if the model is valid or not if not model state dot is valid so here i'm returning written bad request and inside this i'm returning new auth response e-success is equal to false and after that here reason tokens must be provided tokens must be provided and after that here we have to get the token so here i'm writing token so we have to convert the expired token to the jwt token so that we can get the expiration time of the actual token and we can validate it whether it is expired or not if the token itself is not expired it is not needed to generate the refresh token right so for that reason here we are going to the controller and here i am writing get jwt token to this i'm passing request dot expired token so let me press ctrl dot over here and generate a method so inside this we can get the token from the jwt security token handler press control dot over here token handler and equal to new jwt security token handler and here we are returning written token handler dot read jwt token and to this we can pass the expired token this will convert the expired token into the jwt token by which we can get the expiration time so here i'm writing string and also here we have to validate these details again is the database details that we have stored because we have to validate whether these details are present inside the database or not and also whether the request is coming from the same ip address or not so we have to connect to the database so here i'm writing private read only application db context press control dot underscore context select this press ctrl dot and here add parameters to the constructor so it added this adding the context inside the controller is not a recommended way this is not a good practice but i do not want to make this demo complicated so here i am writing the context and here we have to get the stored user refresh token so here i'm writing user refresh token is equal to underscore context dot user refresh tokens dot first or default and here we have to check x dot is invalidated equal to false ampersand ampersand x dot token is equal to request dot token that's expired token ampersand nine percent x dot refresh token equal to equal to request dot refresh token and also ampersand ampersand x dot ib address equal to equal to http context context.connection.remoteipaddress.2string here we have an issue it should written jwt security token because here we are taking the expired token which is a string as an input and reading that using token handler and converting that to jdbt security token and here i am writing auth response response is equal to validate details and to this method i want to pass token and also user refresh token inside this method we are going to do few validations so that's the reason we are generating this method and here we have a few validations if user refresh token is equal to equal to null then we will be returning new auth response of e-success is equal to false and reason is equal to actually these details are not present inside our database when user refresh token equal to null but will written a generic message invalid token details sometimes it is not good to written the exact error message so that's the reason i am giving the reason as invalid token details and after that here i am writing f token dot valid 2 is greater than date time dot utc now that means the token is not expired so there is no point of generating a new refresh token so here we are at written new heart response each success is equal to false and reason token not expired and after that here we are checking if not user refresh token dot is active that means this token expires so how we are calculating this if we go over here here we are checking expiration date is less than date time dot utc now that means this token the refresh token is expired so here we will be returning written new auth response of e-success is equal to false and reason is equal to refresh token expired otherwise we will be returning written new or response a success is equal to true and in here if response dot is success equal to false that means here we can check not response dot e success then we will be returning written bad request of response that we got otherwise we have to generate the refresh token over here it is entire service does not have a method to generate the refresh token so here i'm adding a new method inside i jwt server auth response get refresh token so to this as we are going to generate a new refresh token and access token and also as per our table structure that is user refresh tokens it should have ip address so inside this method we are going to generate both access token and also the refresh token so for generating the access token we need the user id so here i'm writing it refresh token async and it should accept ip address because we want to preserve the ip address for which this refresh token is generated and also in user id and also string username so here this ip address and user id these two are required as we are going to save these details into the database and this username is required because inside the jwt service when we are generating the token we need the username so here i am saving this and going to the jwt service and i'm pressing ctrl dot over here so that it will generate the method and here i'm writing where refresh token is equal to get generator refresh token and after that here i'm writing actually this is an async method so here i'm writing async in the refresh token response we have to return access token also so here i'm writing access token is equal to generate token and do this we need username because we are saving the username inside tokens name claim so if you go to this method here if you see we are saving the name identifier as username let us go back to our method and here we have to preserve these details in the database so we already have that code over here so i'm copying all this information right click quick actions and refactoring extract method and i'm naming this as save token details and clicking on apply but here if you see from this user object we are just using user id only so i'm just changing this method signature and here i'm writing into user id and here instead of user dot user id writing user id so this is the same code but we just refactored it here i'm writing user dot user id instead of passing the entire user object because it makes simple to use that logic over here because here we have the user id so here i'm writing written await they have token details and to this we need ip address and user id and also token string that is access token and also refresh token now inside the controller we have to use this so i'm going to the controller here we have to invalidate the previous token details so here i am writing user refresh token dot is invalidated is equal to true underscore context dot user refresh tokens dot update and to this i am passing the user refresh token and after that we have to call underscore context dot save changes async and here we have to use await here we have to call generate refresh token method of jbt service so here i'm creating where response is equal to underscore jwt service dot generate refresh token async and here we need ip address so let us write await over here and we have to get the ip address but here also we need the ip address so i'm just copying this and i'm pasting it over here string ip address is equal to and after that here i'm writing ip address and here i'm passing the ip address and after that we need the user id we can get the user id from user refresh token so we got the user id and after that we have to pass the username and the username is there inside the name claim of the jwt token so here we already got the jwt token so here i am writing where user name is equal to token dot claims dot first or default and here i'm writing x dot type is equal to equal to claim types as control dot dot name identifier and here we will get the claim object and from that we have to take the value and here i am passing the username and after that we have to pass this response so here we already have a response so here i am renaming it as authrespond and here we are returning return ok of auth response before we run the solution let us make few changes and also fix few issues so here we have the generate token method and i want to reduce the token expiration time to 90 seconds and also here in the same token details method we are not returning the success flag so i'm writing is success is equal to true and one more thing is inside this user refresh token we have an issue so here it should be greater than if expiration date is greater than utc now then it is active but earlier we have written less than that's an issue and inside the controller here we should get this information by comparing to jwt registered claim names and here we have to look for name id now let us run the solution and here i am clicking on this art token method clicking on try it out and here i'm giving the username as test1 and password as password and clicking on exit we got the token details and copying this token and going over here clicking on the authorize button and here i'm writing bearer space token click on authorize click on close go over here get names method click on try it out and click on execute so here we got the response now let us try if we can get the refresh token before the expiration of the actual token so here i am clicking on refresh token try it out and here i am pasting the expired token as the token that is not expired actually so here i'm copying the refresh token that we have over here and going over here and pasting it over here and clicking on execute now if you see here we got the error response stating token not expect that means the actual token is not expected let us wait for some time and after that let us hit this action once again now after some time i am clicking on the execute button again so here if you see we got a new token and also a refresh token let us try if this token works by copying this here and go here and click on authorize logout and here write bearer space token and here click on authorize click on close now here i'm going to the get names method and here i'm clicking on execute and we got the success response now let us wait for some time until the refresh token is expired that is five minutes now both refresh token and access token are expired let us try and see if we could use these tokens to generate the refresh token so i am copying the access token and going over here so here we are inside the refresh token action i'm copying the refresh token and pasting it over here and now here i am clicking on execute so we should get an error stating this token is expired so here we got the error refresh token expired now let us run this solution again i'm clicking on the auth token try it out and here i'm giving the username as test1 and password as password and clicking on execute so we got the token over here now let us go to the database here i am selecting the user refresh tokens and i want to change the ip address of this token so here i'm writing update user refresh token set ip address is equal to 194.0.0.1 why i'm making change because assume somebody has stolen the refresh token and also the access token so we are doing some validation right so to test that i am updating the ip address over here because we are running this application in local machine so always will get that colon colon one so here i'm writing where user refresh token id is equal to nine and i am updating it so this is updated let us wait until the access token is expired now these details are expired so let us try getting the refresh token i'm copying the actual token over here and going to the refresh token method and here i'm clicking on try it out so here in the expired token i have pasted this and i am copying the refresh token so now the request will go from colon colon one but actually these tokens were generated to 194.0.0.1 so in this case we can assume like this token is stolen by someone who is stating it colon colon1 ip address now here i am clicking on execute button so here if you see we got invalid token details response this code looking great but it still have one issue and to fix that issue we have to use custom durability token validation because if you see for the refresh token we are doing the validation for the ip address but for the actual access token we are not doing that the actual access tokens validation will be done automatically by this token validation parameters where it is validate the signature of the token using the key and also it will validate the lifetime of the token but here we don't have any code to validate the ip address of the access token so let me demonstrate that issue i am running the solution and clicking on auth token and clicking on try it out here i'm giving the username as test1 and password as password and clicking on execute and here i am running the select statement and here i am updating the ip address for the latest record so now we can assume like this token was generated to this ip address now in here i'm copying this token and i'm going here clicking on authorize and here i'm writing bearers place token click on authorize click on close so here i am raising this request from colon colon one ip address that's the local host if i click on execute we got the success response that means if somebody stolen your access token still your api is giving the response that's a security issue we have to validate the ip address for the access token also now let us add validation for the ip address for the access token so for that we can use the jetability bearer event so here i am writing jwt options dot events equal to new jwt bearer events and after that jwt options dot events dot on token validated it's a callback and also here we have to pass the lambda expression so here i'm writing a sync context and after that the lambda expression and here we have to get the ip address so what happens here is it will do the automatic validation by these parameters like the expiration time and also the signature of the token and after that it will call this method so here we can get the ip address from which this request came so here i'm writing ip address is equal to context dot request dot http context dot connection dot remote ip address dot to string now we have to validate this ip address again is the ip address to which this is this token is issued and we have that ip address in the database so here inside the jwt service i am writing task bool is token valid and to this i am accepting access token and also ib address so after that i'm going to the jwt service and i'm implementing this method i'm pressing ctrl dot over here so here it added the method here i'm writing where is valid is equal to underscore context dot user refresh tokens dot poster default and here i'm passing x dot token is equal to equal to access token ampersand ampersand x dot ip address is equal to equal to ip address so if we get the object over here that means this token is came from the actual ip address which we have saved so here i'm writing not equal to null so if it is not equal to null that means the token is valid and here i'm returning return await task dot from result is valid and here we have to add a c now here in the startup class let us use that method so for that we have to get igbt service over here so here i'm writing where ability service is equal to context dot request dot http context dot request services dot get service and here i'm writing i jwt service and also here we can get the jwt token so i am writing jwt token is equal to context dot security token as jwt security token press control dot and add the name space and here we are calling if jwd service dot is token valid so to this we are passing jwt token dot raw data and after that here we are passing the ip address if the token is invalid that means we have to put exclamation over here so we are returning context dot fail invalid token details and now let us run the solution now i am clicking on the auth token try it out and here i am giving the username as test1 and password as and i am clicking on execute so here we got the token detail now in the sql server let us update the ip address for the latest token that is 16 so we have the ip address as colon colon one because we made this request from the local host now we are updating it to 194.0.0.1 so what we are doing here is we are making this record to look like this record was created for this ip address now in the swagger ui i'm copying this token and now we are going to issue a request from the local host because this application is running in the local host but the token is created at 194.0.0.1 and clicking on the authorize click on close for this pop-up and now if we go to the get names and click on execute it should show us an error so i'm clicking on execute now if you see we got invalid token error before we wrap it up let me explain the entire flow so here we have an action auth token this issues access token and refresh token and it accepts auth request and the user must pass username and password and it will validate the modal state and if the modal state is invalid we are just returning this error message otherwise we are calling jwt service dot get token async and here inside this we are validating the username and password details if the user does not exist in the database then we are returning null over here and if it is null then here we are returning unauthorized and after that here we are generating the token so to generate the token we need the username so here we are passing the username so to generate the token we need a key so inside appsettings.json we have defined a key over here this can be any key so using this key we are getting the key bytes and after that here we have created the token descriptor and here we are telling the token has to be created with this name identifier and the name identifier is username and here we give the expiration time so this has all the parameters to create the jwd and after that here we are creating the token and after that we are serializing the token object to string and we are returning here and after that we have to generate a refresh token so here we are calling this generate refresh token method generate refresh token method generates a refresh token using rng crypto service provider this can be any random number here we are generating 64 byte random number and we are converting that to a base64 encoded string and after that here we are calling the same token details method so inside the same token details method we are creating user token object and we are saving that into the database and we are returning this response and after that when we get the token here inside the startup class here we have added the authentication details and also we have defined the token validation parameters over here and here we have added jwt bearer options and here we have passed the token validation parameters and also we are doing custom validation using the jwt events and for generating the refresh token here inside the account controller we have the refresh token so here we will get the refresh token request for refresh token we have to get the expired token and the refresh token so here we are checking the modal state if it is valid we are returning this error message otherwise we are fetching the ib address from the http context and also here we are getting the access token from the token that is passed inside the refresh token request so we are using jwt security token handler and we are converting the string to the jwt token and after that here we are getting the refresh token object that we have saved inside the save token details method so after getting that we are validating the token details so inside the validate token details here we are checking if the user refresh token is equal to null that means this token itself is not present in the database so here we are returning invalid token details and here we are checking if the existing token is expired or not if it is not expired we are returning this error message token not expired and here we are checking if the refresh token is active or not if it is not active then we are returning refresh token exped and here we are returning the validation successful and inside this method if the validation is failed then we are returning the bad request otherwise we are making the existing token invalid so we are calling is invalidated is equal to true and why this is required so if you want to invalidate any token you can use that assume the user is logged out of the machine then you can use this kind of flow and after that here we are updating the database for the token and we are setting is invalidated equal to true inside the database and after that here we are fetching the username from the token claims and here we are calling get refresh token async and for the refresh token also we have to get the refresh token and the access token so here we are generating those two tokens and we are saving those details to the database and returning the response it took almost a day to create this tutorial so if you feel this video is helpful to you please like this video and also subscribe to my channel thank you
Info
Channel: AzureTeach•Net
Views: 14,084
Rating: undefined out of 5
Keywords: asp.net core token authentication and authorization using jwt, asp.net core jwt token authentication, asp.net core refresh token, refresh token asp.net core web api, asp.net core web api token based authentication, refresh token jwt asp.net core, jwt asp.net core web api, jwt asp.net core 5, swagger in asp.net core web api, swagger authorize bearer token, swagger authentication, refresh token asp.net core, refresh token vs access token jwt, asp.net core jwt token and refresh token
Id: 8ni7Xg_UxBs
Channel Id: undefined
Length: 76min 16sec (4576 seconds)
Published: Mon Dec 13 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.