No More Posting New Topics!

If you have a question or an issue, please start a thread in our Github Discussions Forum.
This forum is closed for new threads/ topics.

Navigation

    Quasar Framework

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search

    SSR/PWA + Cookie + Auth

    Help
    2
    4
    1715
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • X
      Xteyse last edited by Xteyse

      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 !

      1 Reply Last reply Reply Quote 0
      • B
        bambinou last edited by

        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.

        1 Reply Last reply Reply Quote 0
        • B
          bambinou last edited by bambinou

          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
                          });
          
                      }
                  }
          
          1 Reply Last reply Reply Quote 0
          • X
            Xteyse last edited by

            This post is deleted!
            1 Reply Last reply Reply Quote 0
            • First post
              Last post