vue.jsaxiosoktahttp-response-codes

How to handle Axios 401 response and Okta Vue SDK


I am using Okta to manage authentication in my vue 3 application and for all my api requests to my api I use axios. It is unclear though how to handle 401 responses from my api. If a users token become stale and they go to another page in the application which does an axios request the response is 401 because the token is no longer good. My idea was to check the status code of every response in all of my axios requests and if its 401 then refresh the page however this might be an issue that I am getting my token on the components creation for all axios requests and its not updating along with the automatic stuff thats built into the vue okta sdk. I am just not sure how I can get the access token every request without coding that line into each axios request. I get my access token at the beginning of every component via the following:

  const oktaAuth = getCurrentInstance()?.appContext.app.config.globalProperties.$auth as 

OktaAuth
  const accessToken = oktaAuth.getAccessToken();
  const config = {
    headers:{
      Authorization: 'Bearer ' + accessToken,
    }
  };

Then I use it as follows:

      //Certification Review
  const getCertificationReview = async (mmcid :  number) : Promise<CertificationReview | null> => {
    const data = api.get('/CertificationReview/' + mmcid, config)
      .then(response => { 
        const apiResponse : ApiResponse = response.data
        const certificationReview : CertificationReview = apiResponse.data
        return certificationReview
      }).catch(error => {
        if(error.response.status == 400) {
          router.push({ name: 'Error'})
        }
        return null
      })
    return data
  }

My axios config using interceptors is as follows:

    api.interceptors.request.use(function (config) {
  try{
    
    console.log('test')
    
    const oktaAuth = getCurrentInstance()?.appContext.app.config.globalProperties.$auth as OktaAuth
    const accessToken = oktaAuth.getAccessToken();
  
    config.headers.Authorization = 'Bearer ' + accessToken
  } catch (error){
     console.log('errorrrr')
  }

  return config;
});

However, getAccessToken() always creates an error unless I do this in my components directly. If I use the code below and grab the access token directly from localStorage it works but I was not sure if this token is always valid or not and if the sdk updates it when running something under the hood so if I should not rely on it via the interceptor:

    api.interceptors.request.use(function (config) {
  try{
    
    const accessToken = JSON.parse(localStorage.getItem('okta-token-storage')!).accessToken.accessToken;
    config.headers.Authorization = 'Bearer ' + accessToken

  } catch (error){
     console.log('Failed to grab Okta access token')
  }

  return config;
});

Solution

  • You can export a customized instance of axios and use it wherever is needed. What you do is add request and response interceptors, for request and response handling. Request interceptor is executed before each request, whereas response interceptor is executed when a response is received, if response is successfull it lets it go, if not, you can do something like this as i have done. Below given is a code snippet from my api file, from where I am exporting a customized instance of axios.

    /*-----api.js-------**/
    import axios from 'axios';
    import router from "../router/index.js";
    
    const api = axios.create({
      baseURL: 'http://localhost:8000/api', 
      headers: {
        'Content-Type': 'application/json',
      },
    });
    
    api.interceptors.request.use(config => {
      const token = localStorage.getItem('token');
      if (token) {
        config.headers.Authorization = `Bearer ${token}`; // Attach the token as an Authorization header
      }
      return config;
    })
    
    api.interceptors.response.use(response => {
      return response;
    }, error => {
      if (error.response.status === 401) {
      //  localStorage.removeItem('token');
      //  localStorage.removeItem('user');
        router.push({name: 'login'}) // if 401, takes to login page
      }
      throw error;
    })
    
    export default api;
    

    If using typescript, you can add the sepcific types where needed