Firebase Firestore auth token infinite loop after signing in

Viewed 798
  • When the app is connected to Firestore and a user is signed in (in our app), an auth token infinite loop appears.
  • Without Firestore connection, this effect doesn't occur.
  • You can see the issue on the GIF file included herein as the link (see below).
  • Interestingly, this problem is present only when the project runs on Linux Mint (19.1 Cinnamon) in contrast to Windows 10 or 8.1 (I didn't run it on Mac).
  • The project was bootstrapped with Create React App.

Video-presentation of the issue

I integrated Firebase with ReactJS with this tutorial: https://www.robinwieruch.de/complete-firebase-authentication-react-tutorial

and found very useful the class-based Firebase connection and HOC/Context API session handling presented therein. However, I have no idea how to fix this auth token infinite loop.

Below, there are files responsible for signing in/out and Firebase connection, as well as package.json:

index.js:

import React from 'react';
import ReactDOM from 'react-dom';

import './index.css';
import * as serviceWorker from './serviceWorker';

import App from './App';
import Firebase, { FirebaseContext } from './components/Firebase';

ReactDOM.render(
  <FirebaseContext.Provider value={new Firebase()}>
    <App />
  </FirebaseContext.Provider>,
  document.getElementById('root')
);

serviceWorker.unregister();

App.js:

import React from 'react';
import {
  BrowserRouter as Router,
  Route,
  Switch
} from 'react-router-dom';

import Navigation from './components/Navigation';
import LandingPage from './components/LandingPage';
import SignInPage from './components/SignInPage';
import HomePage from './components/HomePage';
import HomePage2 from './components/HomePage2';

import { withAuthentication } from './components/Session';
import * as ROUTES from './components/constants/routes';

const App = () => (
  <Router>
    <Navigation />
    <hr />
    <Switch>
      <Route exact path={ROUTES.LANDING} component={LandingPage} />
      <Route path={ROUTES.SIGN_IN} component={SignInPage} />
      <Route path={ROUTES.HOME} component={HomePage} />
      <Route path={ROUTES.HOME2} component={HomePage2} />
    </Switch>
  </Router>
);
 
export default withAuthentication(App);

Firebase / context.js:

import React, { createContext } from 'react';

const FirebaseContext = createContext(null);

export const withFirebase = Component => props => (
  <FirebaseContext.Consumer>
    {firebase => <Component {...props} firebase={firebase} />}
  </FirebaseContext.Consumer>
);
export default FirebaseContext;

Firebase / firebase.js:

import app from 'firebase/app';
import 'firebase/auth';
import 'firebase/firestore';

const config = {
  apiKey: process.env.REACT_APP_API_KEY,
  authDomain: process.env.REACT_APP_AUTH_DOMAIN,
  databaseURL: process.env.REACT_APP_DATABASE_URL,
  projectId: process.env.REACT_APP_PROJECT_ID,
  storageBucket: process.env.REACT_APP_STORAGE_BUCKET,
  messagingSenderId: process.env.REACT_APP_MESSAGING_SENDER_ID,
  appId: process.env.REACT_APP_ID
};

class Firebase {
  constructor() {
    app.initializeApp(config);
    this.auth = app.auth();
    this.db = app.firestore();
  }
  // Auth API
  doSignInWithEmailAndPassword = (email, password) => this.auth.signInWithEmailAndPassword(email, password);
  doSignOut = () => this.auth.signOut();
}

export default Firebase;

Firebase / index.js:

import FirebaseContext, { withFirebase } from './context';
import Firebase from './firebase';
 
export default Firebase;
export { FirebaseContext, withFirebase };

Session / context.js:

import { createContext } from 'react';
 
const AuthUserContext = createContext(null);
 
export default AuthUserContext;

Session / index.js:

import AuthUserContext from './context';
import withAuthentication from './withAuthentication';
import withAuthorization from './withAuthorization';
 
export { AuthUserContext, withAuthentication, withAuthorization };

Session / withAuthentication.js:

import React, { useState, useEffect } from 'react';
 
import AuthUserContext from './context';
import { withFirebase } from '../Firebase';

const withAuthentication = Component => {
  const WithAuthentication = (props) => {
    const [authUser, setAuthUser] = useState(null);
    useEffect(() => {
      const listener = props.firebase.auth.onAuthStateChanged(authUser => {
        authUser
          ? setAuthUser(authUser)
          : setAuthUser(null);
      });
      return () => listener();
    }, []);
    return (
      <AuthUserContext.Provider value={authUser}>
        <Component {...props} />
      </AuthUserContext.Provider>
    );
  };
 
  return withFirebase(WithAuthentication);
};
 
export default withAuthentication;

Session / withAuthorization.js:

import React, { useEffect } from 'react';
 
import AuthUserContext from './context';
import { withFirebase } from '../Firebase';
import * as ROUTES from '../constants/routes';

const withAuthorization = (Component) => {
  const WithAuthorization = (props) => {
    useEffect(() => {
      const listener = props.firebase.auth.onAuthStateChanged(
        authUser => {
          if (!authUser) {
            props.history.push(ROUTES.SIGN_IN);
          }
        }
      );
      return () => listener();
    }, []);
    return (
      <AuthUserContext.Consumer>
        {authUser => !!authUser && <Component {...props} />}
      </AuthUserContext.Consumer>
    );
  };

  return withFirebase(WithAuthorization);
};
 
export default withAuthorization;

package.json:

{
  "name": "react-firebase-test",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^4.2.4",
    "@testing-library/react": "^9.5.0",
    "@testing-library/user-event": "^7.2.1",
    "firebase": "^7.14.5",
    "react": "^16.13.1",
    "react-dom": "^16.13.1",
    "react-router-dom": "^5.2.0",
    "react-scripts": "3.4.1",
    "recompose": "^0.30.0"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "devDependencies": {
    "dotenv": "^8.2.0"
  }
}
0 Answers
Related