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

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?

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.

Handling with external logouts in React app

I just finished my session (cookie) based login system in my React+Redux app.
Then I realized that there is no way how I can check if the user logged out from another location (different chrome tab, removing the cookie, server session invalidation, expiration).
I was looking on Instagram's website what using React too. It seems if you log out in a different browser tab, you can still route to another place in-app until u hit some API fetching... Then website is automatically refreshed...
BUT, there is also some kind of system what realize that user is unlogged even when I do some actions whatnot require API calls.
So how to realize these situations in the best way? How do you handle them when developing.
I'm not exactly sure how Instagrams Authentication works but I'd imagine that it's handled by middleware and when you request an API call, it will check to see if the user has an Auth Token stored in Cookies or whatever before initiating the API Request.
You can easily do this yourself by adding a Redux middleware that checks to see if the cookie is there before dispatching the next action. If it's not there you can return an error message to the user or redirect them or even dispatch a redux action that clears out all loaded data and then finally redirect them back to the login page.
The reason why Instagram is only locking the user when it hits an API call is that you can't really do anything dangerous to the users account if the cookie was deleted as you can't make changes to their account (commenting, posting, changing account settings etc.) without interacting with the API. Therefore, the middleware doesn't have to run every time an action has been dispatched which technically makes their application more performant.
Example Redux middleware
import Cookies from 'js-cookie';
const clientHasToken = store => next => action => {
const authToken = Cookies.get('auth');
if (!authToken) {
// redirects user, but you could do anything here
return window.location.href = '/login';
}
// if user has an auth token, proceed to the next action
next(action);
};
export default clientHasToken;

How to deal with 401s in React application?

I am creating a react SPA application and I am using tokens that are refreshed on every request to any authenticated route.
On 401/Unauthrorized for any of these AJAX requests, I prompt the user with a popup/modal that requests the user to login so that they don't have to lose the state of their current page.
I could redirect them to the login page and then back but they would lose any current state unless I persisted it somehow, but that seems like unnecessary complexity.
The problem is that after the initial failed AJAX request, more AJAX requests could occur. So if I just redo the initial failed AJAX request then it will not be correct. I'm thinking that I could save all the failed request promises and then re-resolve them after the user has re-authenticated.
Is the proper high level approach?

React-Redux Strategies for Refreshing Cognito IdToken

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?

Authentication with React, React-Redux-Router and Passport.js

I am using Express for backend and passport-local for user authentication. The state has a user object with 'isAuthenticated' field to check if the user is authenticated. After the user clicks the login button, I dispatch login action, which sends a post request to the server that handles authentication logic and sets 'isAuthenticated' to true. If the user is logged in, react-redux-router dispatches 'push' to change the location.
None of this seems to work however. When I click the login button, the page simply reloads. I see the actions dispatched for a brief second from the logger middleware but the whole app just reloads. Is there a way to get this to work?
Also, I saw an example that uses server side render to save the state so that when the page refreshes, the 'isAuthenticated' still remains true. Is it possible to achieve the same without using this method or cookies? Would there be a way to get the 'req.isAuthenticated' to the client side similar the how templating engines pass objects from the server side to the client side?
Edit: So passport.js works fine on chrome apparently not Safari but I am still wondering what would be the best way to deal with the second question.

Resources