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

Retrying request after refreshing JWT access token in response interceptor #86

Closed
antgel opened this issue Mar 2, 2019 · 7 comments
Closed

Comments

@antgel
Copy link

antgel commented Mar 2, 2019

I understand that interceptors are a good way to um, intercept, when my server responds with 401 because the JWT access token has expired. So far, I have something like:

const api = new Frisbee(...);

const refreshAccessToken = async () => {
  const refreshToken = await SecureStore.getItemAsync('refreshToken');
  api.jwt(refreshToken);
  const res = await post('/users/refresh/');
  api.jwt(res.body.access_token);
};

const accessTokenRefresher = {
  async response(response) {
    if (response.status === 401 && response.body.msg === 'Token has expired') {
      await refreshAccessToken();
    }
    return response;
  },
};

api.interceptor.register(accessTokenRefresher);

This is a good start. My Expo app calls /users/refresh/ on 401, and updates the access token as expected. But then I need to call the original URL again (the one that returned the 401 in the first place). What's the recommended pattern for this? (I can't access request from response.) Do I need to separately maintain some global state for the last attempted request, and exclude /users/refresh/ from that? It feels messy, hope there's a nicer way to do this.

@antgel
Copy link
Author

antgel commented Mar 9, 2019

Whilst waiting for any community response, I am doing the following to achieve this. I'm not sure if this is a better or worse solution than trying to use interceptors, but naively it seems to work:

const get = async (url, body) => {
  try {
    let res = await api.get(url, { body });
    if (res.status === 401 && res.body.msg === 'Token has expired') {
      await refreshAccessToken();
      res = await api.get(url, { body });
    }
    if (res.err) throw res.err;
    return res;
  } catch (err) {
    throw err;
  }
};

In practice, I use a generic wrapper to handle all the HTTP verbs, but I wanted to keep things simple in here. :)

Happy to hear any comments.

@akmjenkins
Copy link
Contributor

No way to do it in frisbee (right now) because the response interceptor doesn't provide you with details of the original request. If the response interceptor did provide this, it'd be a piece of cake, but without it, you're out of luck.

Good use case to create a PR to add original request parameters into the response interceptor.

@antgel
Copy link
Author

antgel commented Mar 14, 2019

Thanks for the answer! My question would be, would such a PR be any "better"(*) than what I eventually implemented, or mere aesthetics? Refreshing a JWT certainly seems like a common use case.

(*) Whatever "better" may mean... :)

@akmjenkins
Copy link
Contributor

@antgel depends on how you want to look at it :) It'd move the short (and great!) snippet you wrote from your codebase to the library. It's unlikely you're the only person using frisbee with JWTs.

I like implementing this using a response interceptor, but, yeah, like I said, you can't do it with frisbee right now. Frisbee does have open issue #66 to investigate integrating a retry mechanism, but nothing yet.

@rommyarb
Copy link

⚛⚛⚛
Hello guys. Maybe what I will ask is out of topic.
Is this library good for React web app? Or is it only good for React Native?
Thank you.

@niftylettuce
Copy link
Collaborator

@rommyarb yes this works great for React.

@niftylettuce
Copy link
Collaborator

Closing in favor of #92

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

4 participants