0

After logging in I call await router.push('/'); to redirect to the home page where I load users and I get this error GET http://localhost:8080/users 401 then when I refrehs the page in the exact same component I get the data just fine with a 200 status. I'm not sure what's going on

async login (username, password) {

    const response = await axios.post('/auth/login', {
        username: username,
        password: password
    });

    this.user = response.data;
    localStorage.setItem('user', JSON.stringify(this.user));

    await router.push('/');
},

This is the function I call after logging in

This is the router.js

import { createRouter, createWebHistory } from 'vue-router';
import Login from '../views/Auth/Login.vue';
import { useAuthStore } from '../stores/auth.store.js';
import IndexUser from "../views/Users/IndexUser.vue";
import IndexHive from '../views/Hives/IndexHive.vue';

const routes = [
    { path: '/', name: 'Home', component: IndexUser },
    { path: '/login', name: 'Login', component: Login },
    { path: '/users', redirect: { name: 'Home' } },
    { path: '/users/create', name: 'CreateUser', component: CreateUser },
    { path: '/hives', name: 'IndexHive', component: IndexHive }

];

import CreateUser from '../views/Users/CreateUser.vue';

const router = createRouter({
    history: createWebHistory(),
    routes
});

router.beforeEach(to => {

    const authStore = useAuthStore();

    const publicPages = ['/login'];
    const authRequired = !publicPages.includes(to.path);

    if (authRequired && !authStore.user) {
        return '/login';
    }
})

export default router;

This is the component I redirect to after logging in

onMounted( async () => {
  const response = await axios.get('/users');
  users.value = response.data;
})

Devtools

enter image description here

Network tab

enter image description here

Axios Error

enter image description here

details of request/response

enter image description here

enter image description here

Response of login

enter image description here

16
  • I'm pretty sure that if you check your network tab (browser devtools), you'll see that your 401 error is related to CORS. If so, check a solution here. Commented Nov 8, 2022 at 15:29
  • @kissu but when I refresh the page I get the data after getting that error. And I already enabled the cors for the client in my backend Commented Nov 8, 2022 at 15:32
  • What do you see in your devtools alongside the error? For me, it's a CORS issue because you don't have it initially (while having a running server), then it happens because you're doing client-side navigation. Does it happen if you come back to the page with a client-side navigation (probably is). Commented Nov 8, 2022 at 15:35
  • I just saved a new user like await axios.post('/users', user); await router.push('/'); to return back to the component and the results show just fine Commented Nov 8, 2022 at 15:38
  • 1
    @kissu I attached the previous response again. All requests work fine with Postman. Thank you for your help anyway :) Commented Nov 8, 2022 at 16:22

1 Answer 1

1

Update 2

Having seen the code, I think the problem is here:

import axios from "axios";

axios.defaults.baseURL = import.meta.env.VITE_API_URL;

if (localStorage.getItem('user')) {
    const user = JSON.parse(localStorage.getItem('user'));
    axios.defaults.headers.common['Authorization'] = `Bearer ${user?.accessToken}`;
}

this will read the axios.defaults.headers when the helpers/axios.js file is loaded. This is why axios.get('/users'); only works on second load, or rather only when the authentication is already loaded into localStorage. A change to the user object or a local storage will not update since this code only runs once at the beginning, the change to axios.defaults.headers needs to be dynamic.

Update

if setTimeout didn't work that could be due to a different issue. Also, if your request works a second time, but it also works if the authentication is passed directly, it seems to me that it has something to do with the authentication being handled implicitly.

I think what's happening is that you are creating multiple instances of axios and relying on shared authentication

// create single axios instance
export const api = axios.create({
   withCredentials: true,
   baseURL: BASE_URL // optional
})

// then use
await api.post('/auth/login', {
  username: username,
  password: password
});

// and 
await api.get('/users');

This might make the axios instance remember the authentication information between calls. It may still require handling race condition if you have an app that doesn't wait on the login request to finish.


I think this is just an issue with a race condition

POST:/login and GET:/users requests appear to be done in parallel.

onMounted( async () => {
  // this should wait until the `login` has been handled
  const response = await axios.get('/users');
  users.value = response.data;
})

I don't see how you call login so can't offer the the exact solution, but if you can store the login request state as a reactive variable, you can do something like

watch: {
  loginState:{
    immediate: true
    handler(value){
      if (value === LOADED) {
        const response = await axios.get('/users');
        users.value = response.data;
      }
    }
  }
})

here's what the changes to the authStore might look like

export const STATES = {
  INIT:"INIT",
  PROCESSING:"PROCESSING",
  ERROR:"ERROR",
  LOADED:"LOADED",
}
export const loginState = ref(STATES.INIT);

async login (username, password) {
    loginState.value = STATES.PROCESSING
    try{
      const response = await axios.post('/auth/login', {
        username: username,
        password: password
      });
      loginState.value = STATES.LOADED
      this.user = response.data;
      localStorage.setItem('user', JSON.stringify(this.user));

      await router.push('/');
    }catch(e){
      // handle error
      loginState.value = STATES.ERROR
    }
},
Sign up to request clarification or add additional context in comments.

14 Comments

But how should it wait? I tried to set a timeout but not working
one approach is to use watch instead of onMounted, updated answer with options API example
My login is in another file auth.store.js here's the public github repo
I recommend validating first if that's the case. To do that, you could add a setTimeout before making the users request. If it works then my assertion is likely correct and then you can figure out how to solve for that. If not, then the issue may be something else. But it's important to validate first before you start looking into implementing a fix. Fixing the problem, however may fall out of the scope of the question
I found a hard time translating your code to the composition API but I tried the setTimeout like onMounted( async () => { setTimeout(() => {}, 5000); const response = await axios.get('/users'); users.value = response.data; }) but still getting the error
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.