Is using authReducer with auth token a good practice? - reactjs

I have been reading various tutorials on how to use, for example, JWT in a React + Redux app.
In those tutorials, JWT is typically saved in local storage and has an expiry date. This is fine.
However, I encounter some examples using an authReducer that reads from the JWT onload and sets the boolean state isAuthed to true or false. This state is used to handle UI changes such as AuthedNavbar or setting private routes.
My concern here is that the authReducer state becomes stale. For example, if the user keeps the app open, the isAuthed state will still be true, even though the token may have expired.
(Of course, the server-side routes will be protected with jwt so the user will not be able to access resources. However, they will still have a bad UX on the front-end.)
Indeed, this contradicts to the principle of "single source of truth." The auth state is set in both redux and local storage.
Not all state has to be handled in redux. We use react local state and handle router state outside of redux.
Therefore, I think that it is better to use only local storage as the source of truth.
What do you think? Hope to get some suggestions!

Don't persist the data isAuthed in the storage system. But only place use the access_token. Then, even if the user changes it will have no problem. Because you'll be checking it through the database system and verify the access_token and know the isAuthed from the database as well.

Related

Is React reduxtoolkit persist on session storage good location to persist user data?

I am building a fullstack MERN app that allows users to login and perform some actions.
Some components are to display or work with user data like email, name, status etc.
Instead of sending a http request to the backend on every component that needs userData, is it a good practice to persist the userData on #reduxtookit persist not on local storage but session storage because if the user closes the browser without logging out, the data is gone on next session, which sounds good to me as logout would do the same.
So instead of sending a http request with loader function on react router v6.4 on every component. I probably should persist the data on login and use useSelector to access the data from every component that needs to work with it.
My question is: is this a good practice?
I persisted the data on reduxtoolkit persist and the pages were a lot more split second quicker than getting the data straight from the backend.

Cognito: How can I check if an accessToken is invalid from my react app?

To give some context as to why I need this, here is our problem: We are using Cognito to manage the users of our web app, built with react. When a user signs in, we retrieve the idToken, store it in AuthContext and we set a state called isAuthenticated to true (also part of the AuthContext).
When the user signs out, the isAuthenticated state is set to false.
Here is the troublesome part:
In the current state of the app, a single user can be signed in simultaneously from multiple places. This is something we want to avoid.
I’ve done some research and it seems the best approach is to implement a Cognito Pre-Auth trigger which logs the user out of every device whenever they authenticate.
I don’t think this approach would helps us because of the previously mentioned isAuthenticated state, which, as long as it is true, keeps the user logged in.
The other idea I saw was doing a global signout every time: (1) the user signs out and (2) before each signin as this (https://github.com/aws-amplify/amplify-flutter/issues/1206).
The issue with the isAuthenticated state persists with this approach, but by doing a global signout, the accessToken gets revoked.
That’s why I was wondering if there is a function or something I can call to verify if the accessToken is valid. In case it is not, I can logout the user by setting isAuthenticated state to false.
We are currently using the aws-amplify library (version 4.3.5) to get access to the Auth class, with which we handle all our authentication processes.
I haven’t come up with any other ideas to solve this issue, but if someone has a better approach to solve this issue, please comment down your solution.
All inputs and ideas will be greatly appreciated.

React Native: Best way to store access token. AsyncStorage or Redux Store?

I am developing React Native app. I need to use access token for auth requests. Currently I am storing it in redux store. But on refresh token become null. So I was thinking of using AsyncStorage to store token. But don't know which one is fast. I can use Persist with redux store to keep token for long time as well.
One more issue with AsyncStorage is that I can't get token without using await, and for that I must use this inside async function. But I will require it in other places as well where I can't use async function , like to in header configs of post request where I set Authorization for all request.
Any help would be appreciated. Thanks.
Both comments are correct - if you want to reuse the access token when the user enters the app again after a short time, it becomes really handy to have the token still in the async storage.
For usage within the app it is then better to have the token in redux or the AuthContext, if you use that one.
Now Redux-persist is a combination of both, gets you the methods to automatically load data into store on starting the app and also put them back into storage.

ReactJs with Firebase, do I need to store user credentials/token in a local storage?

So I want to create a protected route for my app and I read this post Protected Route With Firebase. Some how I didn't see any usage of local storage or cookies. I have created protected route with my custom jwt+Spring before and I checked the token (if it exists on the local storage). I wonder if firebase library automatically set the local storage for me so I don't have to worry about it otherwise the credentials will be reset after user reload/refresh the page.
Firebase auth persists the authentication in local storage by default, and it will automatically re-authenticate if possible when the user returns to the page. The re-authentication is asynchronous, so you may need to set up your routing logic so it waits until you get your first callback from onAuthStateChange before deciding whether to allow the user into the route, or do something else. (EDIT: I checked out the post you linked and it does wait, like my suggestion 👍)
For more information on persisting auth state, see this page: https://firebase.google.com/docs/auth/web/auth-state-persistence

React/Redux server side rendering initial state

I have a React/Redux application that keeps track of a user's authenticated state in local storage. The application is also set up to use server side rendering. I'm running into issues when rendering the initial application state. My server creates a new store and emits the SET_INITIAL_STATE action. This initial action on the client side reads localStorage and passes the authenticated information on to my reducers. The server, however, has no knowledge of this logged in state since I'm using stateless JWT located in local storage for authentication.
Since the server and client are out of sync at this point, I'm getting this error:
React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server:
Which makes sense, because the server is trying to render an unauthenticated state.
What is the accepted standard or practice for setting this initial state that relies solely on something the client has access to?
I found that the OP is right, that cookie storage is the best option. If you're using react, redux, and express for a universal app the option that worked for me was https://github.com/eXon/react-cookie.
Essentially:
In you server side code you can use:
import cookie from 'react-cookie';
function handleRender(req, res) {
cookie.setRawCookie(req.headers.cookie);
// Create new Redux store instance
const store = createStore(rootReducer, {
username: cookie.load('username') || '',
accessLevel: cookie.load('accessLevel') || userRoles.public,
});
//other react/redux/express related code
}
In your react applications, inside the components, you can just save the cookies directly:
import cookie from 'react-cookie';
//first parameter is anything you want, second is the data you want to save
cookie.save( 'username', username );
cookie.save('accessLevel', accessLevel);
Further if you want to store an object, as I did, you can pass in JSON.stringify(your object). cookie.load will automatically parse it for you.
In my code I called cookie.save from the actions, but you can call it directly in components or any abstraction you have.
I found a working solution.
The trick, unfortunately, is to store the authenticated state in a cookie so the session state gets sent to the server automatically upon request.

Resources