@zalando/zally-web-ui
Version:
Zally API Linter Web UI
107 lines (90 loc) • 2.96 kB
JavaScript
import fetch from './fetch';
import { login, refreshToken } from './oauth-util.js';
import { debug } from './debug.js';
// OAuthInterceptor state
const state = {
refreshingToken: false,
unauthorizedRequests: [],
};
const OAuthInterceptor = {
responseError(response, request) {
if (!shouldHandleResponseError(response, request)) {
return Promise.reject(response);
}
const { deferred, resolve, reject } = createDeferredPromise();
debug('Unauthorized request intercepted', request);
state.unauthorizedRequests.push({ request, response, resolve, reject });
// if we are already refreshing the token return the deferred promise
// the original request it's persisted in the unauthorizedRequests stack
if (state.refreshingToken) {
return deferred;
}
debug('refreshing token');
state.refreshingToken = true;
refreshToken()
.then(() => {
state.refreshingToken = false;
retryUnauthorizedRequests();
})
.catch(error => {
console.error(error); // eslint-disable-line no-console
state.refreshingToken = false;
login();
rejectUnauthorizedRequests();
});
return deferred;
},
};
function shouldHandleResponseError(response) {
return window.env.OAUTH_ENABLED && response.status === 401;
}
function createDeferredPromise() {
let resolve, reject;
const deferred = new Promise((rs, rj) => {
resolve = rs;
reject = rj;
});
return { deferred, resolve, reject };
}
function rejectUnauthorizedRequests() {
state.unauthorizedRequests.map(({ response, reject }) => {
reject(response);
});
state.unauthorizedRequests.length = 0;
}
function retryUnauthorizedRequests() {
debug(
'Retrying unauthorized requests',
state.unauthorizedRequests.concat([])
);
const promises = state.unauthorizedRequests.map(
({ request, resolve, reject }) => {
const retryRequest = request.retryClone || request;
// to retry with use the low-level fetch instead of the http client to avoid the usage of interceptors
// that can cause a never ending loop
return fetch(retryRequest)
.then(r => {
debug('Retry unauthorized request success', retryRequest);
resolve(r);
return r;
})
.catch(error => {
console.warn('Retry unauthorized request fails', retryRequest, error); // eslint-disable-line no-console
reject(error);
return Promise.reject(error);
});
}
);
// fetch or others "synchronous" operations can throw some Error that could be "lost"
// if we don't catch with Promise.all
Promise.all(promises).catch(error => {
/* eslint-disable no-console */
console.error(
'Something went wrong while retryUnauthorizedRequests',
error
);
/* eslint-enable no-console */
});
state.unauthorizedRequests.length = 0;
}
export default OAuthInterceptor;