Renew Access Tokens
Ryan Chenkie: [0:00] You'll recall that JSON Web Tokens should have an expiry value. On the exp claim, there should be an expiry time after which our API should no longer accept the tokens. This helps to control for security in case our tokens get out into the wrong hands. We don't want a token to have an indefinite lifetime so that an attacker could just use it forever.
[0:20] Auth0 gives us the ability to control the lifetime of our Access Token in the API section. We can go into applications into APIs. Then here in Orbit API, if we look down at the token settings, we've got a couple expiration values. These values here are in seconds, and they're applied depending on the flow that's used to get an Access Token.
[0:40] We're going to implement something in the application so that we can automatically refresh tokens when we need them. To help us along with that, let's give ourselves a much lower token lifetime here. Let's give ourselves 60 seconds, and that's going to allow us to see the effects of this much quicker.
[0:55] Let's come down, and click Save. Then, back at our application. Let's open up the fetch(context). Into Orbit app, into SourceContext, and fetch context. What we're doing is when the application loads, our fetch provider is run, and we are getting an Access Token from Auth0 with this function here, getAccessTokensilently.
[1:15] Once we have the token, we're attaching it as an Authorization header in this interceptor that's run on our auth-axios instance. We're going to get a fresh Access Token every time the page is refreshed, and every time the user logs in. That covers those two cases.
[1:30] But, we still have the case that if the user leaves the browser open for an extended period of time, beyond the lifetime of the token, they're going to start to run into 401 errors after their token has expired. We can get a sense of this if we come to the application.
[1:44] Let's refresh so that we get a new token. Once again, the new token is received once we load the application, so here it is. Let's say we're just pretending to use the application with a browser still open as a user typically would, and then after a period of 60 seconds, we should see a 401 error.
[2:02] After those 60 seconds are up, if we come back over to dashboard, we've got this request going to dashboard data and now, we've got JWT expired. One thing we can do to smooth this over is we can get a new token when a 401 is encountered. In our fetch(context), when we've got a response coming back on axios-auth, and if that response has an error code of 401 or 403, we can run some logic here.
[2:26] Now, 401 is going to be the case when a token is expired. We've also got a 403 Forbidden error if the user doesn't have the appropriate scope. For now, why don't we take care of the cases where we've got a 401 error. What we want to do here is run that code that gets a new token when we get a 401 error.
[2:44] We can reuse the same code that we've got above here. Let's actually move this function outside of the useEffect so that we can use it elsewhere. We've got our function getAccessToken here. It's being called in the useEffect when the application runs. Now, we can just call this function in that 401 error block.
[3:02] Down here, when we get a 401, let's call that function to get the Access Token, that function is setting the resulting token on state, which is going to trigger a rerender, and we'll be able to get some new dashboard data. Why don't we test this out?
[3:15] Let's save this. Back in our application. We're going to get a fresh token. When the page first loads, we get our dashboard data. Let's go sit in the inventory section, and we'll wait until 60 seconds are up.
[3:26] After a period of 60 seconds, if we go back to dashboard, we get that 401 error, but then we make a call automatically to get that new token. Since the new token is set on state, we trigger a rerender. That rerender makes another call for dashboard data where we get our data back.
[3:42] This kind of setup can be useful for cases where we're doing GET requests, but it doesn't address the cases where we might be doing other kinds of requests like submitting data. Let's say you have a case where a user is filling out a lengthy form.
[3:54] When they hit Submit, it just so happens that their token has expired and the time that they were filling out the form, they would get a 401 error map, and you could then get a new token for them. But you would need some extra code to be able to resubmit the values and make another attempt at submitting their form.
[4:10] That kind of code that you write here in your response interceptor is largely going to depend on the needs of your application. It's a bit beyond the scope of this course. However, if you do want to run some code to get a new Access Token, when a 401 is encountered, this is one way you can do it.
[4:24] We'll want to take care of how we have everything set up here where we are using useEffects and where we now have this getAccessToken function. In the console, we've got this warning that shows us that our Hook called useEffect has a missing dependency.
[4:39] The dependency that it actually needs now is this getAccessToken function, but we've got to use another Hook called useCallback to wrap this getAccessToken function in. Let's bring in a useCallback, and then here, getAccessToken. We're going to put useCallback around it. We'll put our closing paren on the other side and we need to give it an array of dependencies.
[5:00] The dependency it needs is the getAccessTokensilently function. Now, down here where we do use effect, let's pass getAccessToken. Let's save this, and we'll refresh. Now, we've got our token again.