React-Redux Strategies for Refreshing Cognito IdToken - reactjs

I'm using the following combination of packages:
react
redux
react-cognito
react-router (v4)
redux-saga
(and I'll disclaim that I'm pretty new with all of these)
Currently I have a PrivateRoute component which will check (and refresh if necessary) an expired IdToken on route changes. This works well.
My problem is if the browser is open past token expiry to a PrivateRoute which is polling my API and sending along the IdToken in its 'Authorization' header. The API will start returning 401.
Options I've Thought of:
Act on API Error
I could catch the 401 error and dispatch an action to refresh the token, but
if there is any issue refreshing the token I wind up in an infinite loop hammering AWS, so need some logic to catch and prevent this. Perhaps redirect to login route if refresh fails?
I then need to add complexity to all my private API calls throughout my app to have this logic, and re-do the requested API call upon successful refresh.
Pre-empt API Error
To me it makes more sense to separate API calls and keeping the auth token valid. Considering react-cognito stores the token expiry time in cognito.user.signInUserSession.idToken.payload.exp, maybe it is possible to pre-empt the API call and expiry.
How to best do this though? At login a refresh action could be 'scheduled' using setTimeout for (currentTime - expiryTime - someBuffer) seconds in the future.
I'm assuming (haven't verified) AWS will let you refresh an IdToken before it expires. I don't want to wait until afterwards else some API calls may have already failed.
are there concerns with using setTimeout with a timeout that may be up to 1 hour long?
Alternatively I could set something up to poll cognito.user.signInUserSession.idToken.payload.exp frequently to detect and refresh an almost-expired token?
Any suggestions?

Related

Validate Authentication on every request in React

I want to check if the backend is returning a error code `ERR_USER_NOT_AUTHORIZED' whenever any fetch request is sent, and logout the user from frontend if this occurs.
Basically I want to redirect user to login whenever the token is incorrect, expired, etc. One method is polling, I poll the server every x seconds and validate, but the time between polls is vulnerable and the user may see lots of errors.
P.S. I'm a newbie to React, I work mostly on Django, what I would prefer is something like #login_required decorator in Django.
You can handle this in two ways:
Every fetch would attach the authentication token in the fetch's header, and the backend would check if the token is valid. If not, the backend would send a 301 Redirect response to your fetch pointing to your login page
You can handle this in the frontend by wrapping all your fetch request routes in a method that, if the fetch fails with Unauthorised, would redirect the page to login
You can check the response.status on the back-end. It usually returns 401: not authorised.

OAuth access_token refresh techniques

I'm implementing OAuth in my React / Redux ( Or other Flux based state management ) application, And i'm struggling to find good information on "pro's vs cons" of various access_token extension methods.
1) Intercept 401 responses
One option would be to intercept API requests (E.g using "fetch-intercept" package) and detect any 401 HTTP code responses.
In the case we 401 we build logic to retrieve a new access_token, and then replay the previous request.
Replay could be handled via dispatching an INVALID_TOKEN action in the 401 handler, catching these through middle-ware, Which would then replay the action before the INVALID_TOKEN action occurred.
Pros
Full visibility of auth failure related actions are visible ( Therefore we could replay auth related bugs using time travel debugging )
2) Detecting out of date access_token at time of API call
Another method would be at the time of sending any request, decode our access_token and check if we need to refresh it soon ( And if so do so ).
Again some sort of middle-ware would be the best option for this solution. We could intercept all actions and before dispatching it first check we have a valid access_token.
Pros
Doesn't need to hit the server just to receive a 401 response we could have instead preempted it
Cons
More complicated logic to queue the requests whilst we're refreshing a token, before finally replaying them once it's been correctly refreshed
3) Polling method
Upon login, see how long it is until the token expires and set a timeout to refresh the access_token just before then
Pros
Simplest option ( Especially when retrofitting an app with lost of existing request logic )
Any additional options or pro's vs cons for each method would be appreciated, so perhaps I can create an exhaustive list here for those starting out

Best practices for refreshing JWT in SPA?

Hi I'm developing a React app that communicates with an Express API. I'm trying to implement JWT authentication but I don't know what should happen when the jwt expires. It not seems very user friendly if the user is logged out when the token expires.
Should I use refresh tokens? As it says in the node-jsonwebtoken package documentation (jsonwebtoken). This may not be the best approach.
Should I create a new jwt in every request to the server? If the user reads an article for a long time without doing any request, he is going to be logged out.
Maybe I'm wrong and the best practice is using a big expiration time and let the user be logged out. If this is the case what would be a reasonable time?
Thanks!
A pattern commonly used along with refresh tokens is to follow a workflow along the lines of:
Some API call / resource returns with a 401, alerting that the token has expired, this sometimes is accompanied by a reason, e.g. Expired token, invalid token
Create a reference to the API call that failed, to retry later
Attempt to refresh the token with the refresh_token
If the refresh works, go ahead and perform the queued API call again
If the refresh fails, the user will need to log in again
You can also use the above approach to queue multiple failed requests, in the event that multiple calls fails whilst a refresh is taking place.
Coupled with a decent expiry time, which really depends on your application, this has proven to be a robust solution in the past for me.
An alternative approach would be to implement a 'heartbeat' API call that updates the user's token periodically whilst they are on the site, however this may come with side effects that may not be desired.

react + redux + redux-observable + global handler for 401 errors?

Some newbie questions of how to handle unauthorisedin redux and redux-observable
I have an application where user must be authenticated in order make requests to an api.
If user provides token that is invalid, endpoint returns 401 http error code.
So how do I elegantly handle this (i'm using fetch)?
Option 1:
create some kind of function with which I would wrap all api calls in epics which would convert 401 responses to something like NotLoggedInAction
create a reducer that handles NotLoggedInAction and sets flag on the state that user is not logged in
subscribe to state changes in root component (?) and check for not logged in flag
if not logged in flag arrives, redirect user to login page (login page is external app, so i would just use window.location.href ?)
This option is kind of flexible, because before redirecting i can show some popup like "hey your session expired, etc."
But it is also not error prone (I have to always hook up error handling to each call, and add dependency in each epic to NotLoggedInAction)
Option 2:
- in combineEpics use do and watch for errors
- if 401 arrives, simply redirect user to login page (via same window.location.href
This Looks much simpler to me, but this is more like a 'hack`?
Am I going the right way? Are the better options?

Start Redux/React Polling after Login

I'm new to Redux, this is a best practice question.
I have a server that i'm logging my app into, on a successful Login i'm returned a tokenId and an expiry time for that token.
tokenId is attached to subsequent API requests to ensure my sessions is valid. Before the expiry time is exceeded i'm required to send a request which generate a new tokenId.
Where is the best place to poll token regeneration? I believe calling an Action from a Reducer is against best practices.

Resources