UNPKG

@okta/okta-auth-js

Version:
274 lines (264 loc) 9.05 kB
"use strict"; exports.get = get; exports.httpRequest = httpRequest; exports.post = post; var _util = require("../util"); var _constants = require("../constants"); var _errors = require("../errors"); var _features = require("../features"); /* eslint-disable @typescript-eslint/no-non-null-assertion */ /*! * Copyright (c) 2015-present, Okta, Inc. and/or its affiliates. All rights reserved. * The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.") * * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and limitations under the License. * */ /* eslint-disable complexity */ // For iOS track last date when document became visible let dateDocumentBecameVisible = 0; let trackDateDocumentBecameVisible; if ((0, _features.isBrowser)()) { dateDocumentBecameVisible = Date.now(); trackDateDocumentBecameVisible = () => { if (!document.hidden) { dateDocumentBecameVisible = Date.now(); } }; document.addEventListener('visibilitychange', trackDateDocumentBecameVisible); } const formatError = (sdk, error) => { if (error instanceof Error) { // fetch() can throw exceptions // see https://developer.mozilla.org/en-US/docs/Web/API/fetch#exceptions return new _errors.AuthApiError({ errorSummary: error.message }); } let resp = error; let err; let serverErr = {}; if (resp.responseText && (0, _util.isString)(resp.responseText)) { try { serverErr = JSON.parse(resp.responseText); } catch (e) { serverErr = { errorSummary: 'Unknown error' }; } } if (resp.status >= 500) { serverErr.errorSummary = 'Unknown error'; } if (sdk.options.transformErrorXHR) { resp = sdk.options.transformErrorXHR((0, _util.clone)(resp)); } // const wwwAuthHeader = _errors.WWWAuthError.getWWWAuthenticateHeader(resp?.headers) ?? ''; if (serverErr.error && serverErr.error_description) { err = new _errors.OAuthError(serverErr.error, serverErr.error_description, resp); } else { err = new _errors.AuthApiError(serverErr, resp, { wwwAuthHeader }); } if (wwwAuthHeader && resp?.status >= 400 && resp?.status < 500) { const wwwAuthErr = _errors.WWWAuthError.parseHeader(wwwAuthHeader); // check for 403 to avoid breaking change if (resp.status === 403 && wwwAuthErr?.error === 'insufficient_authentication_context') { // eslint-disable-next-line camelcase const { max_age, acr_values } = wwwAuthErr.parameters; err = new _errors.AuthApiError({ errorSummary: wwwAuthErr.error, errorCauses: [{ errorSummary: wwwAuthErr.errorDescription }] }, resp, { // eslint-disable-next-line camelcase max_age: +max_age, // eslint-disable-next-line camelcase ...(acr_values && { acr_values }) }); } else if (wwwAuthErr?.scheme === 'DPoP') { err = wwwAuthErr; } // else { // // WWWAuthError.parseHeader may return null, only overwrite if !null // err = wwwAuthErr ?? err; // } } return err; }; // eslint-disable-next-line max-statements function httpRequest(sdk, options) { options = options || {}; if (sdk.options.httpRequestInterceptors) { for (const interceptor of sdk.options.httpRequestInterceptors) { interceptor(options); } } var url = options.url, method = options.method, args = options.args, saveAuthnState = options.saveAuthnState, accessToken = options.accessToken, withCredentials = options.withCredentials === true, // default value is false storageUtil = sdk.options.storageUtil, storage = storageUtil.storage, httpCache = sdk.storageManager.getHttpCache(sdk.options.cookies), pollingIntent = options.pollingIntent, pollDelay = sdk.options.pollDelay ?? 0; if (options.cacheResponse) { var cacheContents = httpCache.getStorage(); var cachedResponse = cacheContents[url]; if (cachedResponse && Date.now() / 1000 < cachedResponse.expiresAt) { return Promise.resolve(cachedResponse.response); } } var oktaUserAgentHeader = sdk._oktaUserAgent.getHttpHeader(); var headers = { 'Accept': 'application/json', 'Content-Type': 'application/json', ...oktaUserAgentHeader }; Object.assign(headers, sdk.options.headers, options.headers); headers = (0, _util.removeNils)(headers); if (accessToken && (0, _util.isString)(accessToken)) { headers['Authorization'] = 'Bearer ' + accessToken; } var ajaxOptions = { headers, data: args || undefined, withCredentials }; var err, res, promise; if (pollingIntent && (0, _features.isBrowser)() && pollDelay > 0) { let waitForVisibleAndAwakenDocument; let waitForAwakenDocument; let recursiveFetch; let retryCount = 0; // Safari on iOS has a bug: // Performing `fetch` right after document became visible can fail with `Load failed` error. // Running fetch after short timeout fixes this issue. waitForAwakenDocument = () => { const timeSinceDocumentIsVisible = Date.now() - dateDocumentBecameVisible; if (timeSinceDocumentIsVisible < pollDelay) { return new Promise(resolve => setTimeout(() => { if (!document.hidden) { resolve(); } else { resolve(waitForVisibleAndAwakenDocument()); } }, pollDelay - timeSinceDocumentIsVisible)); } else { return Promise.resolve(); } }; // Returns a promise that resolves when document is visible for 500 ms waitForVisibleAndAwakenDocument = () => { if (document.hidden) { let pageVisibilityHandler; return new Promise(resolve => { pageVisibilityHandler = () => { if (!document.hidden) { document.removeEventListener('visibilitychange', pageVisibilityHandler); resolve(waitForAwakenDocument()); } }; document.addEventListener('visibilitychange', pageVisibilityHandler); }); } else { return waitForAwakenDocument(); } }; // Restarts fetch on 'Load failed' error // This error can occur when `fetch` does not respond // (due to CORS error, non-existing host, or network error) const retryableFetch = () => { return sdk.options.httpRequestClient(method, url, ajaxOptions).catch(err => { const isNetworkError = err?.message === 'Load failed'; if (isNetworkError && retryCount < _constants.IOS_MAX_RETRY_COUNT) { retryCount++; return recursiveFetch(); } throw err; }); }; // Final promise to fetch that wraps logic with waiting for visible document // and retrying fetch request on network error recursiveFetch = () => { return waitForVisibleAndAwakenDocument().then(retryableFetch); }; promise = recursiveFetch(); } else { promise = sdk.options.httpRequestClient(method, url, ajaxOptions); } return promise.then(function (resp) { res = resp.responseText; if (res && (0, _util.isString)(res)) { res = JSON.parse(res); if (res && typeof res === 'object' && !res.headers) { if (Array.isArray(res)) { res.forEach(item => { item.headers = resp.headers; }); } else { res.headers = resp.headers; } } } if (saveAuthnState) { if (!res.stateToken) { storage.delete(_constants.STATE_TOKEN_KEY_NAME); } } if (res && res.stateToken && res.expiresAt) { storage.set(_constants.STATE_TOKEN_KEY_NAME, res.stateToken, res.expiresAt, sdk.options.cookies); } if (res && options.cacheResponse) { httpCache.updateStorage(url, { expiresAt: Math.floor(Date.now() / 1000) + _constants.DEFAULT_CACHE_DURATION, response: res }); } return res; }).catch(function (resp) { err = formatError(sdk, resp); if (err.errorCode === 'E0000011') { storage.delete(_constants.STATE_TOKEN_KEY_NAME); } throw err; }); } function get(sdk, url, options) { url = (0, _util.isAbsoluteUrl)(url) ? url : sdk.getIssuerOrigin() + url; var getOptions = { url: url, method: 'GET' }; Object.assign(getOptions, options); return httpRequest(sdk, getOptions); } function post(sdk, url, args, options) { url = (0, _util.isAbsoluteUrl)(url) ? url : sdk.getIssuerOrigin() + url; var postOptions = { url: url, method: 'POST', args: args, saveAuthnState: true }; Object.assign(postOptions, options); return httpRequest(sdk, postOptions); } //# sourceMappingURL=request.js.map