Search code examples
axiosvuejs3pinia

Return to original request after refresh token


I make certain requests via Axios via a pinia store. These requests are via a timer, multiple requests can be sent. My server works with tokens that need to be refreshed. I have written an interceptor that checks the expiration time of the access token and sends a refresh token before it is invalid. All requests that happen while I handle the refresh token are stored in an array. Now this works, but I do not return to where the request was made (pinia store), so the data is not adjusted.

import axios from 'axios';
import {useAuthStore} from "@/stores/Authentication/AppAuthStore.js";
import VueJwtDecode from 'vue-jwt-decode';


axios.defaults.baseURL = import.meta.env.VITE_API_ENDPOINT;
axios.defaults.maxRedirects = 0;
let refreshing_token = null;
let myTimeout= null;
let busy = false;
let requestArray = [];
const baseURL = import.meta.env.VITE_API_ENDPOINT;


axios.interceptors.request.use( async req =>{
    if (req.url.endsWith("refresh")) {
        // if we are making a refresh token call, return it to prevent infinite loop
        return req;
    }

    const accesstoken = sessionStorage.getItem("accesstoken")
    if (accesstoken){
        req.headers.Authorization = `Bearer ${accesstoken}`;
        //const {header,payload} = useJwt(accesstoken)
        let user = VueJwtDecode.decode(accesstoken);
        const isExpired = Date.now() >= ((user.exp * 1000) - 10000)
        console.log(isExpired)
        if (!isExpired) return req;
        requestArray.push(req);
        console.log("is expired");
/*        refreshing_token = refreshing_token ? refreshing_token : refresh_token();
        let res  = await refreshing_token;
        refreshing_token = null;
        clearTimeout(myTimeout);*/
        if (!busy){
            busy = true;
            const refreshtoken = sessionStorage.getItem("refreshtoken")
            axios.post(baseURL + 'auth/refresh', {
                refresh_token : refreshtoken
            }).then((res) => {
                console.log(res);
                console.log("token refreshed : " + res.data.accesstoken);
                const authappstore = useAuthStore()
                authappstore.isAppOK = true;
                sessionStorage.setItem("accesstoken",res.data.accesstoken);
                sessionStorage.setItem("refreshtoken", res.data.refreshtoken);
                req.headers.Authorization = `Bearer ${res.data.accesstoken}`;
                if (requestArray.length !== 0) {
                    requestArray.forEach(x => {
                        try {
                            console.log(x);
                            axios(x)
                        } catch (e) {
                            console.log(e)
                        }
                    });
                    requestArray = [];

                }
            }) .catch((err) => {
                const errStatus = err.statusCode || 500;
                const errMsg = err.message || 'Something went wrong';
                console.log(errMsg)
            }) .finally(() => {
                busy = false;
            });
        }


    }
    else {
        return req
    }
    


})

export default axios;

I have no idea how to do that?


Solution

  • I just provide an idea ,you need to wait for a new access token When a request's access token expired, and go on that request (not push them to an array), so we need to return a Promise in the request interceptors to make the request in a waiting status, and we need create a CustomEvent ,when the request of getRefreshToken completed ,the CustomEvent triggered, the Promise will be resolved at this time, and the request will go on.

    import axios from 'axios';
    import {
      useAuthStore
    } from "@/stores/Authentication/AppAuthStore.js";
    import VueJwtDecode from 'vue-jwt-decode';
    
    axios.defaults.baseURL =
      import.meta.env.VITE_API_ENDPOINT;
    axios.defaults.maxRedirects = 0;
    let refreshing_token = null;
    let myTimeout = null;
    let busy = false;
    const baseURL =
      import.meta.env.VITE_API_ENDPOINT;
    const getRefreshToken = new CustomEvent('getRefreshToken');
    axios.interceptors.request.use(async req => {
      if (req.url.endsWith("refresh")) {
        // if we are making a refresh token call, return it to prevent infinite loop
        return req;
      }
      const accesstoken = sessionStorage.getItem("accesstoken")
      if (accesstoken) {
        req.headers.Authorization = `Bearer ${accesstoken}`;
        //const {header,payload} = useJwt(accesstoken)
        let user = VueJwtDecode.decode(accesstoken);
        const isExpired = Date.now() >= ((user.exp * 1000) - 10000)
        console.log(isExpired)
        if (!isExpired) return req;
        return refresh(req)
      } else {
        return req
      }
    })
    
    function refresh(req) {
      if (!busy) {
        busy = true;
        const refreshtoken = sessionStorage.getItem("refreshtoken")
        axios.post(baseURL + 'auth/refresh', {
          refresh_token: refreshtoken
        }).then((res) => {
          console.log(res);
          console.log("token refreshed : " + res.data.accesstoken);
          const authappstore = useAuthStore()
          authappstore.isAppOK = true;
          sessionStorage.setItem("accesstoken", res.data.accesstoken);
          sessionStorage.setItem("refreshtoken", res.data.refreshtoken);
          window.dispatchEvent(getRefreshToken)
        }).catch((err) => {
          const errStatus = err.statusCode || 500;
          const errMsg = err.message || 'Something went wrong';
          console.log(errMsg)
        }).finally(() => {
          busy = false;
        });
      }
      return new Promise(res => {
        function send() {
          const accesstoken = sessionStorage.getItem("accesstoken");
          req.headers.Authorization = `Bearer ${accesstoken}`;
          res(req)
          window.removeEventListener('getRefreshToken', send)
        }
        window.addEventListener('getRefreshToken', send)
      })
    }