Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to refresh expired access tokens before submitting the original request? #87

Open
johannesschirrmeister opened this issue Aug 17, 2018 · 2 comments

Comments

@johannesschirrmeister
Copy link

Hi,

Thank you for this great library! I'm using an interceptor to add JWT access tokens to the requests in order to access our protected APIs. Now I'm trying to figure out what's the best way to deal with expired access tokens. Ideally, I would like to...

  1. check the token expiration in the request interceptor
  2. retrieve a new access token with the help of a refresh token before
  3. submitting the original request

Could someone point me in the right direction? Is this generally feasible or should I rather focus on handling the 401 responses caused by the expired token? If it can be done, how would I go about it?

Asking here as I think it may be relevant for others.

Thank you,
Johannes

@johannesschirrmeister
Copy link
Author

I still wanted to follow up with the solution I eventually went for. Hope it will be useful for someone. And, of course, please critique!

I created a custom middleware that runs before this (redux-axios-middleware) middleware. It basically intercepts every request and checks if the access token is still valid. If it's not, it will refresh the access token and make all other incoming requests wait. As I'm writing this, I realize it's probaby not super clean to release the blocked requests in random order, but it should be good enough for my use case. Here is the middleware code:

const isRequest = (action) => {
    return action.payload && action.payload.request;
};

export default store => next => async action => {
    if (isRequest(action) && action.type !== GET_REFRESH_TOKEN) {
        if (isExpired(store.getState().tokensReducer.accessTokenExpiresAt)) {
            if (!store.getState().tokensReducer.refreshingToken) {
                try {
                    await store.dispatch(refreshToken());
                } catch (error) {
                    console.log('Failed to refresh access token. ' + error)
                }
            } else {
                let maxRetries = 5;
                for(let i = 0; i < maxRetries; i++) {
                    await sleep(100);
                    if (!store.getState().tokensReducer.refreshingToken) {
                        break;
                    }
                }
            }
        }
    }
    return next(action);
};

@matviishyn
Copy link

matviishyn commented Oct 19, 2019

I ended up with something like that

const expiredAccessInterceptor = ({dispatch}, error) => {
  const statusCode = get(error, 'response.status', null); // get from lodash

  if (statusCode === 401) {
    // dispatch logout action
    return Promise.reject((error));
  }
};


export const client = {
  default: {
    client: axios.create({
      baseURL: APP_API_URL,
      responseType: 'json',
    }),
    options: {
      interceptors: {
        request: [tokenInterceptor],
        response: [{
          error: (store, error) => expiredAccessInterceptor(store, error)
        }]
      },
    },
  },
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants