9

I am using a JWT Token auth system, and when I login I get the token like this:

axios.post('/login', data)
    .then(response => {
        localStorage.setItem('token', response.data.token);
   });

This works well and the token is saved in localStorage. However, the token is not included in the later requests. The Authorization header is Bearer null.

This is how I set up my global axios object.

window.axios = axios.create({
    baseURL: '/api/',
    timeout: 10000,
    headers: {
        'X-Requested-With': 'XMLHttpRequest',
        'X-CSRF-TOKEN': document.head.querySelector('meta[name="csrf-token"]').content,
        'Authorization': 'Bearer ' + localStorage.getItem('token')
    }
});

If I refresh the site, the token is set, and is used properly.

Edit:

I got it to work by removing the Authorization header from the create() method and instead using window.axios.defaults.headers.common['Authorization']. But now the same problem appears with Laravel Echo. I create the instance like this:

window.Echo = new Echo({
    broadcaster: 'pusher',
    key: 'xxx',
    cluster: 'eu',
    encrypted: true,
    namespace: 'xxx',
    auth: {
        headers: {
            'Authorization': 'Bearer ' + localStorage.getItem('token')
        }
    }
});

And I update the header like this:

window.setAuthToken = (token) => {
    window.axios.defaults.headers.Authorization = 'Bearer ' + token;
    window.Echo.options.auth.headers.Authorization = 'Bearer ' + token;
    localStorage.setItem('token', token);
}

The axios header is successfully updated, but not Echo.

Fredrik
  • 3,027
  • 8
  • 33
  • 66

3 Answers3

19

Use axios interceptors for this purpose. It will run for every request call.

Better to keep axios methods in a separate file and make call to it than using it directly in all components. This way we can replace axios with another library if we want with minimal effort. Here's what I'm doing in my project.

import axios from "axios";
import AuthService from "./auth";

import config from '../config'

const instance = axios.create({
  baseURL: config.apiServer.url,
  timeout: config.apiServer.timeout
});

instance.interceptors.request.use(
  config => {
    const token = AuthService.getToken();
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  error => Promise.reject(error)
);

const ApiService = {

  get(url) {
    return instance.get(url)
      .then(res => res)
      .catch(reason => Promise.reject(reason));
  },

  post(url, data) {
    return instance.post(url, data)
      .then(res => res)
      .catch(reason => Promise.reject(reason));
  },

  awaitAll() {
    return axios.all(Array.from(arguments))
      .then(axios.spread((...responses) => responses))
      .catch(reasons => Promise.reject(reasons));
  }

};

export default ApiService;

Now to use it in a component:

ApiService.get(YOUR_GET_URL)
      .then(res => {
        Console.log(res);
      ))
      .catch(reason => {
        console.log(reason);
      })
Drunken Daddy
  • 7,326
  • 14
  • 70
  • 104
8

The problem is that your are using localStorage.getItem('token') at page load. When you are setting it in localStorage, you have to update it in axios header.

window.axios = axios.create({
    baseURL: '/api/',
    timeout: 10000,
    headers: {
        'X-Requested-With': 'XMLHttpRequest',
        'X-CSRF-TOKEN': document.head.querySelector('meta[name="csrf-token"]').content,
        'Authorization': 'Bearer ' + localStorage.getItem('token')
    }
});

axios.post('/login', data)
    .then(response => {
        localStorage.setItem('token', response.data.token);
        window.axios.defaults.headers.common['Authorization'] = 'Bearer ' + localStorage.getItem('token');
    });
Vipin Kumar
  • 6,441
  • 1
  • 19
  • 25
  • This sounds logical, but it actually doesn't seem to work. The token is still `null`. – Fredrik Dec 08 '17 at 16:59
  • 1
    Can you create a minimal fiddler/plunker link? – Vipin Kumar Dec 08 '17 at 17:00
  • Well, it seems that the header is set if I, for example, set it to "Authorization_foo". So, maybe it is the *defaults* being overridden? – Fredrik Dec 08 '17 at 17:02
  • I got it to work by removing the `Authorization` header from the `create()` method and instead using `window.axios.defaults.headers.common['Authorization']`. However, now I basically have the same problem with *Laravel Echo*. See edit in question. – Fredrik Dec 08 '17 at 20:52
  • Thanks for saving my day bro, commended! – MONSTEEEER Dec 03 '20 at 06:21
7

I faced the same problem before and I found out that the file that contains my axios config was being loaded at the time of storing the token, so it was accessing it before it is stored.

The solution is, in axios config:

  const axiosInstance = axios.create({
  baseURL: `${API_BASE_URL}`,
  headers: {
    Accepted: 'appication/json',
    'Content-Type': 'application/json',
  },
});

axiosInstance.interceptors.request.use(
  (config) => {
    const token = localStorage.getItem('token');
    if (token) {
      config.headers.authorization = token;
    }
    return config;
  },
  (error) => Promise.reject(error),
);

export default axiosInstance;

After that, use this instance where you need to make a request.