testdrivenio / fastapi-vue

Single Page App with FastAPI and Vue.js
MIT License
265 stars 103 forks source link

Question - losing auth on refresh? #19

Open dgroo opened 9 months ago

dgroo commented 9 months ago

Howdy - first, thank you for the excellent tutorial. Has been a great launching off point to get me used to Vue/FastAPI (as a recovering manager/executive who hasn't coded up something wholly new in quite a while :) ).

I hope you don't mind a possibly silly question - if it's annoying, feel free to delete this issue.

I'm running into an issue which I've been trying to debug, but haven't been able to figure out - which is that whenever the page is refreshed, the app loses the user (and requires log in again).

I can't decide if this is expected behavior (I know it's sending an authentication cookie over) - and I realize that it may be, as I can't find any place that the app is interacting with local storage to maintain the user. So I figured I would ask. :)

Mostly just trying to figure if I missed something somewhere, or if it's just something I need to add.

Thanks again!

AndyQ commented 9 months ago

I've just been going through this and hit the same issue.

I've worked around it though by changing router where it checks whether the user is authenticated and if not redirects to the login page, and adding an initial check first to see if we are not authenticated, then call the viewMe service on the userStore (which uses the existing token to try to retrieve current user details) which then enables the next isAuthenticated check to pass.

No idea if this is a good/bad way of doing it though!

This is what I did (its using the Composable API and Pinia so slightly different but should easily be adapatable:

router.beforeEach(async (to, _, next) => {
  const userStore = useUserStore()

  if (to.matched.some(record => record.meta.requiresAuth)) {
    // If not authenticated (maybe refreshed page?)
    // Retry to get user details
    if (!userStore.isAuthenticated) {
      await userStore.viewMe();
    }

    if (userStore.isAuthenticated) {
      next();
      return;
    }
    next('/login');
  } else {
    next();
  }
});
dgroo commented 8 months ago
  1. Thank you! This solved it (with a couple of small tweaks I'll put below for others)

(2. Memo to self, actually look back at my question on github to see if anyone answered before haring off on another path - that would have saved me an hour :) )

Just for other n00bs since it took me a minute to work it out, if working from the raw setup of the tutorial, the code below should work (logging just for seeing what is happening).

router.beforeEach(async (to, _, next) => {
  if (to.matched.some(record => record.meta.requiresAuth)) {
    // If not authenticated (maybe refreshed page?)
    // Retry to get user details
    if (!store.getters.isAuthenticated) {
      console.log('Not authenticated');
      await store.dispatch('viewMe');
    }

    if (store.getters.isAuthenticated) {
      console.log('Is authenticated');
      next();
      return;
    }
    next('/login');
  } else {
    next();
  }
});
eharvey71 commented 7 months ago

Thank you for the fix. Unfortunately, this worked for all routes except a refresh from the "Home" ('/') route. I was able to fix it with this change (adding the to.path conditional) and now a refresh on ANY page will continue with a persistent user session. This totally feels hacky (not previous fixes, only mine) and I'm hoping there's another way to attack this from a Vuex perspective or perhaps a refactoring of the routes and views in some way.

router.beforeEach(async (to, _, next) => {
  if (to.matched.some(record => record.meta.requiresAuth) || to.path === '/') {

    if (!store.getters.isAuthenticated) {
      await store.dispatch('viewMe');
    } 

    if (store.getters.isAuthenticated) {
      next();
      return;
    }
    next('/login');
  } else {
    next();
  }
});