pre-emptive refresh of expired JWT token - reactjs

All of the solutions I've found while searching react to a 401 response from an API call before triggering logic to refresh the expired token. In my case, I'm using react-cognito which puts the expiry time in the redux store under cognito.user.signInUserSession.idToken.payload.exp (integer representing unix time).
I would like to try and implement a scheme where expiry is pre-empted and I'd like to keep this logic separated from my API call code, if practical.
One option I explored is setting a timeout for currentTime - expiryTime - someBuffer, but this may be 50 minutes long or more and I've read that long-running timeouts can often be unreliable, having strange performance especially when the browser tab is not in-focus.
Another option I considered was a saga that runs in a loop, initiated by the LOGGED_IN action and ended by the LOGGED_OUT action, that checks for an almost expired token and refreshes it. On mobile, my understanding is that code execution is paused while the browser is in the background - as such this approach would have an edge case where if the user foregrounds the browser just after token expiry then there's a window of time equal to the loop interval where API calls will 401. The loop interval can be made smaller but the edge condition could never be eliminated.
Is there some scheme that can reliably fire an event/action just before token expiry or, in the case of mobile browsers, fire immediately upon execution-resume if foregrounding happens after the desired refresh time?
Thanks!
David

There is no need for long lived timeouts, simply check each second or so if the token has expired.
while(true){
yield delay(5000);
if(yield call(checkExpiring)){
yield relogin();
}
}
There will be a lot of checks, but they don't have any real performance impact.
That said, I usually write a fetch middleware which checks if the server replies with 401/402 and than reauthenticate and resubmit with the new token.

Related

Best way to refresh token every hour?

I am building a website with React and I have to send about 3 requests per every page, but first of all I have to get communication token that needs to be refreshed every hour by the way, and then use it as a base for all other requests.
I have a plan to get it as soon as App mounts and put it in state (redux, thunk) and use it in every component that subscribes to store and then put setInterval function in componentDidMount method too. Another thing that comes to my mind is to put it in local storage but that would be a bit complicated (I have to parse every time I get something from local storage).
class App extends React.Component {
componentDidMount() {
this.props.getToken()
setInterval (this.props.getToken, 5000)
}
This works pretty well, and switching between pages doesn't spoil anything, it works pretty good. Note that here 5000 miliseconds is just for trying out, I will put it to be 3500000. Is this OK or there is another way to do this? Thanks!
Your implementation is pretty fine although I'd make a few changes
Use local storage so you don't have to refetch your token if user refreshes the page (since it'll be lost from memory). Also you'll have same benefit when working with multiple tabs. You can easily create some LocalStorageService that does all parsing/stringify for you so you don't have to worry.
I'd suggest to move that logic to some kind of service where you'll control your token flow much easier - e.g. what happens if user logs out or somehow token becomes invalid? You'd have to get new token from other place than your App (since root componentDidMount will be called only once) and also you'd need to clear the current interval (on which you won't have reference with current implementation) to avoid multiple intervals.
Instead of intervals maybe you could even use setTimeout to avoid having multiple intervals in edge cases:
getToken() {
// do your logic
clearTimeout(this.tokenExpire);
this.tokenExpire = setTimeout(() => getToken(), 5000);
}
Overall your implementation is fine - it can only be improved for easier maintenance and you'll need to cover some edge cases (at least ones mentioned above).
Ideally your server should put tokens on secured sessions so they are not vulberable to XSS.
If there's no such an option. I'd suggest using axios. You configure it to check the tokens on each request or response and handle the tokens accordingly.

How to be sure that the react-redux app is rendered based on the latest request?

I have a react-redux application which:
Loads N records from the database depending on a "limit" query parameter (by default 20 records) on first application load (initialization)
Every 10 seconds app requests same (or newer) records from the database to update data in real time
If a user changes filters - app requests new records from the database according to the filter and re-renders app (+ changes interval to load data according to the filters)
If users scrolls down, the app automatically loads more records.
The problem is that if a user for and instance tries to filter something out and at this same time interval is loading more data, 2 requests can clash and overwrite each other. How in react-redux app I can be sure in a request sequence. Maybe there is a common approach on how to properly queue requests?
Thanks in advance!
I am not sure what you mean by 'clash'. My understanding is that the following will happen:
Assuming that both requests are successful, then data is retrieved for each of them, the redux state will be updated twice, and the component which renders the updated state will render twice (and the time passed between the two renders might be very short, which might not be very pleasant to the user)
If you want only one of these two requests to refresh the component, then a possible solution may be the following:
Each request starts, before retrieval of data from the database, by creating a 'RETRIEVAL_START' action. 'RETRIEVAL_START' will set a redux state variable 'retrievalInProgress'
If you want, in such a case, to get results only from the 1st of the two requests, you can check, before calling the action creator from the component, if 'retrievalInProgress' is on. If it is, don't call the action creator (in other words, do not request data when a request is in progress). 'retrievalInProgress' will be cleared upon successful or failed retrieval of data.
If you want to get results only from the 2nd of the two requests, then make 'retrievalInProgress' a counter, instead of a boolean. In the 'retrievalSuccess' action of the reducer, if this counter is higher than 1, it means that a new request already started. In this case, do not update the state, but decrement the counter.
I hope that this makes sense. I cannot be 100% sure that this works before I test it, which I am not going to do :), but this is the approach I would take.

DynamoDB ConditionalCheckFailedException thrown but succeeds

I think that have seen in many occasions that a DynamoDB conditional put throws ConditionalCheckFailedException but succeeds. Usually in this scenario, the request takes quite long (~10s) to finish, but I can see that the request is updated despite the fact that a ConditionalCheckFailedException is thrown (and the it took few seconds).
By the way I don't force any timeout on the DDB request.
Is this a bug, or some DDB conditional put contract that I misunderstand? Has anyone experienced this issue?
Answering this late to inform others:
ConditionCheckFailedException but item is persisted:
This typically happens when you save an item to DynamoDB, DynamoDB acknowledges the write request but the response gets lost on the return path which can happen for multiple reasons, keeping in mind that DynamoDB is one of the largest distributed systems in the cloud.
This causes the SDK timeout to exceed while awaiting a response, which then triggers an SDK retry. When the write request is retried, the condition now evaluates to False as the item already exists, which in turn throws a ConditionCheckFailedException, which can cause confusion.
When I receive a ConditionCheckFailedException I typically do a strongly consistent GetItem request for the item to ensure it exists with the values I expect and move on.

Handle Spinner in multiple http calls angular

I have implemented interceptor in my application which shows spinner on any http request and hide after getting the response. Also i have implemented counter for multiple http calls so that spinner goes off after last call.
But now in some cases , suppose i have three http async calls and i get the response of first call before my second call reach interceptor . This causing flickering of spinner on screen as it goes ON and OFF due to this scenarios.
Based on my understanding of the question, your code is working "as expected". The flickering is not caused by a bug in your implementation, but instead by a "limit case": two successive $http calls, resulting in your loader screen to go off for a split second after the first request completes just to be reactivated soon after when the second request is made. When the two request are close enough this gives the flickering effect: your end user does not know that two sequential requests are made, he just sees a loading screen going off only to come up again soon after.
In this case, counting the open request cannot mitigate your problem: when the first promise is completed, the second one hasn't been made yet: the counter is still "one open request", so after the promise completes your logic detects that now there are 0 open requests and stops the loading screen.
For those reasons, the only viable solution I can imagine is to implement a sort of "switch off delay" on the loading screen. By making the "loading screen switch off" not immediate, you give your app the time to start the second request. In order to achieve this, you have two options.
The first and probably the cleaner one is to implement the "delay" in the component handling the spinner. Modify the .hideSpinner() method to hide the spinner after a certain time delay has passed, and modify the .displaySpinner() method so that it cancels out any pending "hideSpinner" call. Notice that this may not be viable if you are using a spinner component that you didn't implement and therefore cannot modify easily.
The second approach is to work on the interceptor side. You should already have an "end request" method that checks if the request counter has returned to 0 and in that case stops the loading screen
Your code should look similar to (notice, this uses Typescript):
private startRequest(): void {
// If this is the first request,start the spinner
if (this.requestCount == 0) {
this.loadingOverlayService.start();
}
this.requestCount++;
}
private endRequest(): void {
// No request ongoing, so make sure we don’t go to negative count.
// Should never happen, but it is better to be safe than sorry.
if (this.requestCount == 0)
return;
this.requestCount--;
// If it was the last request, call the service to stop the spinner.
if (this.requestCount == 0) {
this.loadingOverlayService.stop();
}
}
Just add a setTimeout delay on the endRequest method. This way, the actual "is this the last request" check will be delayed, giving your app the time to start a new request before the spinner gets closed. Notice that this introduce a new problem: now any loading spinner will last 1Δ more than required, where Δ is the timeout you are using. In most of the real world cases this isn't actually a problem, you don't want your loading spinner to be "too fast" anyway in order to avoid a similar flickering problem with very short requests.
Your code should then be as follow:
private endRequest(): void {
setTimeout(function() {
if (this.requestCount == 0)
return;
this.requestCount--;
if (this.requestCount == 0) {
this.loadingOverlayService.stop();
}
}, 1000);
}
As already stated, now the check will run a second after the request has ended. This will give your second request the time to be started and increment the counter before the handler can check if there are still other pending request to await. As a result, your loading screen should'b be rapidly closed and reopened but instead just stay open, hence removing the appearance of flickering.
PS: There is a third option that I haven't discussed because your post gave me the impression it won't be applicable to your situation, so I will just post it here in this foot notice as an hint for future readers.
IF all your requests are predetermined, meaning they can be started at the same time (no request must await for the results of the preceding one) you may be able to chain them in a cumulative promise by using $q.all(). This may avoid the flickering effect, but further tests would be required to be sure this solution fits your need. In the end, the setTimeout option is probably the most convenient one, with the best effort/cost/quality compromise.

Automatic $cancelRequest() for all pending request in $rootScope.$on('$stateChangeStart')

We have a page with multiple requests to the backend REST with big delay and much calculation time on the server side, that makes a user to wait for a long time until all queries respond, then run other requests no matter whether new queries are fired by the actions on the page itself.
If it does not matter whether that page or state changes it, then I would like to cancel the pending requests.
I can cancel each pending request by using $cancelRequest() function from ngResource#1.6.6.
Answer
How to implement automatic resourceInstance.$cancelRequest() on $rootScope.$on('$stateChangeStart')
Used
place $resourceProvider.defaults.cancellable = true; in
app.config($resourceProvider)
Service/Factory to return an instance of $resource
ngResource#1.6.6
angular#1.6.6

Resources