Building an Ionic JWT Refresh Token Flow

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] what's up simonix welcome back to a new tutorial today we want to implement a token refresh flow with ionic because the topic is kind of complicated i already created a simple server upfront that you can also use for your dunny application and i also added a bit of code to our starting point as always you can find the full code linked below the video so perhaps directly open the devtectic tutorial side by side with this video soaker you can get some of the snippets already and we can talk more extensively about the really important parts let's get into it what we want to do is we want to create something so um to first of all log in with the user or register then get a json web token back after login but actually at the same time also get an access and a refresh token the access token will be used to add to the authorization header of our calls to identify against the backend for authorization but this token only looks like perhaps a minute two minutes five minutes any short duration on the other hand we got the refresh token which has a longer lifetime like some days perhaps a month or even a year and once the excess token expires we want to automatically use the refresh token to get a new access token from the server that's the whole idea now to do this i created a blank new application and i generated two pages so we can have a simple login with an inside area we of course also need a service and we will also add a little guard for security in the end uh we don't want to put too much focus on this but i think we should mention it so let's go through the steps that i already did so far so we can uh do everything here a bit faster first of all i added my api url as i said i hosted this on heroku if you also want to see the back end part which was written with an sjs it's not perfect but let me know in the comments so i can maybe create another tutorial on that part as well so that is the api url then because we want to make http calls we're going to add to our app module the http client module and i just noticed that for videos i should have the sidebar on the right side it's always confusing me but i think it's better for you like this now the app routing um is kind of simple the first page the empty page i will use our login page and inside area we'll use our inside page that inside page is also protected by the authentication guard which in the beginning if you directly pass the can load uh to the generation like um yeah thanks for this uh can we have a new window so if you add implements can load uh this functionality will be automatically implemented and therefore we can already add it to our inside route it won't protect it yet but it's already there then let's talk about the api service because this is quite big but not super interesting actually so i use ionic not ionic storage but the capacitor storage you can also use ionic storage if you want to and i used basically the same flow i had in the past as well when the service is initialized i want to call a low tone to see if we currently have an access token stored and if that's the case i'll directly set this to my current access token and call on our behavior subject next true this is authenticated will be basically only used by our guard as far as i know um so that's the whole logic of uh refreshing the page and immediately being logged in later on then we got a few api calls i got a call to get the secret data it doesn't look like much but this call is actually protected so if i bring in a postman for example uh and i get users secret make a standard get call yeah my connection is refused let's quickly use our environment file instead i don't have the local api running right now so let's do this and let's wake up our heroku server anyway a good idea um so that call is protected and we need to add a json web token here to the authorization header we will also implement this with our own interceptor in the end so that interceptor is actually the really interesting part so without that we get an unauthorized back from the server okay that is first part signup just a simple post with the username and password that's implemented in the api you can also find the api routes that are available in the tutorial below this then we got the login a bit more interesting same credentials like before but this time making a call to auth and then piping the result because as i said we get back an element that looks like this access token refresh token and we want to store both tokens so we want to store the xs token and so using the value access token and the refresh tool using refresh token because uh calling storage.set on the capacitor plugin returns a promise and we're currently within an observable chain um it actually looks more complicated than it needs to um if you call from on a promise you can convert it to an observable and since we do uh need to fulfill both promises we first call promise all with the array of our promises and then this basically returns another promise which is fulfilled once both of these two uh promises are finished then we return it as an observable inside the switch map as i said it's not going to be an easy tutorial and once all of this is done we have saved our tokens after a login we can also call our is authenticated next so we're always up to date um for the log out i actually thought i might do something special and posting the user id but i didn't want to add a json web to package so i can't really decode it right now you could easily do this but i just want to show you perhaps you do a post to your server because the servers then are deleting the stored refresh tokens um in our case it will actually not do a lot so we will just remove our own stored values just like before the logic with from and all promises and then we're gonna say we're not authenticated anymore and we navigate back to the login now the really interesting part actually it's not too interesting yet but the really important part is a call to get a new access token inside the api service for this you first of all need to get your refresh token from the storage once again a bit of transformation between promises and observables and once you get the token uh in my case i implemented the server side that the api route auth slash refresh expect the token just like the xs token in the authorization header so we can manually just set for this one call our header options like this and make a get request so we get a new uh access token otherwise we don't have a refresh token we could perhaps already just lock the user out would be basically the same but that's important for our api to use those custom headers and of course one function more to store a new access token because this call will just uh do i have a refresh or some no i don't have anything um this call will just return one access token because you already got the refresh token you don't need a new refresh token you just need a new access token so within here we can store a new xs token okay that already took us eight minutes to explain i know why i didn't want to do all this live now let's go through the pages of our application and then we can finally focus on the interceptor so login module we need the reactive forms module so we can build a simple reactive form which should look like this in our application header username password log in and sign up i just kept both on one page to make it easy so the login page nothing really special in here we got a credentials form uh with username and password perhaps in your implementation you might also just have an email instead if username on login we call our api service login and once we're done we navigate to the inside area otherwise we present an error and the sign up is basically the same but this time we call the sign up on our api service and since we already got the uh information inside the form we can also directly call login so the user doesn't have to click one more time we just immediately lock him or her in after a successful sign up login page i think that's not really interesting for this tutorial just the form with our form controls and the two buttons and finally the inside area it's basically just a dummy example so we can call our get secret data from the api and call log out and the inside page well a button and displaying the secret data that's really um it's just a lot of boilerplate that we have to set up to get to the actual uh really interesting stuff in this tutorial so i wanted to quickly go through this once again check out the code in the linked tutorial and then we can dive into something that's more interesting perhaps we start with something easy and that is our guard within our guard uh what is this can we just can we just get rid of the most of this i think we actually can uh and we will return observable boolean that's right so within here uh we can inject our service private api service just notice i took me 10 minutes to write any kind of code in this video i think that's a maybe that's a new record maybe what we want to return is if the current user is allowed to access a certain certain page for example in our case the inside area page and therefore we're going to use the api service is authenticated and we're going to pipe the result because first of all we want to filter out some values we want to filter out and make sure we are not using the null value which is a little heck i use so the initial value of a behavior subject right here it's actually a boolean but i use null in the beginning that kind of works so we try to filter out null values in that case we actually um do we have a typo in here no i don't think so oh yeah i need to use value unequal now once we filter out that we will use the first result because that will actually filter out the initial value of the behavior subject then we're going to take the first result which will be true or false depending on if we were able to load the token from the storage or in general if we already authenticated and trying to access another page we just want like uh the current value and then we check is authenticated and if we are or if the user is authenticated i feel like there's a typo authenticated so if the user is authenticated we return true because the user can load that page and otherwise we will return false because the user can't load the page and in that case you can also if you want to uh gracefully or forcefully um sent the user back to the login did that import work i'm not sure yeah it it did work so um just send the user back using the router navigate by url and we're going to send him back to the initial page okay that is our authentication guard and with that in place we're actually not able anymore to access the inside page we're not logged in and we're brought back right here now the interesting part begins uh where we create an interceptor to intercept every api call in our application i'll create a new folder i call this one interceptors there's actually no cli command that i know of that creates an interceptor perhaps happening in the future maybe jason web token interceptor dot ts and there we go also um within that interceptor let's quickly um set up the basics what we're going to need is this to be an injectable json web token interceptor and it implements the http interceptor intercept functionality intercept septerception to use that interceptor we also need to go back to our app module because we need to provide it right here in the area of providers so just like we're providing a custom ionic route strategy we're now providing for the hdp interceptors to use our own class the json web token interceptor there's the import and that is the import from angular common i think we also need to pass in multi-true when true injector returns an area of in the users below multiple providers um i think we need it i'm not sure if you're an expert on exactly this let me know in the comments i might pin your comment in that case maybe um so back to our interceptor in fact we need to implement a little interface here let me bring this in um it's really now the hard part of the tutorial begins um no the brackets were already right let's add all the missing imports so it will be really nice to um yeah just import the damn observable from rxjs why since when do i get those i usually have single quotes what is here we got single quotes and here i had huh what is wrong what is wrong today i really don't know um we actually already got the http even why are you complaining i guess because i don't return anything yeah that makes you happy i know all right um at this point you can imagine we're intercepting an api call the app is making an http call to whatever route get post doesn't matter to anything so the first thing that we should actually do in here is uh write some cider code maybe so first of all do we need to intercept uh if not if that's not the case we can just return the request um yes then perhaps no add the json web token to the header then uh we get back a result and if the result is fine we don't have any problem but we also need to catch errors now and if we catch a four uh or one error that usually indicates uh that your token is expired you're not authenticated so in that case we need to start the token refresh if we catch a 400 that is a bit uh depending on what your back end implemented so um 400 usually means it's not about the tokens that's just in general something of wrong so uh just lock out the user and clear any information that is the action plan in the end this will be a 120 lines interceptor so let's get started first of all the check do we need to intercept that call to check that you can write a function um i will call this is in block list so don't use blacklist anymore you can easily use is in block list instead and what we're going to check is can we import this easily um so for example we don't want to attach our token to the header if we're making a call to the login because we're just trying to get the token and also not to the lockout and perhaps you have other routes in your application where you don't want to send the header then you could filter them out perhaps use a regular expression and then we can start our interceptor by checking if this dot is in block list request is it rick or no it's request dot url and if this is in the block list um just return next handle perhaps use a dot instead of a slash uh the request so that base is like the easiest form of the interceptor nothing is going to happen now the other case is going to be a bit more challenging in that case we first of all need to attach the token to the header so let's write another function and call this private token to our request which is an http request any i hate this i don't know why it happens um so if our api has a token which means we need to add a little constructor private api service api service it's usually just a problem of uh making the whole flow work correctly with observables and all the logic so if our api come on if the api service the current access token exists in that case we can clone our request otherwise we can just return the request no header is attached or no authorization header is attached because we don't have a token but if we do have a token we gonna return it no i'm not gonna change it uh and set the authorization header to bira and our current token that is actually also a simple way to write your own json web token interceptor you really don't need to use uh the plugin there is a common angular json web token plugin that i used in the past which can also decode and checks for expiration time but if you want to do it on your own that's really like the easiest way of writing your interceptor just grab your token attach it to the authorization header and then return the request that's really all you got to do so uh in the else case it means we want to attach our token so we will return next handle our this dot add token and passing in the current request now we could stop at that point um if we just wanted to attach the json web webto into a request because that would be fine right now but now the real fun begins where we pipe the result and we catch errors actually catch error so there we go now we can check if the error is actually an instance of an http error or response so that is important if that's not the case um where do i put the else statement right here in that case um whatever handle it show an alert um lock out the user um well not lock out the user it's a different problem but let's just for now throw the error up the chain and let someone else handle it it's not a perfect example of handling errors everywhere that would really blow up the code i did my best uh for this case so now we're gonna switch uh you can also do an if if you want to um just wanted to do a bit different this time we're going to switch the error status and if it's a 400 we're going to do something um yeah to do if it's a 401 we're going to do something as well and for the default case guess what we're also doing something actually in the default case i will also just throw an error no that's it now the handling of a 400 the 400 era to lock out the user is pretty simple um i will bring in the code because in that case i just want to show a little toast or alert present this and log out the user remember our logout function calls um or makes an http request to the lookout and then removes all the tokens so calling that function basically completely uh removes all uh user related information in our case we also need the toast controller now as i used it and so in that case we can return this dot handle 400 error with our error now we just need one more case and in that case we gonna handle something in a special kind of way i think that's actually the part you are all uh you've all been waiting for um you will be kind of sad that it's so easy in the end but right now we already got a quite extensive interceptor with a lot of logic in so we're gonna handle our 401 era and we're not only passing the error in actually we don't uh we're not interested in the error we're going to pass the request and our handler because in that case uh we don't want to say okay we don't have a tone we're not allowed i'm sorry in that case we want to do another request get a new token make the request again at the same time we have a little problem because while we make the call to get a new access token with our refresh token other calls might come in and say i want to get data i want to get data i want to get data and we don't want to trigger the call to get a new access token like five times therefore we need a little bit of a special logic in here that i already started up here no i didn't start it yet um so let me bring this in we're gonna use a token subject and a variable for this let's just keep it like it is and i will explain it once we use it inside our handle 401 so what we're gonna do now is first of all check if uh not this is refreshing token only in that case we want to refresh otherwise it means there is already an operation going on because right here we will set is refreshing token to true so no one else can get into that block in the other case we want other calls to wait until we got a new token so what we're going to do now here is we're going to wait for our token subject to have a new value the token subject initially has a value of null so just like we did in our guard we can now filter out token token not equal no are we going to take the first result that comes after our null can i just have can this work please that would be great thanks thanks thanks and once we really got back a token at this point it means we can finally make the call because right now for example um there's one call blocked 401 it's going into refresh and we click twice and the same call comes in here now we're waiting until we got a token and now we got the token so we can attach it to the header and perform the actual request so now is the time to call the next handle our request and we're not just passing in our request because that wouldn't attach the the token to the call we're gonna once again call this dot a token which actually um returns a request with token attach remember we clone it here and add the token so actually we don't need the token here we can keep it like this so now we only need to refresh our token once the access token is expired so what we're going to do as well right here in the beginning is set our token subject to null just to make sure nobody's really doing any other stuff or running right here already in that blog because we have a token from the past we should really make sure this works at the same time we can tell our api servers that the current access token well is not so valid anymore so we just set it to null and now the real fun begins which is making an api call using the api service get new access token and we're gonna pipe that result so that is the call to our api which returns a new um access token based on the refresh token we don't really need to worry about that too much everything of this happens in here and it just returns an observable so once we get back the data uh from our get new access token uh we can switch map because we need to switch to another observable once again that's usually the case if you see a switch map functionality yeah so if we got a token we're happy um the else case i don't want to do too much in here so i'll just return off null you can do a lot more in terms of error handling here for sure but i will just keep it and try it because we're already yeah we're close to half an hour fml if we got a token um that's actually wrapped from the get new access token you will see if you trigger my api the xs token is actually inside the body or the result.xs token i hope i don't have any typo in here so hopefully it should work and now we can return this dot api service first of all store the new access token we could maybe also have done this right here in the call but i wanted to make this clear that we do it at this point um do we want a pipe or could i just um i could maybe also just return it but i will use for a bit more confusion another pipe yeah i think we could also have another switch block here but i want to keep it now in here so what we're going to do now is after storing the token i will finally call uh our token subject next with a new token um which is this one or yeah exactly and then we can return not this but next handle actually we can just copy it from here because this is now the point where we retry our initial call the initial call that failed and got a 401 arrow will be now resumed at this point because we're handling the same request once again um once again there's not a full arrow handling in this um but we should now be able to see a bit of that logic so let's try in i will log in with my credentials i already created an account yeah that was not my password i guess what was the password then yeah there was a password i can now get the data um there's actually a little delay but this is my secret message from the server the access token that you get currently has a lifetime of 10 seconds as far as i know so we can take a closer look at our network tab and we can see now we made a successful request the header was attached if i now make another call let's clear everything we see that we get a 401 back unauthorized um that was the first call to secret so our header for some reason expired then we made a call to the refresh route so odd refresh using a different bearer token remember i use now the refresh token inside the header perhaps your api also wants it in the body that's really whatever you want and what you get back is a new access token so after our refresh call we also see in the chain that we're making a new call to the secret and in here we have attached our new authorization header if you want to do it fast i can also do it like this doing it twice it fails twice we should now see one new there's actually a small tiny piece that i missed in this tutorial to actually make it work finally and that is within the call to get the new access token to add a finalized block otherwise um we're basically stuck in this uh is refreshing token and so within here we're gonna set is refreshing token back to false because that was actually the reason why we couldn't make another call in here so now when i do this twice let's do it again i do two calls it fails initially it calls the refresh and then it fulfills both the calls to get the secret data so that was actually our whole flow uh 127 lines in the interceptor with a bit of console lock added by me and a bit of this i hope you enjoyed this it actually took us almost half an hour and we just went in detail through the interceptor so once again the rest the boilerplate code is linked in the article below this video and i hope uh you enjoyed my explanation on building the interceptor to attach the token and to go through the chain of the whole uh back and forth between the refresh token and the access token used in your server if you have your own server and logic it might be different you might have different status codes the body fields might be different you might need to send the tokens in different places all of that could be uh the case but i hope this gave you the first uh or the like the boilerplate code for a refresh token flow with ionic using excess tones and refresh tones and any kind of api that you want to use if you enjoyed this video please hit the like button and stay subscribed so you get notified about all the new tutorials quick wins and other app development and web development videos on this channel if you want to learn more about ionic with in-depth courses a community of like-minded developers so you can learn and build your apps faster you should definitely check out the ioniq academy which is my code school to help you with everything ionic with a huge library of courses material and a supportive slack channel so we can get your app out i hope you enjoyed this video i will see you inside the next video have a great day and happy coding silent
Info
Channel: Simon Grimm
Views: 6,161
Rating: undefined out of 5
Keywords: ionic framework, learn ionic, angular, ionic angular, ionic guide, cross platform, hybrid app, ionic for beginners, ionic course, ionic, cordova, javascript, ionic 5, learn ionic 5, ionic 5 for beginners, angular 9, ionic 5 tutorial, ionic 5 angular, ionic 5 course, ionic academy, ionic tutorial
Id: ig24euSxR7Q
Channel Id: undefined
Length: 33min 8sec (1988 seconds)
Published: Tue Jan 12 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.