1

I'm creating a redux middleware to refresh the token before any request if the token is expired.
The problem is that after the first call I always get error 400 bad request, but if did another call (It's a pagination requrest) it works fine because the token was refreshed.
I noticed that the Axios request gets fired before the new token comes in and before setting the new Axios Authorization header, so I tried to make the middleware async-await, but still no luck ...

import {
    getJWT,
    isTokenExpired
} from "../util/helpers";
import Cookies from "js-cookie";
import axios from "axios";

const refreshTokenMiddleware = store => next => async action => {
    if (!action.excludeFromRefresh) {
        const token = getJWT();
        if (token && isTokenExpired()) {
            try {
                const { data } = await axios.post("auth/refresh");
                axios.defaults.headers.common[
                    "Authorization"
                ] = `Bearer ${data.data.access_token}`;
                Cookies.set("AppToken", data.data.access_token);
                next(action);
            } catch (error) {
                console.log(error);
            }

        } else {
            next(action);
        }
    } else {
        next(action);
    }
};

export default refreshTokenMiddleware;

I also suspect that the Axios request gets called with the old Token the first time.
What am I doing wrong here?

=====================

Edit

I tried to ditch the middleware all together and implement axios interceptors, since the middleware doesn't catch the axios request ...

axios.interceptors.request.use(
    function(config) {
        // Do something before request is sent
        if (
            config.url !== "auth/refresh" &&
            config.url !== "auth/login" &&
            config.url !== "auth/register"
        ) {
            const token = getJWT();

            if (token && isTokenExpired()) {
                axios.post("auth/refresh").then(res => {
                    config.headers.Authorization = `Bearer ${res.data.data.access_token}`;
                    console.log(
                        "Inside the refresh in interceptor",
                        res.data.data.access_token
                    );
                    return config;
                });
            }
        }
        return config;
    },
    function(error) {
        // Do something with request error
        return Promise.reject(error);
    }
);

But that also doesn't seem to work, the request fires with the old Token in the Authorization header on the first time.

What can I do?

Ruby
  • 2,207
  • 12
  • 42
  • 71
  • Your second solution will always fail since the interceptor is not async - it never waits for `auth/refresh` call. Your choice is to change it to a function which returns a Promise: https://stackoverflow.com/a/36089100/2404452 – blaz Oct 12 '19 at 17:28
  • For your first solution with Redux middleware, check version of your axios. Someone observed strange behavior with updating `axios.default.headers.common` in a recent version here: https://github.com/axios/axios/issues/209#issuecomment-462177458 – blaz Oct 12 '19 at 17:29
  • Also for your second solution, I am not certain if chage in `config.headers.Authorization` will be applied globally, i.e. the new token is preserved for next API calls. – blaz Oct 12 '19 at 17:31
  • it's been a long time, but you are not returning the `axios.post("auth/refresh")...` . the `then` part returns a config but you have to also return the promise itself `return axios.post("auth/refresh")...` – sertsedat Jul 18 '20 at 20:26

2 Answers2

1

For anyone interested who may come across this post, I managed to solve my issue using this Axios interceptor ... https://gist.github.com/Godofbrowser/bf118322301af3fc334437c683887c5f

Ruby
  • 2,207
  • 12
  • 42
  • 71
0

At first glance, this if statement is somehow wrong cause you trying to check if url have three different value at once, I think you should use || operator here.