Search code examples
javascriptreactjsjwtaxiospassport.js

Axios: Using baseUrl in a separate file causes problems with setting authentication token in axios request headers


I used to write the API Url inside the request like this:

export const getAllAdmins2 = () => dispatch => {
  axios
    .get("http://baseUrl:3083/api/admin/getAllAdmins")
    .then(retrievedAdmins => {
      console.log("getAllAdmins2");
      console.log(retrievedAdmins);
      // this.setState({
      //   usersInformation: retrievedAdmins.data
      // });
    })
    .catch(error => console.log(error));
};

But, this could be improved by setting the URL in a separate file :

conf/api.js

import axios from "axios";

export default axios.create({
  baseURL: `http://baseUrl:3083`
});

And so I changed the API request to this:

import API from "../conf/api";

export const getAllAdmins = () => dispatch => {
  API.get("api/admin/getAllAdmins")
    .then(retrievedAdmins => {
      console.log("retrievedAdmins insideAPI.get(api/admin/getAllAdmin)");
      console.log(retrievedAdmins);
      // this.setState({
      //   usersInformation: retrievedAdmins.data
      // });
    })
    .catch(error => console.log(error));
};

But, this caused a strange problem: The code that was responsible for automatically setting the authentication token in each request no longer does its job. In the backend, the token is undefined:

 2020-02-28T17:49:41+0100 <notice> TokenServices.js:76 Object.HasValidToken Access Token:undefined

This is the code that's responsible for setting the authentication header:

App.js

if (localStorage.jwtToken) {
  // Set auth token header auth
  setAuthToken(localStorage.jwtToken);

  // Decode token and get user info and expiration
  const decoded = jwt_decode(localStorage.jwtToken);
  // Set User and is Authenticated
  store.dispatch(setCurrentUser(decoded));
  // Now if you reload a page, if the user has already logged-in you will still have his info in he state

  // Check for expired token
  const currentTime = Date.now() / 1000;
  if (decoded.exp < currentTime) {
    // Logout User
    store.dispatch(logoutUser());
    // Redirect to login
    window.location.href = "/login";
  }
}

utils/setAuthToken.js

// What we will do here will prevent us from manually making sure of having the token inside each relevant request
// If we're logged-in, we can call this function and it will always attach that authorization header
// TODO: Need to make sure that the token is stored inside x-access-token
import axios from "axios";

const setAuthToken = token => {
  console.log("setAuthToken is called");
  if (token) {
    // Apply to every request
    axios.defaults.headers.common = {
      // Authorization: token,
      //TODO: Not sure about this
      "x-access-token": token
    };
    console.log("token is: ");
    console.log(token);
    console.log("axios.defaults.headers.common: ");
    console.log(axios.defaults.headers.common);
  } else {
    // Delete the Auth header
    axios.defaults.headers.common = {
      //Authorization: token,
      "x-access-token": token
    };
  }
};
export default setAuthToken;

And this is the logging result of the setAuthToken:

setAuthToken.js:7 setAuthToken is called
setAuthToken.js:15 token is: 
setAuthToken.js:16 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwibm9tIjoiYXZlbXBhY2UiLCJlbWFpbCI6ImF2ZW1wYWNlQGF2ZW1wYWNlLmNvbSIsIm1vdF9kZV9wYXNzZSI6IlUyRnNkR1ZrWDEvRGdCN3JUUjB1YTJvc1BIcm1hRkswN3pjTGd5aSsxcE09IiwibGFzdF9sb2dpbiI6bnVsbCwicm9sZSI6InN1cGVyX3VzZXIiLCJjcmVhdGVkQXQiOiIyMDIwLTAyLTI4VDA5OjQwOjE4LjM3MFoiLCJ1cGRhdGVkQXQiOiIyMDIwLTAyLTI4VDA5OjQwOjE4LjM3MFoiLCJpYXQiOjE1ODI5MDg0NjAsImV4cCI6MTU4MjkxMjA2MH0.8Btk4f0Bdw-pM2qGEbk0s5V-u3jBugIHYDKH8aoDzW8
setAuthToken.js:17 axios.defaults.headers.common: 
setAuthToken.js:18 {x-access-token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwib…A2MH0.8Btk4f0Bdw-pM2qGEbk0s5V-u3jBugIHYDKH8aoDzW8"}

It seems to be working. However, as I said, in the backend when I use getAllAdmins that uses the new implementation of the axios url, the token is undefined in the backend. But, when I use getAllAdmins2 that uses a direct implementation of the axios url, the code is well received in the backend and everything works fine.


Solution

  • Your issue is likely due to the fact that the axios instance in conf/api.js is created before you set the token in axios.defaults so your instance will be unaffected. I.e. your order is:

    • Create axios instance without token (done on include/comiple of api.js)
    • Load token and set defaults

    Since you essentially have a singleton axios instance, you can instead set it's defaults in place of the global defaults when the token is loaded, i.e.,

    Before:

    // This is setting the defaults used when creating instance and the global axios is used
    axios.defaults.headers.common = {
    ...
    

    After:

    import API from "../conf/api";
    
    ...
    // This will set the the defaults of your singleton instance
    API.defaults.headers.common = {
    ...
    

    The above should fix your issue with your current setup but an alternate option is to go back to using the global axios (e.g. axios.get) and instead set the base URL on it's defaults, i.e.:

    // During app init:
    axios.defaults.baseURL = 'http://your.base.url';