简体   繁体   中英

How to properly configure Firebase Auth in a single page application (SPA)?

I'm having difficulties understanding how to best implement Firebase Auth in a SPA web application. I'm new to both SPAs and Firebase.

My app consists of both secure pages and non-secure pages. The non-secure pages are for things like terms & conditions, privacy policy and forgot password.

Inside my app code, at the highest level eg /app.js , I'm importing a Firebase Auth configuration module as the first order of operation. This module contains the following function which listens for changes in authentication and acts accordingly.

firebase.auth().onAuthStateChanged(user => {
    if (!user) {
        Store.router.navigate("/login"); // <-- this is my problem
    } else {
        // get user data from Cloud Firestore
        // store user data locally
    }
});

This is my router at it's basic level:

router.on({
    '/': () => {
        // import module
    },
    '/login': () => {
        // import module
    },
    '/forgot-password': () => {
        // import module
    }
}).resolve();

Before I decided to use Firebase Auth, my router checked for authentication at each route and looked a little like this:

router.on({
    '/': () => {
        if (isAuthenticated) {
            // import module
        } else {
           router.navigate("/login")
        }
    },
    '/login': () => {
        if (!isAuthenticated) {
            // import module
        } else {
           router.navigate("/")
        }
    },
    '/forgot-password': () => {
        // import module
    }
}).resolve();

Every time a route changes using the Firebase Auth version of my app, the onAuthStateChanged listener receives an update and, if the user is logged out, it redirects them to the /login page. If logged in, it grabs the user's full profile from the database and stores it locally.

Now, this works brilliantly unless the user is logged out, is on the /login page, and wants to visit the /forgot-password page. When a user navigates to this page, or any other no-secure, public page, the authentication listener updates itself and redirects the user back to /login instantly and this is wrong.

This is highly undesirable but I really like the way this listener works other than that, as if/when a user has multiple tabs open and logs out of one, it returns all tabs back to /login .

How can I configure this listener, or reconfigure my app, to allow the public pages to be available too? And should I be unsubscribing from the listener?

I managed to solve the problem, so I'll share my findings here for others. I did however lose the functionality that returned all open tabs to the login page when they logged out of one but this does work better for my app that has public routes.

I now have a method in my User module called getCurrentUser() which is now where the onAuthStateChanged observable sits. Because I used the 'unsubscribe()` method, I can now call this as and when I need it without having it observing continuously.

getCurrentUser: () => {
    return new Promise((resolve, reject) => {
        const unsubscribe = firebase.auth().onAuthStateChanged(user => {
            unsubscribe();
            resolve(user);
        }, reject);
    })
}

In my router, I can now check the auth state by calling and waiting for User.getCurrentMethod() .

router.on({
    '/': async () => {
        if (await User.getCurrentUser()) {
            // import module
            // load HTML
        } else {
            router.navigate('/login')
        }
    },
    '/login': () => {
        ...
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM