hang-coder
hang-coder

Reputation: 440

Use firebase.auth().setPersistence(firebase.auth.Auth.Persistence.LOCAL) however nothing is persisted on refresh

I used firebase.auth().setPersistence(firebase.auth.Auth.Persistence.LOCAL) however log in is NOT persisted on page refresh or close/reopen. It is just there and do nothing.

Anyone can help with it, please ...

export const loginUser = (creds) => (dispatch) => {
    dispatch(requestLogin(creds))
    firebase.auth().setPersistence(firebase.auth.Auth.Persistence.LOCAL)
        .then(() => {
            // New sign-in will be persisted with local persistence.
            return firebase.auth().signInWithEmailAndPassword(creds.username, creds.password)
                .then(() => {
                    var user = firebase.auth().currentUser;
                    if (user.emailVerified) {
                        localStorage.setItem('user', JSON.stringify(user));
                        // Dispatch the success action
                        dispatch(fetchFavorites()); 
                        dispatch(receiveLogin(user));
                    } else {
                        firebase.auth().signOut()
                        alert('Your email can not be verified.')
                    }
                })
                .catch(error => { dispatch(loginError(error.message)); alert(error) })
        })
        .catch(error => { alert(error) });
}

Upvotes: 3

Views: 2414

Answers (1)

hang-coder
hang-coder

Reputation: 440

OMG. It crunched me a full 6 hours :). 2hrs before yelling for rescue here then 4hrs after to fully solve it myself and clean code throughout react => redux => firebase.

Below is the full round solution and will work to

  • Persist and give auto login for all login types email/password, via google login, via facebook login ...etc

Step by step:

  1. Important: when set persistence, it is important to put this command firebase.auth().setPersistence(firebase.auth.Auth.Persistence.LOCAL) in firebase configuration. Do not use it with signup, login functions. You want it to umbrella above your whole web/app auth, not piece by piece of inner code

Below is my firebase configuration full code. You can see that I set persistence condition above everything else

import { config } from './config';
import firebase from 'firebase';

firebase.initializeApp(config);
firebase.auth().setPersistence(firebase.auth.Auth.Persistence.LOCAL);

const settings = {timestampsInSnapshots: true};
firebase.firestore().settings(settings);
export const firestore = firebase.firestore();
export const firebasestore = firebase.firestore;
export const auth = firebase.auth();
export const fireauth = firebase.auth;
export { firebase };
  1. Remember up till now we already have login persisted BUT IT IS ONLY WITH GOOGLE. ~ 4hrs ago I was still thinking that is enuf, lmao :). Well need to go and make react and redux knows about this info, update state for log in, update state for user, update state for data fetching for this current user, update data render for this current user in all UI components.

  2. Go to the component which manages authentication. My file is "HeaderComponent.js". It is the navigation bar where the login button is.

Here is what I include in "HeaderComponent.js" to update for redux:

import { connect } from 'react-redux';
import { fetchFavorites, receiveLogin } from '../redux/ActionCreators';

const mapStateToProps = state => {  
    return {
        favorites: state.favorites,
        auth: state.auth,
    }
}
const mapDispatchToProps = (dispatch) => ({  
    fetchFavorites: () => dispatch(fetchFavorites()),
    receiveLogin: (user) => { dispatch(receiveLogin(user)) },
});

Explanation:

  • auth: redux reducer for everything for authentication. It produces current state for state = { isLoading: false, isAuthenticated: false, user: null, errMess: null }
  • favorites: redux reducer for the user saved data. For example in my case, favorites are the food dishes that they love and saved to their account. If you do an e-commerce store, it can be user purchase history, user profile.
  • fetchFavorites: redux action to fetch favorites for user
  • receiveLogin(user): redux action to add the user in redux store to render to the web. Remember until now the user only known by Google auth. Your web/app doesnt know about it yet.
  1. After pull redux into "HeaderComponent.js", use componentDidMount() if 'Header' code is written as a class component or useEffect() if a function. I wrote it as a component so below I use componentDidMount().

I reinforced web security with request to satisfy email verification before user can be logged in. This is optional

componentDidMount() {
        firebase.auth().onAuthStateChanged((user) => {
            if (user) {
                if (user.emailVerified) {
                    localStorage.setItem('user', JSON.stringify(user));
                    // Dispatch the success action
                    this.props.fetchFavorites();
                    this.props.receiveLogin(user); // update state. isAuthenticated=true, user=currentUser
                } else {
                    firebase.auth().signOut()
                    alert('Your email can not be verified.')
                }
            } else {
                // User is signed out
                firebase.auth().signOut()
            }
        });
    }
  1. Connect this 'Header' component to redux store:
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Header));

Explanation:

  • withRouter: to allow routing to work with redux.
  • connect, mapStateToProps, mapDispatchToProps are standards for react to work with redux
  1. Handle log out properly. Below code/logic needs to put in the app component/main component
componentWillUnmount() {
    this.props.logoutUser();
  }

Upvotes: 2

Related Questions