Updating State of a component before request finishes - reactjs

More of an implementation question. I have an application which allow users to bookmark specific pages. In order to bookmark a page, the users must be logged in. Whenever a request is made, we check to see if their current token has expired. If it is, we re-authorize through auth0 and then send the request to our backend.
When the request is complete, we then update the state of the bookmark icon to be filled in, informing the user that the page is now bookmarked.
This process of calling out to auth0 takes about 1.5-2 seconds, sometimes causing a noticeable delay after clicking the icon.
My initial thought was I would update the state of the bookmark on the frontend first, then send the request to the backend to actually persist it, and then assuming the request failed, would update the state again to undo the first change. This would guarantee that the update would always "appear" instantaneous from the users perspective.
However, part of me is feeling like this isn't a proper solution. Just hoping if someone could weigh in and let me know if this is a good way to approach this problem.

Related

How can you tell/wait for a user to be determined to be logged in or not?

According to the official firebase docs for implementation of google login, you create a listener/observer (onAuthStateChanged) that will emit a user once your user has successfully gone through the login process.
When signing in, this works very well. You do signInWithPopup or the full-screen variant and google takes care of collecting the relevant information and doing the login. If successful, onAuthStateChanged will emit the User object. From there, you have everything at your fingertips. It's very cool and I am super appreciative of the process here.
However, I am having trouble wrapping my brain around what happens when a user has logged in already and that logged-in user refreshes or re-approaches your app in whatever way. onAuthStateChanged will fire up, but this is an asynchronous process, meaning that, for some time, the observer will emit nothing, but that lack of response could mean two different things. This brings me to my question:
How can you prevent visually displaying that you don't know whether the user is logged in or not? Is there some part of this implementation that I don't understand?
If, for example, I wait on that user object and then assign it to some redux store stuff, don't I always have a moment at the beginning of the app where I cannot possibly know whether a user is authenticated? I have to display the login page, even if I assume we're checking because the outcome may be that the user is not logged in. There's no signal to differentiate waiting for response and there being no response (because a user is not logged in)
You only have two options from the observer:
A user is emitted because the user is signed in
A user is not emitted because the request hasn't been completed or they're just simply not logged in.
That last one being two different possibilities is the problem, at least in my thinking.
Any advice?
The answer I found for this came from another thread.
It is not possible to tell whether a user will be signed when a page starts loading, there is a work around though.
You can memorize last auth state to localStorage to persist it between sessions and between tabs.
Then, when page starts loading, you can optimistically assume the user will be re-signed in automatically and postpone the dialog until you can be sure (ie after onAuthStateChanged fires). Otherwise, if the localStorage key is empty, you can show the dialog right away.
The thread has example code, but, basically, you set a localstorage piece on successful login. Then, you can reasonably assume they will be logged in. Obviously, if credentials are revoked, this would mean the user would always see a spinner, but that's probably OK. I may add a timeout that kicks them to the login page if so much time goes by without an emitted user.

Do I need to call firebase.database.on() again after token refresh?

I'm having this problem with my application where it will eventually (unsure of the timeframe, presumably 1 hour) stop updating live with firebase realtime database changes without logging out and back in. The security rules prevent unauthorized users from accessing data. The user will remain logged in and the authentication session persists, so I've been miserably confused as to why it eventually stops updating.
After a painful amount of internet scouring, I've come to find out that the token ID given on login lasts only 1 hour, at which point they are refreshed automatically using the refresh token. (For the record I am logging in with firebase.auth().signInWithEmailAndPassword).
Now, my question - Do I need to resubscribe to my database after the token ID is refreshed? More specifically, do I need to call firebase.database().ref().off() and then subsequently call firebase.database().ref().on() when a token refresh is detected? If not, can you possibly point me in the direction of what might be going wrong?
Edit: It may also be worth noting that if I change my security rules to allow unauthenticated reads, the user is still able to write to the database indefinitely without having to reauthenticate.
Firebase passes the new authentication token to the database automatically. As long as the user remains the same, you don't need to reattach the listeners.
The only moment you might have to reattach is if the user somehow becomes signed out. This may happen when the ID token can't be refreshed, for example because the account has been disabled, or the password has been changed. In that case the existing listeners will be canceled, which will be logged in the client, and the (optional) error callback for on will be invoked. At that point you'll need to reauthenticate and attach new listeners.

React router auth control, how to handle session end?

So I have read few methods how to do that, the first one is using onEnter mehods on route and the second one is having an higher component (like this example https://medium.com/the-many/adding-login-and-authentication-sections-to-your-react-or-react-native-app-7767fd251bd1)
So my question here is not about state management but about state update, let me explain.
I login successfully , my state is updated, and know I can access the '/settings' route.
(1'. If I logout state is update and I can't access ...)
My question is how should I handle properly session end ?
The login state is saying I'm still connected but meanwhile the session has expired. So it means I can access the '/settings' page because has not been updated. What should I do ? I see two options:
I send a request to my server each time I want to access a protected route so it will update the state accordingly.
I let the user go to '/settings' but when he tries to perform an action I check on server side if the session is still valid and send him to the login page in case the session has expired.
I hope it's clear, what do you think the better option is ? I would go for the option 2. but I'm not 100% sure
thanks for your help
Short answer for your use case: go with option 1.
Explanation: Imagine you click on "Settings" and and maybe all the settings are loaded from the store. Now, displaying all these settings is fine, but what if you want to update something? You probably make changes or even fill out some text fields, only to get informed that your session has timed out and you need to re-authenticate in order for your settings to be updated/saved. Also, what if the user is using a shared/public client and forgets to log out? All settings will still be displayed, even when the session has long timed out. And to make it even worse, just think of the additional effort required for effectively caching actions made / information added by the user that have to be replayed once re-authentication took place.
So in order to not discourage your users and for security reasons, go with option 1 and make sure the session is still valid BEFORE you render a protected resource.
This can be done in many ways, for example sending a timeout value with your token and check the value on client side (not very secure, since everything stored on the client can be altered on the client), send a verification request to your API using middleware or HoCs before rendering, a combination of both et cetera.

How to detect expired user session in a react app?

I am developing a REST API based on Node / Express and a frontend for it based on React / Redux. Users can login (which gives them access to additional functionality) but they can use basic functionality also without logging in.
When a user logs in, the client makes an HTTP call with the credentials, the server creates a session and returns a user object (user_id and some other data) as well as a session cookie. The React app saves the user object in its Redux state. In subsequent HTTP calls, the user is authenticated through the cookie.
When rendering the user interface, the React app determines whether it is logged in or not by checking for a user object in its state. This is used to grey out some buttons which are only available to logged in users, or to hide the login link when the user is already logged in.
The problem
It could occur that the session expires, or that the user logs out in a different browser tab. The React app has no way of knowing this and thinks it is still logged in (i.e. app state mismatches reality), leading to wrong UI display.
What pattern to solve this?
Put a hook on all Ajax calls to check for 401 and update the
state?
Return session state in HTTP headers (and then?)
A Comet pattern for the server to notify the client that it has been logged out? (not a REST API anymore then)
Additional calls before actual API calls to make sure user is still logged in? (seems wasteful)
And how to deal with this once the client detects it is no longer logged in during an ongoing operation? I'd prefer to handle this in one place rather than all functions making API calls...
I'd be thankful for some best practice!
There are two straightforward ways to deal with this issue in a React application that I can think of. Both inspired by a colleague of mine few days ago.
Use SSE (server-side-events) technology to PUSH notifications. As you correctly pointed out, this makes your API less pure. This approach should be quite an acceptable sacrifice where flawless UX is required AND/OR your server might need to push other notifications to the app.
Establish a short term timer somewhere in your client app (e.g.: setTimeout(...)) that makes periodic calls to a "ping" API endpoint that will return current user/session information. This approach will impact UX for the duration of timeout, often negligible, and is commonly known as polling.
Hope this helps!
As an alternative to the naive polling, you can make it a little smarter by adding an endpoint that lets you know in how many seconds timeout is set to occur for the session at that point in time.
Then ping just before that time (instead of at a certain poll-rate) and update accordingly.
Logging out in another tab would return with an invalid token so would be picked up, too, but not as quickly if this is your main concern.
For this you could use broadcasting to let the other tabs know immediately (or use sessionStorage's change event to simulate a broadcast on unsupported browsers).
Otherwise the best way would be to implement a ServiceWorker; these can handle all requests for your app to the server. It's a centralised piece of code separate from your app that can broadcast to all tabs that a session is lost the moment it sees that one of its requests was rejected, and you can efficiently naively poll from this one place (instead of in each individual tab's runtime).
Since I am using token from the API Server that is valid for a specific period of time. So in addition to setting token in session storage I was thinking of setting up another session storage variable that stores the timestamp at which the token was generated. Then, in my js code I plan to add the validity period (say, 3600 seconds) and check if the token is still valid or not. If it is valid then the user session is valid else it is invalid.

What is the best approach to work with data while using token based authentication

I am building an sample application that lets user store comments.
I've created the registration and login process. When the user registers, his details are stored in a MySQL database and a token is returned to the browser. Now he can access the Profile page.
When an existing user logs in he is redirected to the profile page. The profile page is accessible only when a user registers or logs in.
After logging in, I want to show all his comments if he has already added them.
My frontend is in Angular and backend use Laravel. For authentication I use Satellizer.
I want to know, what is the best approach while playing with data, considering the fact that the user will add, edit his comments. Should I use localstorage and store data in a key value pair or should I create a json file which gets updated everytime the user adds a comment or makes a change.
I wanted to know what is the most efficient way to deal with data from server so that the application is fast even when it scales to a 10000 users and lot of data for each user.
Thanks
You should be updating it on the server when changes are made rather than only relying on localstorage. You can use localstorage to cache, but it should only be for immutable data, it shouldn't really be used for data that is going to change.
So in this case you'll be adding and updating new comments via your API (ideally a RESTful one!). Once you've made a change, you could store the comments locally and only update them when the user makes a new comment, however you'll quickly run into issues where the data is invalid on different clients. (i.e. if you update the comments on a different computer, the other computer won't be aware).
Alternatively, you could cache the comments and then simply ping the server to find out if new comments have been added. This could be using a HEAD request for example to check the last modified date on your comments resource.
You can store comments data locally on user browser, but you should properly manage it.
I don't how much load your server will have and if the time invested now worths it.
You can fetch comments and store them locally
User adds a comment, then you update locally and send a request to the server
You need to track the request response, if requests fail so notify user and remove comments from local.
if request was successful so you can continue on your way.
** facebook uses this "success first" approach
user does an action, and he see it happens instantly, in the background it could take few seconds, only if it fails they will notify you.
** look at their commenting process, when you comment, it appears instantly, no loading... but in the BG the load happens.

Resources