@juanortizoviedo
- I had the exact same problem so once the web browser is refreshed whilst the URL is /dashboard, the page is automatically redirected to /unauthorized which makes sense as this is specified in ProtectedRoute.
I tried refreshing the browser whilst on /unauthorized page and it worked as expected (reloaded /unauthorized as normal).
I've since created a fresh create-react-app project with firebase and authenticating a user with either email or google provider and this exact same refresh issue is happening. It's almost as if the user is authenticated quickly enough on page load so the browser redirects the specified page or back to the root (/) URL.
So if a user (authenticated or not authenticated) refreshes the browser on a non-protected page, everything is fine but they are redirected if already on a protected page.
Have you managed to figure out how to overcome this issue? Any help would be greatly appreciated @mychal
Client side routing is hard! Basically what you're seeing is the server try to find something at /route, when your SPA loads from /index.html - /route doesn't 'really' exist - until your index.html is read and your JS kicks in. It really blows your mind until you get it. Using react router you can evade this issue by using hashrouting
I came across this issue when working with protected routes. The reason as you've already figured out, is due to the user state initially being sent to our components (defaults as false), so when it hits our protected route, it redirects the user to 401 unauthorised. What I did to solve this issue was check the user state before returning the routes. To "load" the app before it actually hit the router. I've attached some sample code, hopefully helps someone stuck with this.
The issue seems to be as simple as the demo application doesn't have a persisting state of the user's auth. Since it's just artificially creating auth for a user and storing it in memory. The state of the user being logged in can't survive a page refresh. So naturally when you login and going to dashboard via the on page links it works but when you do a page refresh the user's current logged in state is lost from memory and defaults back to its original state of false. Look at the React Developer Tools and monitor the components state regarding the user's auth. A solution to this in a real application would be to have some state management system like context or Redux to persist the state so the accurate page shows when expected.
@franciscobenedict I had this exact same issue. Firebase will persist an authenticated users details in local storage, however to retrieve them initially is asynchronous which as you've experienced doesn't work when refreshing the page and you get taken to a login screen, etc.
As we will be wanting to know about this user state across multiple components its advised to wrap all this logic into a context provider. To start solving this we can utilise onAuthStateChanged from Firebase which is the recommend way of retrieving a user as it allows Firebase to initialise, it will provide either the user object or null in the callback. What we also need is a loading state, which is true on initial launch and when the user has been loaded or if there isn't one we can stop loading. To get this behaviour we can wrap onAuthStateChanged in a promise which resolves the user and call it on initial launch using useEffect. We can then wait for this promise to settle before we manage our final loading state. Finally we can decide what to do once we've finished loading and we either have or don't have a user.
Not work if i refresh '/dashboard'
@juanortizoviedo - I had the exact same problem so once the web browser is refreshed whilst the URL is
/dashboard, the page is automatically redirected to/unauthorizedwhich makes sense as this is specified inProtectedRoute.I tried refreshing the browser whilst on
/unauthorizedpage and it worked as expected (reloaded/unauthorizedas normal).I've since created a fresh create-react-app project with firebase and authenticating a user with either email or google provider and this exact same refresh issue is happening. It's almost as if the user is authenticated quickly enough on page load so the browser redirects the specified page or back to the root (
/) URL.So if a user (authenticated or not authenticated) refreshes the browser on a non-protected page, everything is fine but they are redirected if already on a protected page.
Have you managed to figure out how to overcome this issue? Any help would be greatly appreciated @mychal
Have a read over the answer here: stackoverflow.com/questions/279283...
Client side routing is hard! Basically what you're seeing is the server try to find something at /route, when your SPA loads from /index.html - /route doesn't 'really' exist - until your index.html is read and your JS kicks in. It really blows your mind until you get it. Using react router you can evade this issue by using hashrouting
I came across this issue when working with protected routes. The reason as you've already figured out, is due to the user state initially being sent to our components (defaults as false), so when it hits our protected route, it redirects the user to 401 unauthorised. What I did to solve this issue was check the user state before returning the routes. To "load" the app before it actually hit the router. I've attached some sample code, hopefully helps someone stuck with this.
gist.github.com/FaizanH/88ede257df...
The issue seems to be as simple as the demo application doesn't have a persisting state of the user's auth. Since it's just artificially creating auth for a user and storing it in memory. The state of the user being logged in can't survive a page refresh. So naturally when you login and going to dashboard via the on page links it works but when you do a page refresh the user's current logged in state is lost from memory and defaults back to its original state of false. Look at the React Developer Tools and monitor the components state regarding the user's auth. A solution to this in a real application would be to have some state management system like context or Redux to persist the state so the accurate page shows when expected.
@franciscobenedict I had this exact same issue. Firebase will persist an authenticated users details in local storage, however to retrieve them initially is asynchronous which as you've experienced doesn't work when refreshing the page and you get taken to a login screen, etc.
As we will be wanting to know about this user state across multiple components its advised to wrap all this logic into a context provider. To start solving this we can utilise
onAuthStateChangedfrom Firebase which is the recommend way of retrieving a user as it allows Firebase to initialise, it will provide either the user object ornullin the callback. What we also need is a loading state, which is true on initial launch and when the user has been loaded or if there isn't one we can stop loading. To get this behaviour we can wraponAuthStateChangedin a promise which resolves the user and call it on initial launch usinguseEffect. We can then wait for this promise to settle before we manage our final loading state. Finally we can decide what to do once we've finished loading and we either have or don't have a user.Here's my code:
Helper function
AuthContext.js
ProtectedRoute.js
There must be a typo