Coder Social home page Coder Social logo

flyrell / axios-auth-refresh Goto Github PK

View Code? Open in Web Editor NEW
1.0K 10.0 85.0 2.56 MB

Library that helps you implement automatic refresh of authorization via axios interceptors. You can easily intercept the original request when it fails, refresh the authorization and continue with the original request, without user even noticing.

License: MIT License

JavaScript 3.26% TypeScript 96.43% Shell 0.30%
axios axios-plugin interceptor stalled-requests token authentication middleware http-interceptor typescript

axios-auth-refresh's People

Contributors

antonkomarev avatar darkbasic avatar dependabot[bot] avatar elliottcarlson avatar flyrell avatar gund avatar markvesterskov avatar mjsarfatti avatar nathanpovo avatar nkanetka avatar reichhartd avatar sdornan avatar sonerokur avatar steveninc avatar swilliams96 avatar wuservices avatar zhuoqi-chen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

axios-auth-refresh's Issues

Axios error.response is null

I have a POST request that sends multipart/form-data.

When the server returns 401 to this call, Axios throw a "network error"

it's a known problem in Axios

so your interceptor check

 if (!error.response || !options.statusCodes.includes(parseInt(error.response.status))) {
        return false;
    }

And because the error.response is null the interceptor doesn't work

What should I do?

[2.x] Failed config mutation in refreshAuthCall

In v2 beta there is a code block where failed request is re-sending:

.then(() => {
    error.config.skipAuthRefresh = true;
    return axios(error.response.config);
});

I found an issue in this implementation. In my application refreshAuthCall method requests new access token and stores it in the local storage. New access token injects to the request config with request interceptor, but this interceptor is ignored in current implementation because new Axios instance without any interceptors is using for re-sending.

If initial Axios instance will be used in that case there is a recursion loop:

.then(() => {
    error.config.skipAuthRefresh = true;
    return instance(error.response.config);
});

I can see another one issue with using initial instance. Failed request config state could be already mutated and if we will pass it to the initial instance with all interceptors there could be raised another issue because we will try to mutate already mutated data. From this perspective current implementation seems okay.

But on the other hand if new Axios instance is used for re-sending request - then it wouldn't be handled by response interceptors which were used in initial Axios instance. And there will be issues with response handling.

Another one edge case, that I don't want to mutate failedRequest.response.config in refreshAuthCall method because it breaks single responsibility and this kind of object mutation could lead to unexpected behavior. With current implementation it will require additional property or option for preparing request for re-sending. If initial Axios instance will be used for re-sending - then this case will disappear.

Any ideas how to deal with it?

Not retrying last request in React Native

Hi man,
Before that, thanks for this special plugin ๐Ÿ‘ :)

So basically, I'm not importing this plugin and instead I copy index.js in src and create a new file because I need to pass store (redux) parameter. I have tried to install this plugin and pass params but it failed.
All these codes are working except it doesn't retry the last request. If I'm trying another request it succeeds. I import this file with axiosConfig(store).

Thanks for your help.

/** @type {Object} */
const defaults = {
  /** @type {Number[]} */
  statusCodes: [
    401 // Unauthorized
  ]
};

import axios from "axios";

function changeToken(token) {
  return {
    type: "CHANGE_TOKEN",
    payload: token
  };
}

// Function that will be called to refresh authorization
const refreshTokenCall = (error, store) =>
  axios
    .get(`request-new-token`, undefined, {
      headers: {
        "USER": error.config.headers["USER"],
        "TOKEN": error.config.headers["TOKEN"]
      }
    })
    .then(response => {
      const { token } = response.data.data;

      store.dispatch(changeToken(token));
      error.response.config.headers["TOKEN"] = token;
      error.config.headers["TOKEN"] = token;

      // console.log("ERROR RESPONSE");
      // console.log(error.response.config);
      // console.log("ERROR CONFIG");
      // console.log(error.config);

      return Promise.resolve();
    });

function errorToken(store, options = {}) {
  const { userid, token } = store.getState().auth.user;
  axios.defaults.headers.common["USER"] = userid;
  axios.defaults.headers.common["TOKEN"] = token;

  const id = axios.interceptors.response.use(
    res => res,
    error => {
      // Reject promise if the error status is not in options.ports or defaults.ports
      const statusCodes =
        options.hasOwnProperty("statusCodes") && options.statusCodes.length
          ? options.statusCodes
          : defaults.statusCodes;

      // retry fetch
      // error.config._retry = true;

      if (
        !error.response ||
        (error.response.status &&
          statusCodes.indexOf(+error.response.status) === -1)
      ) {
        return Promise.reject(error);
      }

      // Remove the interceptor to prevent a loop
      // in case token refresh also causes the 401
      axios.interceptors.response.eject(id);

      const refreshCall = refreshTokenCall(error, store);

      // Create interceptor that will bind all the others requests
      // until refreshTokenCall is resolved
      const requestQueueInterceptorId = axios.interceptors.request.use(
        request => refreshCall.then(() => request)
      );

      // When response code is 401 (Unauthorized), try to refresh the token.
      return refreshCall
        .then(() => {
          axios.interceptors.request.eject(requestQueueInterceptorId);
          return axios(error.response.config);
        })
        .catch(error => {
          axios.interceptors.request.eject(requestQueueInterceptorId);
          return Promise.reject(error);
        })
        .finally(() => errorToken(store));
    }
  );
}

export default errorToken;

Intercepted Request Doesn't Refire, Goes into Infinite Loop

My refresh Auth Logic is built like this.

    const refreshTokenRequest = new AuthApi({ basePath: SURELY_API_BASE_URL });
    const storedRefreshToken = localStorage.getItem(REFRESH_TOKEN_ACCESSOR);
    if (storedRefreshToken && storedRefreshToken !== '') {
        return refreshTokenRequest
            .refreshToken({ refreshToken: storedRefreshToken })
            .then((tokenRefreshResponse) => {
                setTokens(tokenRefreshResponse.data);
                failedRequest.response.config.headers[
                    'Authorization'
                ] = `Bearer ${tokenRefreshResponse.data.accessToken}`;
                return Promise.resolve();
            })
            .catch((error) => {
                clearTokens();
                return Promise.reject(error);
            });
    } else {
        return Promise.reject();
    }
};

export default refreshAuthLogic;

And I am initiating it like this in my App.tsx

createAuthRefreshInterceptor(axios, refreshAuthLogic);

And everytime a 401 goes through it just locks up my app infinitely and never refires the request, but using debugging and console logs it does get right up to the return statement within the if statement. Any ideas on what I am missing?

Intercepted requests should be updated with new access_token

First of all, i absolutely love this plugin - made my life real easy refreshing tokens in a new project im working on.

For the sake of it, i tried added sleep(10) to my backend API refresh function, to see how the plugin would handle additional intercepted requests. They're intercepted correctly, and run when the refresh request finishes BUT - they're not updated with the newly fetched access_token, so they spawn up a new refresh request per request, thus slowing down all subsequent requests, and making N new (unnecessary) access tokens.

Would it be possible to update the Authentication headers on all waiting requests with the newly fetched token? I'd love to do a PR but my Promises skills are extremely limited.

How should I automatically replay the first failed call?

There is something I'm missing...
I've implemented the library (and also read through https://stackoverflow.com/questions/51563821/axios-interceptors-retry-original-request-and-access-original-promise - but since I'm on React it doesn't fully apply).
If a call gets rejected with 401, the refresh call is sent automatically, and it does refresh the token.
But shouldn't it also replay the original call once it receives the token?
Is that part left to us to implement? And what would a good strategy be, in broad terms?

[2.x] Skip interceptor on specific endpoints

I faced the situation that on failed login action server respond with 401 error additional information error: "invalid_credentials" but interceptor started to refresh token because it treats all 401 errors as expired token. It will be good to have mechanism to exclude interception of the specific endpoints.

I've thought about this syntax:

createAuthRefreshInterceptor(axios, refreshAccessToken, {
    excludeEndpoints: {
       '/login' : {'post'},
    }
});

This implementation will have issues with ignoring endpoints like PATCH /products/104 because it will require to add wildcards support and it will bring too much complexity to the package.

Undesired behavior via `pauseInstanceWhileRefreshing` flag

I am using this library to refresh an OAuth2 token, and it works really well.

Unfortunately, it seems like I'm unable to get it to work exactly as I want. I want it to.

What I want is for it to pause all requests until the refresh flow completes, and retry the other failed requests that were sent before the flow was started.

The library seems to let me do one or the other, but not both.

Here's what it looks like when I set pauseInstanceWhileRefreshing to true:

image

All of the failed requests are sent before the refresh flow is started (the reset?req=... as well as the timeout-500 one). The first one (resets?req=3) is retried after the flow completes, but the others are just dropped.

The other timeout ones are ostensibly sent wile the flow is in-progress, and those are paused until the flow completed (desired behavior).

I'd like a way to retry the pre-flow ones that failed.

Here's what I get if I turn the flag off:

image

All of the failed requests are retried, which is exactly what I want, but also requests that are sent after the flow is started are not paused, resulting in more failed requests than necessary.

Still, the failures are hidden from the callers, which is what I want in either case, so that's good.

onRetry and retryInstance not working

I have following code:

createAuthRefreshInterceptor(
      axios,
      async () => {
        localStorage.removeItem('accessToken');

        try {
          await store.dispatch('refreshToken', {
            refreshToken: localStorageService.getRefreshToken()
          });

          return Promise.resolve();
        } catch (error) {
            return Promise.reject(error);
        }
      },
      {
        onRetry: config => {
          console.log(config);

          return config;
        }
      }
    );

Refreshing the token works as expected, but when re-requested, the payload is sent in a wrong(string) format (2 image) instead fine(object) format (1 image). I tried to add a handler to the onRetry parameter but nothing happens.
image
image

Bug with axios.create and baseURL option?

First let me just say thanks for creating a great little library.

I think I've found a bug.

I set my JWT TTL to 60 seconds but the refresh TTL longer so that I can debug this JWT stuff and get it working.

So, I log into my application and then wait > 60 seconds, then I hit an authenticated route /api/auth/me and get a 401.

The interceptor kicks in and hits the refresh token route (/api/auth/refresh), which returns a new token and all that jazz.

After that, I see the original route that triggered the 401 getting hit again... except for one slight problem - It's hitting /api/api/auth/me. The api part is now doubled.

Can't get refresh to trigger

Hi There

First off, I just want to say a huge thanks for this utility. It's exactly what I'm looking for. Unfortunately, I just can't seem to make it work quite right. I'm sure I'm close and missing something obvious.

Context

  • My app allows a user to log in and is granted a short-lived access token and refresh token
  • I'm using the axios.create method to instantiate the baseURL of the api
  • I have two custom interceptors applied to the axios instance that automatically sign the request with the access token

Observed Behavior

  • Once the short-lived access token expires, any additional requests that require authentication (except one, more below) following the expiration will correctly return a 401 error response.
  • While inspecting the network tab, the refresh doesn't seem to be triggering.

Debug Steps

  • Removed my custom interceptors from the equation
  • Removed the axios instantiation with axios.create and just used Axios's default export.
  • added .then, .catch and .finally to the api action to try force promises to go through.

I'm out of ideas of things to try at this point. My other interceptors are working great. I'm sure there's something minor I'm doing wrong with promises or something like that. Any tip that could be provided to point me in the right direction would be greatly appreciated!

Chad

Code:

api.json

import axios from "axios";
import store from "../store";
import { userActions } from "../_actions/user.actions";
import createAuthRefreshInterceptor from "axios-auth-refresh";
import { showLoading, hideLoading } from "react-redux-loading-bar";

const { dispatch } = store;

const api = axios.create({
    baseURL: process.env.REACT_APP_API_ROOT,
    timeout: 150000,
    headers: {
        Accept: "application/json"
    }
});

// Function that will be called to refresh authorization
const refreshAuthLogic = (failedRequest) => {
    console.log('failed request interceptor');

    // unable to refresh because the token is not available
    if (userActions.getRefreshToken() === false) {
        return Promise.reject(failedRequest)
    }

    api
        .post("/oauth/token", {
            grant_type: "refresh_token",
            refresh_token: userActions.getRefreshToken(),
            client_id: process.env.REACT_APP_CLIENT_ID,
            client_secret: process.env.REACT_APP_CLIENT_SECRET
        })
        .then(tokenRefreshResponse => {
            // save the new access and refresh tokens
            localStorage.setItem(
                "auth-token",
                JSON.stringify(tokenRefreshResponse.data)
            );
            failedRequest.response.config.headers["Authentication"] = "Bearer " + userActions.getAuthenticatedToken();
            return Promise.resolve();
        })
}


// Instantiate the interceptor (you can chain it as it returns the axios instance)
createAuthRefreshInterceptor(api, refreshAuthLogic, {statusCodes: [ 401, 400 ]});

// show loader
api.interceptors.request.use(function(config) {
    dispatch(showLoading());

    return config;
});

// hide loader
api.interceptors.response.use(
    response => {
        dispatch(hideLoading());
        return response;
    },
    error => {
        dispatch(hideLoading());
        return Promise.reject(error);
    }
);

// append auth header
api.interceptors.request.use(function(config) {
    let token = userActions.getAuthenticatedToken();

    if (token !== false) {
        config.headers.Authorization = `Bearer ${token}`;
    }

    return config;
});

export default api;

Actions.js

/**
 * Retrieve collection of lead sheets
 * @param page
 * @param orderColumn
 * @param orderDirection
 * @returns {Function}
 */
export function getLeadSheets(page, orderColumn, orderDirection) {
    return dispatch => {
        dispatch(request());

        leadsApi.getLeadSheets(page, orderColumn, orderDirection).then(
            response => {
                dispatch(success(response));
            },
            error => {
                dispatch(failure(error));
            }
        )
    };

    function request() {
        return {
            type: leadsConstants.LEAD_SHEET_INDEX_REQUEST
        };
    }

    function success(response) {
        return {
            type: leadsConstants.LEAD_SHEET_INDEX_SUCCESS,
            payload: response.data
        };
    }

    function failure(error) {
        return {
            type: leadsConstants.LEAD_SHEET_INDEX_FAILURE,
            payload: error
        };
    }
}

Include in documentation that this project can do more than its name

This project is great, and I was able to not only handle automatic token refreshing on 401 status codes, but also automatic request throttling on 429 Too Many Requests status codes as well (reading the Retry-After header to detemine how many seconds to to call a setTimeout()).

The issue is that I had doubts at first to try and implement this in my project, because this library's name is axios-auth-refresh, and the readme also doesn't mention being able to use this library for other types of retry requests.

It would be cool to include in the readme some encouragement to use this library for other cases other than refreshing tokens.

Cannot skipAuthRefresh in promises

Hi and thanks for the great package!

i have a problem when trying to skip a specific request.
in my vuex i have a promise like this:

return new Promise((resolve, reject) => {
            commit('authRequest');
            window.axios.post(actionUrl, data, { skipAuthRefresh: true })
                .then((resp) => {
                        let access_token = 'Bearer ' + resp.data.access_token;
                        Cookies.set('access_token', access_token, {expires: remember ? 365 : 1});
                        axios.defaults.headers.common['Authorization'] = access_token;

                        commit('authSuccess', access_token);
                        dispatch('user/userRequest');
                        resolve(access_token);
                })
                .catch((err) => {
                    console.log(err.response.data);
                    commit('authError', err.response.data);
                    Cookies.remove('access_token');
                    reject(err);
                })
        })

and i hoped it would be sufficient to skip the refresh for that request.

but it doesnt, the flag is simply ignored, i've already tryed to put my confing into a const and pass it along with data, but it simply wont work unless i pass the request like this:

window.axios.post(actionUrl, config) or window.axios.post(actionUrl, { skipAuthRefresh: true })

but this will take out my payload and will be out of the whole point.
am i missing something? cannot use the skipAuthRefresh into vuex?

After the refresh token only first request get called.

after the refresh token only first request get called.
Screenshot from 2019-12-28 18-34-40

axios version -> 0.18.0,
axios-auth-refresh -> 2.0.2,

My Source :

`import axios from "axios";
import createAuthRefreshInterceptor from "axios-auth-refresh";
import { API_BASE } from "../apiurl/baseurl";

const client = axios.create({
baseURL: API_BASE,
headers: {
"Content-Type": "application/json",
},
timeout: 100000,
});

const refreshAuthLogic = failedRequest =>
axios
.post(${API_BASE}/api/v1/token/refresh, {
refreshtoken: localStorage.getItem("refreshtoken"),
})
.then(tokenRefreshResponse => {
localStorage.setItem("accesstoken", tokenRefreshResponse.data.data.AccessToken)
localStorage.setItem("refreshtoken", tokenRefreshResponse.data.data.RefreshToken)

  failedRequest.response.config.headers.Authorization = `Bearer ${tokenRefreshResponse.data.AccessToken}`;
  return Promise.resolve();
});

createAuthRefreshInterceptor(client, refreshAuthLogic);

client.interceptors.request.use(
config => {
const token = localStorage.getItem("accesstoken");
if (token) {
config.headers.Authorization = Bearer ${token};
}
return config;
},
err => {
return Promise.reject(err);
}
);

client.interceptors.response.use(
response => {
return response;
},
error => {
if (error && error.response && error.response.status === 401) {
return Promise.reject(error);
}

if (error.response.status !== 401) {
  return new Promise((resolve, reject) => {
    reject(error);
  });
}

}
);

const request = async options => {
const onSuccess = response => {
return response;
};

const onError = error => {
if (error.response) {
} else {
}
return Promise.reject(error.response || error.message);
};

try {
const response = await client(options);
return onSuccess(response);
} catch (error) {
return onError(error);
}
};

export default request;

`

Multiple requests

How to work with the library if multiple requests go away simultaneously Promise.all()

Interceptor order.

Hi, first, thx for this usefull module.

Could be better to disable interceptor and re-enable it without using axios "eject / use" function.
Axios "eject / use" function break the interceptor order.
So, first time, my interceptor is well called after refresh.
Second time, my interceptor is called before refresh (and refresh is called if my interceptor reject the promise with the original error).
Perhaps using a flag or other trick could help ...

Not all pending calls are retried if multiple calls return 401

In my React app, when a particular component is activated, I immediately use Axios to make three independant API calls. When my token is expired, all three calls return 401 as expected. As soon as one of the calls returns a 401, the refresh interceptor kicks off a call to refresh the token. When the refresh call succeeds, the original call is retried, but the other two original failing calls are not retried. It seems like any calls that return 401's while the refresh is happening should be retried after the refresh succeeds. I expected this library to already handle this. Perhaps it does, and I'm misinterpreting what I'm seeing? Any guidance/feedback would be very much appreciated.

GET query invoked with a previous token after 401

Hi guys,

Library works fine. But I have one problem.
There is one GET requests, that I have to resend after it return 401.

It looks like:

axios.get(http://someUrl/check_token?token=${getToken()})

In general, it looks like when I call the query I get the current token from localStorage. It works if token is valid, but if it returns 401 and if the library invokes the query again, it pass the old token.
The function is not called again in order to get new token.

Do you have any ideas on how to do it to always have a fresh token?

Thanks for any help.

Regards.

Same token in request after refresh token

The interceptor is working fine. but in some post requests the second request is sent with the previous access token

here is the refresh token logic.

// Function that will be called to refresh authorization
const refreshAuthLogic = (failedRequest: any) =>
    authService
        .refreshToken(authService.getRefreshToken()!)
        .then(res => {
            authService.storeTokens(res.data);
            return Promise.resolve();
        })
        .catch(() => {
            authService.logout();
            history.push('/login');
            return Promise.reject();
        });

// Instantiate the interceptor (you can chain it as it returns the axios instance)
createAuthRefreshInterceptor(axios, refreshAuthLogic);

and I have axios interceptor that should set the header each request

// Add a request interceptor
axios.interceptors.request.use(function (config) {
    config.headers.Authorization = 'Bearer ' + authService.getAccessToken();
    return config;
});

image

Why all the functions are exported?

What is the reason to export all these functions?

  • mergeOptions
  • shouldInterceptError
  • createRefreshCall
  • createRequestQueueInterceptor
  • unsetCache

From my point of view they are all internal and should stay private.

Token is refreshed once [caused by interceptors' stack movement]

So I implemented the library like this:

import axios from 'axios';
import { loadProgressBar } from 'axios-progress-bar';
import createAuthRefreshInterceptor from 'axios-auth-refresh';
import 'axios-progress-bar/dist/nprogress.css';
import { apiUrl, headers } from '../services/constants';

const refreshAuthLogic = failedRequest =>
  AXIOS.get('/auth/refresh-token', { headers: headers() }).then(res => {
    sessionStorage.setItem('WinToken', res.data.token);
    failedRequest.response.config.headers['Authorization'] =
      'Bearer ' + res.data.token;
    return Promise.resolve();
  });

const AXIOS = axios.create({
  baseURL: apiUrl,
  headers: {
    'Content-Type': 'application/json',
  },
  timeout: 100000,
});

createAuthRefreshInterceptor(AXIOS, refreshAuthLogic);

AXIOS.interceptors.response.use(
  response => {
    return response;
  },
  error => {
    if (error && error.response && error.response.status === 403) {
      window.location.href = '/';
      return Promise.reject(error);
    }
  },
);

loadProgressBar(undefined, AXIOS);
export default AXIOS;

And it works the first time I get a 401 response, but after several tests I confirmed that after this first refresh, it won't refresh the token anymore, the interceptor doesn't trigger again when I receive a 401

Update documentation

  1. Currently, the skipAuthRefresh logic is marked as beta in docs. This should not be a thing any more, as after multiple tests it works as expected. Also the axios team fixed the issue described in axios#2295 and will publish the fix in v0.19.1
  2. In Syntax section, the axios: AxiosStatic is no longer valid as it accepts AxiosInstance now, also there is an unnecessary ? in options?: AxiosAuthRefreshOptions = {} as it has a default value.

refreshAuthLogic is possible to pass parameters on that callback?

I am want to know if it is possible to pass parameters on that callback, using the same parameters of request. Is it possible to do it?

In my case I want to use axios-auth-refresh for handling multiple refresh_access_token at the same application. So that means that I can't use the localstorage or other state management.

err without err.response

I wish I could return Promise.reject(err) when err is already the response... Its because my app has the following interceptor:

axios.interceptors.response.use((response) => { return response; }, function (error) { return Promise.reject(error.response); });

Thanks in advance...

Custom axios instance causing options not to work

First off, awesome project. But I can't figure out how to use the options.

This is the best I could come up with and it dosent work at all :/

createAuthRefreshInterceptor(client, refreshAuthLogic, {statusCodes: [ 401, 500, 400 ]});

How to eject manually

The documentation doesn't say how to combine
axios-auth-refresh and custom error interceptors.
There is only the line "interceptor id (in case you want to eject it manually)"
How to eject it manually?
When i use $axios.onError (from @nuxt/axios) with createAuthRefreshInterceptor it stop working.

Code in nuxtjs plugin file:

import createAuthRefreshInterceptor from 'axios-auth-refresh';
import { Api } from '@/api/api'; // Custom api layer 

export default function({ $axios, store, isServer, res, error }, inject) {
  $axios.onError(async errorObject => {
    const code = parseInt(errorObject.response && errorObject.response.status);
    let message = '';
    if (code === 404) {
      message = 'Page not found';
    }
    if (code === 422) {
      return { error: errorObject, data: errorObject.response.data };
    }
   ... // other codes
    error({ statusCode: code, message }); 

    return { error: errorObject, data: errorObject.response.data };
  });

  const refreshAuthLogic = failedRequest =>
    store.dispatch('global/refreshToken').then(tokenRefreshResponse => {
      failedRequest.response.config.headers['Authorization'] =
        'Bearer ' + tokenRefreshResponse.token;
      return Promise.resolve();
    });

  createAuthRefreshInterceptor($axios, refreshAuthLogic);

  const api = new Api($axios); 
  inject('api', api);

}

And it doesn't work, but if remove the onError handler it will

Function called to refresh authorization not run

Hello!

My project for vue js. I followed the example and created a file main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import vuetify from './plugins/vuetify';
import axios from 'axios';
import createAuthRefreshInterceptor from 'axios-auth-refresh';

Vue.config.productionTip = false;
Vue.prototype.$http = axios;
Vue.prototype.$serverApiLink = 'http://a.xsph.ru/api/';

function getAccessToken(){
    return store.state.authTokens.access
}

// Function that will be called to refresh authorization
const refreshAuthLogic = failedRequest =>
    axios.post(this.$serverApiLink+'/api/auth/token/new_access', {headers: {Authorization: store.state.authTokens.refresh}}).then(tokenRefreshResponse => {
    window.console.log('HELP');
    store.commit('userAuthTokens', tokenRefreshResponse.data.tokens);
    failedRequest.response.config.headers['Authorization'] = tokenRefreshResponse.data.tokens.access;
    return Promise.resolve();
});

// Instantiate the interceptor (you can chain it as it returns the axios instance)
createAuthRefreshInterceptor(axios, refreshAuthLogic);
axios.interceptors.request.use(request => {
    request.headers['Authorization'] = getAccessToken();
    return request;
});

when i get error 401 Nothing happens. refreshAuthLogic not work!
What am I doing wrong and how can I fix it? Thank you!

401 not trigger interceptor sometimes

Hi there, first of all, thanks for the plugin.

My problem with the plugin is that sometimes, it doesn't hit the refreshToken method specified in createAuthRefreshInterceptor. Below is my sample implementation, it worked on some page, for example, if i have multiple API call in that page that return 401, then request interceptor get called and the refresh token method as well. But if i have a single api call on that page, then it simple doesn't work as intended in my case.


/*retrieve latest accessToken from localstorage*/
function getAccessToken(){
    return localStorage.getItem('accessToken');
}

/*perform refresh token action by calling vuex store refreshSession method*/
const refreshToken = failedRequest => store.dispatch('refreshSession')
.then(response => {
    failedRequest.response.config.headers = getHeaders();
    return Promise.resolve(failedRequest);
})
.catch((error) => {
	store.dispatch('logout');
	router.push({ name: 'Login' });
    return Promise.reject(error);
});

/*axios request interceptor */
axios.interceptors.request.use(request => {
    createAuthRefreshInterceptor(axios, refreshToken);
    request.headers['Authorization'] = 'Bearer ' + getAccessToken();
    return request;
});

Hope you can point out my mistakes in the above implementation, thank you.

Catching 401 Unauthorized or Bad Request if the refresh token fails

Good day,

I just recently installed this package on my project. I created a validation in my refresh token web api if the refresh token is valid or not. If it's not valid, it will return an "Unauthorized" 401 response to the client side.

I tried this code from the readme note and I added catch exception but the catch exception doesn't work.

 const tokens = {
     token: localStorage.getItem("token"),
     refreshToken: localStorage.getItem("refreshToken")
};

// Function that will be called to refresh authorization
const refreshAuthLogic = failedRequest => axios.post(`api/token/refresh`, tokens).then(response => {
    localStorage.setItem("token", response.data.token);
    localStorage.setItem("refreshToken", response.data.refreshToken);
    failedRequest.response.config.headers['Authentication'] = 'Bearer ' + response.data.token;
    return Promise.resolve();
}).catch(error=>{ // I added this but it doesn't work
    alert('Token is invalid. Please re-login your account')
})

// Instantiate the interceptor (you can chain it as it returns the axios instance)
createAuthRefreshInterceptor(axios, refreshAuthLogic);

axios.interceptors.request.use(request => {
    request.headers['Authorization'] = `Bearer ${this.getToken()}`;
    return request;
});

Package version: "axios-auth-refresh": "^1.0.7"

Dziekuje in advance!

Fix: Add axios to externals in webpack

After make the axios a peerDependency in #65. We should add it to externals in webpack so it's not bundled when building the library. This will improve file size significantly.

Fix "Other usages of the library" section in docs

In the "Other usages of the library" section, the people mentions should be links to their profile, because anyone who sees them on websites like e.g. npm should be able to get to their profiles. Also, the section should be a header level 1 or 2.

Add possibility to edit the stalled request's config

In case the stalled request gets queued and is waiting for the refresh call to resolve, it gets re-called automatically and the developer has no option to edit the request's configuration.

The issue is based on @jackmu95's comment in #20.

Trying to get axios-auth-refresh working with NodeJS

I'm trying to get axios-auth-refresh working on a small NodeJS app that I'm writing, but as far as I can tell the Axios request isn't continuing after axios-auth-refresh intercepts it.

I'm pretty new to JS development, so not sure if I've missed something obvious. I've looked through the documentation, but can't see any major differences in my implementation.

I'm running Node v13.2.0, v2.2 (latest) of axios-auth-refresh, and v0.18.1 of axios

My code is as follows:

// request.js
require('axios-debug-log');
const axios = require('axios');
const axiosauthrefresh = require('axios-auth-refresh');

const instance = axios.create({
    baseURL: 'https://api.example.com/api/v1.0',
});

let authToken = '';

const refreshAuthLogic = (failedRequest) => {
    console.log('Intercepting auth');
    instance
        .post('/auth/login/', {
            username: process.env.USER,
            password: process.env.PASS,
            skipAuthRefresh: true,
        })
        .then((tokenRefreshResponse) => {
            authToken = tokenRefreshResponse.data.token;
            failedRequest.response.config.headers.Authorization = `Token ${authToken}`;
            console.log(`Auth token: ${authToken}`);
            return Promise.resolve();
        });
};

function getAuthToken() {
    if (authToken) {
        console.log(`Token exists: ${authToken}`);
        return `Token ${authToken}`;
    }
    return null;
}

instance.interceptors.request.use((request) => {
    console.log(`Requesting ${request.url}`);
    const token = getAuthToken();
    if (token) {
        request.headers.Authorization = token;
    }
    return request;
});

axiosauthrefresh.default(instance, refreshAuthLogic);

module.exports = {
    instance,
};

I make a request like this:

// nmcapi.js
const request= require('./request');
async function GetFolderInfo(volumeID, filerID, path) {
  try {
    const refreshResponse = await request.instance.get(`/volumes/${volumeID}/filers/${filerID}/path/${path}`);
    console.log(`Refresh triggered: ${path}`);
  } catch (error) {
    console.log(error);
  }
}

// interval.js
const nmcapi = require('./nmcapi.js');
const info = await GetFolderInfo('examplevolumeid', 'examplefilerid', 'examplepath')

And this is what I get as output:

Requesting /volumes/examplevolumeid/filers/examplefilerid/path/examplepath
  axios GET /volumes/examplevolumeid/filers/examplefilerid/path/examplepath +1ms
  axios Error: Request failed with status code 401 (GET https://api.example.com/api/v1.0/volumes/examplevolumeid/filers/examplefilerid/path/examplepath) +265ms
Intercepting auth
Requesting /auth/login/
TypeError: Cannot read property 'then' of undefined
    at f (/home/sean/data-reports/node_modules/axios-auth-refresh/dist/index.min.js:1:1718)
    at /home/sean/data-reports/node_modules/axios-auth-refresh/dist/index.min.js:1:2719
    at processTicksAndRejections (internal/process/task_queues.js:93:5)
    at Object.GetFolderInfo (/home/sean/data-reports/server/nmcapi.js:29:29)
    at /home/sean/data-reports/server/interval.js:25:18
    at async Promise.all (index 0)
    at Object.intervalFunc (/home/sean/data-reports/server/interval.js:36:18)
  axios POST /auth/login/ +16ms
  axios 200 OK (POST https://api.example.com/api/v1.0/auth/login/) +561ms
Auth token: 17412724ef5169eaab8502a9851480741e606ffa

As far as I can tell, the refreshAuthLogic function is working properly (because it returns a new auth token), but everything stops after that.

What am I missing?

401 is returned via the success response interceptor, and not the error response interceptor

Hello,

I'm using the beta version of this library (2.0.0-beta.2), and I noticed that with axios 0.18.1 doesn't throw on 401 status codes by default.

Since axios doesn't throw, the beautiful axios-auth-refresh interceptor logic doesn't run, therefore my token isn't refreshed.

I'm not sure if I'm messing something up on my end, but I can't figure this out, any ideas?

Here's some code:

    this.handler = axios.create({
      baseURL: this.sdkConfig.base,
      timeout: 20000,
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
      },
      paramsSerializer: params => qs.stringify(params),
    });

    const refreshAuthLogic = (failedRequest : MyCustomResponseError) => this.auth.refreshTheAccessToken()
      .then((res: MyCustomTokenResponseObj) => {
        const {
          config,
        } = res || {};
        const {
          url,
          MyCustomSpace,
        } = config;

        logEvent('EXPIRED_TOKEN_FOUND', {
          path: url,
        });
        failedRequest.response.config.headers['Authorization'] = 'Bearer ' + res.data.access_token;
        return Promise.resolve();
      });

    // Create the refresh token interceptor
    createAuthRefreshInterceptor(this.handler, refreshAuthLogic);

    // Intercept requests
    this.handler.interceptors.request.use(async (options : MyCustomRequestOptions) => {
      const newOptions = await this.requestInterceptor.prepareRequestInterceptor(
        options,
        this.sdkConfig,
        this.getAccessToken.bind(this)
      );

      // check if too many requests have been dispatched
      this.requestInterceptor.checkIfTooManyRequestsInterceptor(newOptions);

      return newOptions;
    });

    // Intercept responses
    this.handler.interceptors.response.use(
      (opts) => {
        this.responseInterceptor.successInterceptor(opts)
      }, // success interceptor
      (err: MyCustomResponseError) => { // error interceptor
        return this.responseInterceptor.errorInterceptor(err, this.sdkConfig, this.handler);
      }
    );

Thanks @Flyrell I appreciate all your work.

Documentation needs to say that refreshAuthLogic needs to return the axios instance

Thank for you this package. Very useful. First of all, having to use skipWhileRefreshing: false is required for the desired behavior of the package, and I'd urge you to make this the default behavior. Other people seem to have suggested this already, and you have promised to make it so. Thank you for that. Please make it happen. ๐Ÿ‘

Secondly, documentation is unclear. It needs to mention that refreshAuthLogic needs to return the axios instance:

const refreshAuthLogic = (failedRequest: any): Promise<any> => {
  return axios.post(...).then(...);
}

The return is missing from the documentation and leads to errors.

Bump Axios version?

Hi, I tried the new TS supported version but it throws "incompatible types" errors. It's probably, because the axios version is locked to ~0.18.0 but the latest release is 0.19. Maybe unblock this restriction so that npm could use the latest to avoid type conflicts?

Argument of type 'import("/node_modules/axios/index").AxiosInstance' is not assignable to parameter of type 'import("/node_modules/axios-auth-refresh/node_modules/axios/index").AxiosInstance'.
  The types of 'defaults.adapter' are incompatible between these types.
    Type 'import("/node_modules/axios/index").AxiosAdapter | undefined' is not assignable to type 'import("/node_modules/axios-auth-refresh/node_modules/axios/index").AxiosAdapter | undefined'.
      Type 'import("/node_modules/axios/index").AxiosAdapter' is not assignable to type 'import("/node_modules/axios-auth-refresh/node_modules/axios/index").AxiosAdapter'.
        Types of parameters 'config' and 'config' are incompatible.
          Type 'import("/node_modules/axios-auth-refresh/node_modules/axios/index").AxiosRequestConfig' is not assignable to type 'import("/node_modules/axios/index").AxiosRequestConfig'.
            Types of property 'method' are incompatible.
              Type 'string | undefined' is not assignable to type '"get" | "GET" | "delete" | "DELETE" | "head" | "HEAD" | "options" | "OPTIONS" | "post" | "POST" | "put" | "PUT" | "patch" | "PATCH" | undefined'.
                Type 'string' is not assignable to type '"get" | "GET" | "delete" | "DELETE" | "head" | "HEAD" | "options" | "OPTIONS" | "post" | "POST" | "put" | "PUT" | "patch" | "PATCH" | undefined'.

edit I'm happy to make a PR if we agree on some solution.

TypeScript

Could not find a declaration file for module 'axios-auth-refresh'. 'node_modules/axios-auth-refresh/dist/index.min.js' implicitly has an 'any' type.
Try npm install @types/axios-auth-refresh if it exists or add a new declaration (.d.ts) file containing declare module 'axios-auth-refresh';ts(7016)

[Error] s.finally is not a function

Hi, I want to use the library in a react native application, I have a main axios request with the following

import axios from 'axios';

// interceptor
createAuthRefreshInterceptor(
    axios, 
    (failedRequest) => refreshToken(failedRequest, token, refresh_token),
    { skipWhileRefreshing: true }
  );

// original request
  axios.post(url, data).then(resp => {
    console.log('resp post', resp);
  }).catch(err => {
    console.log("error resp", err);
  })

with the refresh logic

const refreshAuthLogic = (failedRequest, token, refresh_token) => {
  return axios({
    method: 'POST',
    url: `${CUSTOMERS_API_URL}${V1}/client/customers/token-refresh`,
    headers: {
      refresh_token: `${refresh_token}`,
      Authorization: `Bearer ${token}`,
    },
  })
  .then(async (response) => {
    console.log('refresh token: ', response.data.data.access_token);
    await setToken(response.data.data.access_token);
    await setRefreshToken(response.data.data.refresh_token);
    failedRequest.response.config.headers['Authorization'] =
      'Bearer ' + response.data.data.access_token;
    return Promise.resolve();
  })
  .catch((err) => {
    console.log('refresh token error', JSON.stringify(err, null, 2));
  })
}

When the error 401 show up, the cath of the axios.post return this:

[TypeError: s.finally is not a function. (In 's.finally(function () {
          return n.unsetCache(e, u);
        })', 's.finally' is undefined)]

However the refresh logic executes fine and set the new token credentials, but the original request do not execute again.

What can be wrong ?

licence ?

Hello,

Why the licence isn't in Git ?
This package is MIT ?

Thank you ^^

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.