SSR/PWA + Cookie + Auth



  • Hello,

    I need some help to implemant a persistant auth session in a SSR/PWA quasar app. I’m stuck with hydratation errors from the server because the cookie is not available on server side.

    I tried many things but i think i’m a little lost with all of these tests

    I tried with a boot file wich persist the state with the cookie but i still get the errors. I followed the indications from this topic : https://forum.quasar-framework.org/topic/3306/how-to-make-vuex-store-persist/t

    So far this is what i have done :

    #router/index.js
     Router.beforeEach(async (to, from, next) => {
        const requiresAuth = to.matched.some(route => route.meta.requiresAuth)
        if (requiresAuth) {
          await store
            .dispatch('user/startSession')
            .then(() => {
              next()
            })
            .catch(() => {
              next('/login')
            })
        } else {
          next()
        }
      })
    
    #user/actions.js
    export async function startSession ({ commit, dispatch, getters }) {
      return new Promise(function (resolve, reject) {
    
        let token = getters.getUserToken()
        if (token) {
          axios.defaults.headers.common['Authorization'] = 'Bearer ' + token
          commit(types.USER_TOKEN_CHANGED, { newToken: token })
          dispatch('me')
          resolve()
        } else {
          reject()
        }
      })
    }
    

    I don’t know why it persist all my state object. Path option doens’t seems to work in this boot file :

    #boot/persist-auth.js
    import { Cookies } from 'quasar'
    import createPersistedState from 'vuex-persistedstate'
    
    export default async ({ app, router, store, Vue, ssrContext }) => {
      const cookies = process.env.SERVER ? Cookies.parseSSR(ssrContext) : Cookies
      const options = { path: '/' }
      createPersistedState({
        path: [
          'user',
          'user.token',
          'user.user.token',
          'user.user',
          'User.user',
          'User.user.token'
        ],
        storage: {
          getItem: key => JSON.stringify(cookies.get(key)),
          setItem: (key, value) => cookies.set(key, value, options),
          removeItem: key => cookies.remove(key)
        }
      })(store)
    }
    
    

    With this i get a cookie with all my state object stored in the browser. But when i refresh my browser on a page that need user to get auth, the server render the login page while the browser remains on the page i was (url stay the same).

    Did i miss something about the cookies being server side rendered ?
    Also if i use the q-nossr component on my pages where auth is necessary will it solve my hydratation problems ?

    I saw this issue on Git for adding the ssrContext in the modules. But i do not understand the part of getting the module aware of it. With this i will not need to persist my state and i could check the cookie in my actions, maybe it could correct my issues
    https://github.com/quasarframework/quasar/issues/2285

    Thanks !



  • You may need to adjust the actions if you use modules as this way for a Vue/Vuex project with a single store.js file. But the way I did it is stick the below routerBackup code to the bottom of my router.js file(right to the bottom). And then add the subsequent code into my actions and mutations + states one by one.
    It enabled me to reload the data in the app each time the browser is shut. I have also noticed the issue you describe where certain details are not updating on time when used in certain .js files, This is why I used the below method, no more issues. The actions are asynchronous so no worry. If anyone has a better way of doing it, you are welcome to post it.

    routerBackup.beforeEach((to, from, next) => {
        store.dispatch('resetErrors');
        store.dispatch('fetchGlobalSettingsFromApi'); // Very important part
    
        let role = localStorage.getItem('rid');
        let accessToken = localStorage.getItem('accessToken');
    
        if (to.meta.requiresAuth) {
            if (!role || !accessToken) {
                routerBackup.push({path: '/login'});
            } else {
                if (to.meta.adminAuth) {
                    if (role === "admin") {
                        return next();
                    } else {
                        routerBackup.push({path: '/login'});
                    }
                } else if (to.meta.customerAuth) {
                    if (role === "customer") {
                        return next();
                    } else {
                        routerBackup.push({path: '/customer'});
                    }
                } else if (to.meta.userAuth) {
                    if (role === "user") {
                        return next();
                    } else {
                        routerBackup.push({path: '/user'});
                    }
                }
            }
        } else {
            return next();
        }
    
    });
    

    export default routerBackup;

    In my store, my fetchGlobalSettingsFromApi actions look like this:

        //FETCH GLOBAL SETTINGS
    
            fetchGlobalSettingsFromApi(context) {
    
                if (localStorage.getItem('homepage_title') === null
                    || localStorage.getItem('homepage_description') === null
                    || localStorage.getItem('homepage_keywords') === null
                    || localStorage.getItem('gdpr') === null
                    || localStorage.getItem('newsletter') === null
                    || localStorage.getItem('admin_email') === null) {
    
                    api.get('/loading')
                        .then(response => {
    
                            let homepage_title = response.data.data[0].homepage_title;
                            let homepage_description = response.data.data[0].homepage_description;
                            let homepage_keywords = response.data.data[0].homepage_keywords;
                            let gdpr = response.data.data[0].gdpr;
                            let newsletter = response.data.data[0].newsletter;
                            let admin_email = response.data.data[0].admin_email;
    
                            context.commit('fetchGlobalSettingsFromApi', {
                                homepage_title,
                                homepage_description,
                                homepage_keywords,
                                gdpr,
                                newsletter,
                                admin_email
                            });
                        })
                        .catch(error => {
                            if (error.response.status < 500) {
                                context.commit('apiErrorMessage', error);
                            } else {
                                context.commit('noServerError');
                            }
                        });
                } else {
    
                    let homepage_title = localStorage.getItem('homepage_title');
                    let homepage_description = localStorage.getItem('homepage_description');
                    let homepage_keywords = localStorage.getItem('homepage_keywords');
                    let gdpr = localStorage.getItem('gdpr');
                    let newsletter = localStorage.getItem('newsletter');
                    let admin_email = localStorage.getItem('admin_email');
    
                    context.commit('fetchGlobalSettingsFromApi', {
                        homepage_title,
                        homepage_description,
                        homepage_keywords,
                        gdpr,
                        newsletter,
                        admin_email
                    });
    
                }
            }
    

    Mutation:

    fetchGlobalSettingsFromApi(state, data) {
    
                let homepage_title = data.homepage_title;
                let homepage_description = data.homepage_description;
                let homepage_keywords = data.homepage_keywords;
                let gdpr = data.gdpr;
                let newsletter = data.newsletter;
                let admin_email = data.admin_email;
    
                localStorage.setItem('homepage_title', homepage_title);
                localStorage.setItem('homepage_description', homepage_description);
                localStorage.setItem('homepage_keywords', homepage_keywords);
                localStorage.setItem('gdpr', gdpr);
                localStorage.setItem('newsletter', newsletter);
                localStorage.setItem('admin_email', admin_email);
    
                state.homepage_title = localStorage.getItem('homepage_title');
                state.homepage_description = localStorage.getItem('homepage_description');
                state.homepage_keywords = localStorage.getItem('homepage_keywords');
                state.gdpr = localStorage.getItem('gdpr');
                state.newsletter = localStorage.getItem('newsletter');
                state.admin_email = localStorage.getItem('admin_email');
    
                state.isLoggedIn = localStorage.getItem('isLoggedIn');
                state.accessToken = localStorage.getItem('accessToken');
                state.refreshToken = localStorage.getItem('refreshToken');
                state.rid = localStorage.getItem('rid');
                state.name = localStorage.getItem('name');
    
            },
    

    I scratch ed me head for days on this and finally used this method, and now I have a “settings” area in my admin account where I load all my settings which are sent to my laravel app. Each time the app loads a page it checks for the settings. I did not notice any slowdown and the app is really fast.

    No idea if this is an ideal way of doing it but it worked great on my project.



  • Let me rephrase this… it does not load the settings each time or it would be a waste of resources, it only fetches via the api the settings if 1 of the setting is missing in the localstorage as shown here:

    fetchGlobalSettingsFromApi(context) {
    
                if (localStorage.getItem('homepage_title') === null
                    || localStorage.getItem('homepage_description') === null
                    || localStorage.getItem('homepage_keywords') === null
                    || localStorage.getItem('gdpr') === null
                    || localStorage.getItem('newsletter') === null
                    || localStorage.getItem('admin_email') === null) {
    
                    api.get('/loading')
                        .then(response => {
    
                            let homepage_title = response.data.data[0].homepage_title;
                            let homepage_description = response.data.data[0].homepage_description;
                            let homepage_keywords = response.data.data[0].homepage_keywords;
                            let gdpr = response.data.data[0].gdpr;
                            let newsletter = response.data.data[0].newsletter;
                            let admin_email = response.data.data[0].admin_email;
    
                            context.commit('fetchGlobalSettingsFromApi', {
                                homepage_title,
                                homepage_description,
                                homepage_keywords,
                                gdpr,
                                newsletter,
                                admin_email
                            });
                        })
                        .catch(error => {
                            if (error.response.status < 500) {
                                context.commit('apiErrorMessage', error);
                            } else {
                                context.commit('noServerError');
                            }
                        });
                } else {
    
                    let homepage_title = localStorage.getItem('homepage_title');
                    let homepage_description = localStorage.getItem('homepage_description');
                    let homepage_keywords = localStorage.getItem('homepage_keywords');
                    let gdpr = localStorage.getItem('gdpr');
                    let newsletter = localStorage.getItem('newsletter');
                    let admin_email = localStorage.getItem('admin_email');
    
                    context.commit('fetchGlobalSettingsFromApi', {
                        homepage_title,
                        homepage_description,
                        homepage_keywords,
                        gdpr,
                        newsletter,
                        admin_email
                    });
    
                }
            }
    


  • This post is deleted!

Log in to reply