Firebase auth.currentUser is null when loading the page, user loaded when authstatechange called after some milli seconds of page load

一笑奈何 提交于 2020-04-05 06:53:10

问题


I am using React with Firebase to develop a small web app. To take care of authentication, I am using context API and inside context I am adding loggedin user details.

AuthProvider.tsx

const AuthProvider: React.FC = props => {
  const [state, setState] = useState<IAuthContext>(authInitialState);
  console.log("Inside AuthProvider");
  useEffect(() => {
    auth.onAuthStateChanged( user => {
      console.log("Auth State changed and user is ----->", user);
      if (user) {
        console.log("User value updated to the context")
        setState({
            ...authInitialState,
          isAuthenticated:!!user,
          permissions:[],
          user:user
        });
      }
      const stateChange = {
        ...authInitialState,
        isAuthenticated: !!user,
        user
      };
      // if (!user) {
        return setState(stateChange);
      // }
    });
  }, []);
  console.log("Rendering AuthProvider", state);
  return (
    <AuthContext.Provider value={state}>{props.children}</AuthContext.Provider>
  );
};
export default AuthProvider;

AuthConsumer.tsx

const withAuthContext = (
  Component: React.ComponentClass<any> | React.FunctionComponent<any>
) => {
  console.log("Rendering AuthConsumer for ");
  return (props: any) => (
    <AuthContext.Consumer>
      {context => <Component {...props} context={context} />}
    </AuthContext.Consumer>
  );
};

export default withAuthContext;

PrivateRoute.tsx

interface PrivateRouteProps extends RouteProps {
    // tslint:disable-next-line:no-any
    component: any;
    context: IAuthContext;
}

const PrivateRoute = (props: PrivateRouteProps) => {
    const { component: Component, context: IAuthContext, ...rest } = props;
    console.log("Private route for ", props);
    return (
        <Route
            {...rest}
            render={(routeProps) =>
                props.context.isAuthenticated ? (
                    <Component {...routeProps} />
                ) : (
                    <Redirect
                        to={{
                            pathname: '/login',
                            state: { from: routeProps.location }
                        }}
                    />
                )
            }
        />
    );
};

export default withAuthContext(PrivateRoute);

App.tsx

return (
        <BrowserRouter>
            <div>
                <Switch>
                    <PublicRoute path="/frame" component={Frame} exact isAuthorized={true}/>
                    <Route path="/login" component={NewLogin} exact isAuthorized={true}/>
                    <PrivateRoute path="/nav" component={NavigationBar} exact/>
                    <PrivateRoute path="/dashboard" component={AnalyticsDashBoard} exact/>
                    <PrivateRoute path="/subscription" component={OrderSuccess} exact/>
                    <PrivateRoute path="/onboarding" component={OnBoarding} exact/>
                </Switch>
            </div>
        </BrowserRouter>
    );

The user is already loggedin, and session persistence set to local. The problem is, when I try localhost/subscription, which is a private route, context.isAuthenticated is false since "onAuthStateChanged" observer is not triggered yet, so its going to login page, but in few milliseconds, authStateChange triggered and context set, but its no use since application already navigated to login since privateroute thought user not loggedin. I would like to get knowledge on how to overcome this issue.


回答1:


When the page loads Firebase restores the user's credentials from local storage and checks with the server whether they are still valid. Since this is a call to the server, it may take some time and happens asynchronously. This is the reason it is normal that firebase.auth().currentUser is null until onAuthStateChanged fires.

The problem you have is that there are multiple reasons firebase.auth().currentUser can be null:

  1. firebase.auth().currentUser is null because the client just loaded and it's still checking the credentials with the server.
  2. firebase.auth().currentUser is null because the client checked the credentials against the server and the client isn't logged in.
  3. firebase.auth().currentUser is null because the user never signed in, so there are no credentials to check.

You want to navigate in case 2 and 3, but not case 1.

The typical solution is to not handle navigation until the first time onAuthStateChanged has fired. At that point you can be certain that the credentials were checked against the server, or that there were no credentials to check, in both of which cases you'll want to navigate to the sign-in page.


An additional option to speed this up is to store a small token in local storage yourself when the user signs in for the first time, and then read that when the app loads. If the token is present you can distinguish between case 1 and 3, and use that to navigate to the correct page a bit sooner.

For an example of this, see this I/O talk about Architecting Mobile Web Apps.



来源:https://stackoverflow.com/questions/60149287/firebase-auth-currentuser-is-null-when-loading-the-page-user-loaded-when-authst

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!