oauth2-auth-code-pkce icon indicating copy to clipboard operation
oauth2-auth-code-pkce copied to clipboard

Bug: prevent concurrent access token refresh attempts

Open chasecaleb opened this issue 4 years ago • 2 comments

Making multiple fetch calls using OAuth2AuthCodePKCE.decorateFetchHTTPClient(fetch) while the access token is expired will result in multiple token refresh calls. Unfortunately this causes issues such as invalid_grant error responses and potentially even rate limiting. For example, gitlab.com rate limits this particular API call to 10 requests/minute.

Here's how I worked around the issue, but it would be ideal to implement this in your library instead:

export const createGitlabOAuth = () => {
  let expiryPromise;
  let invalidGrantPromise;
  return new OAuth2AuthCodePKCE({
    authorizationUrl: 'https://gitlab.com/oauth/authorize',
    tokenUrl: 'https://gitlab.com/oauth/token',
    clientId: process.env.REACT_APP_GITLAB_CLIENT_ID,
    redirectUrl: window.location.origin,
    scopes: ['api'],
    extraAuthorizationParams: {
      clientSecret: process.env.REACT_APP_GITLAB_SECRET,
    },
    onAccessTokenExpiry: async (refreshToken) => {
      if (!expiryPromise) {
        expiryPromise = refreshToken();
      }
      const result = await expiryPromise;
      expiryPromise = undefined;
      return result;
    },
    onInvalidGrant: async (refreshAuthCodeOrToken) => {
      if (!invalidGrantPromise) {
        invalidGrantPromise = refreshAuthCodeOrToken();
      }
      // This is a void promise, so don't need to return the result. Refer to the TypeScript source
      // of OAuth2AuthCodePKCE. Types are great.
      await invalidGrantPromise;
      invalidGrantPromise = undefined;
    },
  });
};

Note: the onAccessTokenExpiry workaround was the important part for the specific scenario I encountered, but I think it makes sense to do in onInvalidGrant too.

chasecaleb avatar Nov 03 '21 01:11 chasecaleb

For additional context, check out the linked PR in organice: https://github.com/200ok-ch/organice/pull/740

chasecaleb avatar Nov 03 '21 02:11 chasecaleb

Thank you again for your explanation and suggested fix! I've just been really busy the past months. Bity development team is practically 2 people for the past year, and yeah, I'm leaving now, so this may not see any updates for awhile. I know personally if I ever need an OAuth2 solution though, I'll be using this one I've created :slightly_smiling_face:

lf94 avatar Feb 14 '22 16:02 lf94