UNPKG

@auth0/auth0-spa-js

Version:

Auth0 SDK for Single Page Applications using Authorization Code Grant Flow with PKCE

360 lines (359 loc) 15.9 kB
(function(factory) { typeof define === "function" && define.amd ? define(factory) : factory(); })(function() { "use strict"; function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } function _arrayWithHoles(r) { if (Array.isArray(r)) return r; } function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (;!(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0) ; } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } class GenericError extends Error { constructor(error, error_description) { super(error_description); this.error = error; this.error_description = error_description; Object.setPrototypeOf(this, GenericError.prototype); } static fromPayload(_ref) { let error = _ref.error, error_description = _ref.error_description; return new GenericError(error, error_description); } } class MissingRefreshTokenError extends GenericError { constructor(audience, scope) { super("missing_refresh_token", "Missing Refresh Token (audience: '".concat(valueOrEmptyString(audience, [ "default" ]), "', scope: '").concat(valueOrEmptyString(scope), "')")); this.audience = audience; this.scope = scope; Object.setPrototypeOf(this, MissingRefreshTokenError.prototype); } } function valueOrEmptyString(value) { let exclude = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; return value && !exclude.includes(value) ? value : ""; } function __rest(s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; } typeof SuppressedError === "function" ? SuppressedError : function(error, suppressed, message) { var e = new Error(message); return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }; const stripUndefined = params => Object.keys(params).filter(k => typeof params[k] !== "undefined").reduce((acc, key) => Object.assign(Object.assign({}, acc), { [key]: params[key] }), {}); const createQueryParams = _a => { var client_id = _a.clientId, params = __rest(_a, [ "clientId" ]); return new URLSearchParams(stripUndefined(Object.assign({ client_id: client_id }, params))).toString(); }; const fromEntries = iterable => [ ...iterable ].reduce((obj, _ref) => { let _ref2 = _slicedToArray(_ref, 2), key = _ref2[0], val = _ref2[1]; obj[key] = val; return obj; }, {}); let refreshTokens = {}; let allowedBaseUrl = null; const cacheKey = (audience, scope) => "".concat(audience, "|").concat(scope); const cacheKeyContainsAudience = (audience, cacheKey) => cacheKey.startsWith("".concat(audience, "|")); const getRefreshToken = (audience, scope) => refreshTokens[cacheKey(audience, scope)]; const setRefreshToken = (refreshToken, audience, scope) => refreshTokens[cacheKey(audience, scope)] = refreshToken; const deleteRefreshToken = (audience, scope) => delete refreshTokens[cacheKey(audience, scope)]; const getRefreshTokensByAudience = audience => { const seen = new Set; Object.entries(refreshTokens).forEach(_ref => { let _ref2 = _slicedToArray(_ref, 2), key = _ref2[0], token = _ref2[1]; if (cacheKeyContainsAudience(audience, key)) { seen.add(token); } }); return Array.from(seen); }; const deleteRefreshTokensByValue = refreshToken => { Object.entries(refreshTokens).forEach(_ref3 => { let _ref4 = _slicedToArray(_ref3, 2), key = _ref4[0], token = _ref4[1]; if (token === refreshToken) { delete refreshTokens[key]; } }); }; const wait = time => new Promise(resolve => setTimeout(resolve, time)); const formDataToObject = formData => { const queryParams = new URLSearchParams(formData); const parsedQuery = {}; queryParams.forEach((val, key) => { parsedQuery[key] = val; }); return parsedQuery; }; const updateRefreshTokens = (oldRefreshToken, newRefreshToken) => { Object.entries(refreshTokens).forEach(_ref5 => { let _ref6 = _slicedToArray(_ref5, 2), key = _ref6[0], token = _ref6[1]; if (token === oldRefreshToken) { refreshTokens[key] = newRefreshToken; } }); }; const checkDownscoping = (scope, audience) => { const findCoincidence = Object.keys(refreshTokens).find(key => { if (key !== "latest_refresh_token") { const isSameAudience = cacheKeyContainsAudience(audience, key); const scopesKey = key.split("|")[1].split(" "); const requestedScopes = scope.split(" "); const scopesAreIncluded = requestedScopes.every(key => scopesKey.includes(key)); return isSameAudience && scopesAreIncluded; } }); return findCoincidence ? true : false; }; const messageHandler = async _ref7 => { let _ref7$data = _ref7.data, timeout = _ref7$data.timeout, auth = _ref7$data.auth, fetchUrl = _ref7$data.fetchUrl, fetchOptions = _ref7$data.fetchOptions, useFormData = _ref7$data.useFormData, useMrrt = _ref7$data.useMrrt, _ref7$ports = _slicedToArray(_ref7.ports, 1), port = _ref7$ports[0]; let headers = {}; let json; let refreshToken; const _ref8 = auth || {}, audience = _ref8.audience, scope = _ref8.scope; try { const body = useFormData ? formDataToObject(fetchOptions.body) : JSON.parse(fetchOptions.body); if (!body.refresh_token && body.grant_type === "refresh_token") { refreshToken = getRefreshToken(audience, scope); if (!refreshToken && useMrrt) { const latestRefreshToken = refreshTokens["latest_refresh_token"]; const isDownscoping = checkDownscoping(scope, audience); if (latestRefreshToken && !isDownscoping) { refreshToken = latestRefreshToken; } } if (!refreshToken) { throw new MissingRefreshTokenError(audience, scope); } fetchOptions.body = useFormData ? createQueryParams(Object.assign(Object.assign({}, body), { refresh_token: refreshToken })) : JSON.stringify(Object.assign(Object.assign({}, body), { refresh_token: refreshToken })); } let abortController; if (typeof AbortController === "function") { abortController = new AbortController; fetchOptions.signal = abortController.signal; } let response; try { response = await Promise.race([ wait(timeout), fetch(fetchUrl, Object.assign({}, fetchOptions)) ]); } catch (error) { port.postMessage({ error: error.message }); return; } if (!response) { if (abortController) abortController.abort(); port.postMessage({ error: "Timeout when executing 'fetch'" }); return; } headers = fromEntries(response.headers); json = await response.json(); if (json.refresh_token) { if (useMrrt) { refreshTokens["latest_refresh_token"] = json.refresh_token; updateRefreshTokens(refreshToken, json.refresh_token); } setRefreshToken(json.refresh_token, audience, scope); delete json.refresh_token; } else { deleteRefreshToken(audience, scope); } port.postMessage({ ok: response.ok, json: json, headers: headers }); } catch (error) { port.postMessage({ ok: false, json: { error: error.error, error_description: error.message }, headers: headers }); } }; const revokeMessageHandler = async _ref9 => { let _ref9$data = _ref9.data, timeout = _ref9$data.timeout, auth = _ref9$data.auth, fetchUrl = _ref9$data.fetchUrl, fetchOptions = _ref9$data.fetchOptions, useFormData = _ref9$data.useFormData, _ref9$ports = _slicedToArray(_ref9.ports, 1), port = _ref9$ports[0]; const _ref0 = auth || {}, audience = _ref0.audience; try { const tokensToRevoke = getRefreshTokensByAudience(audience); if (tokensToRevoke.length === 0) { port.postMessage({ ok: true }); return; } const baseBody = useFormData ? formDataToObject(fetchOptions.body) : JSON.parse(fetchOptions.body); for (const refreshToken of tokensToRevoke) { const body = useFormData ? createQueryParams(Object.assign(Object.assign({}, baseBody), { token: refreshToken })) : JSON.stringify(Object.assign(Object.assign({}, baseBody), { token: refreshToken })); let abortController; let signal; if (typeof AbortController === "function") { abortController = new AbortController; signal = abortController.signal; } let timeoutId; let response; try { response = await Promise.race([ new Promise(resolve => { timeoutId = setTimeout(resolve, timeout); }), fetch(fetchUrl, Object.assign(Object.assign({}, fetchOptions), { body: body, signal: signal })) ]).finally(() => clearTimeout(timeoutId)); } catch (error) { port.postMessage({ error: error.message }); return; } if (!response) { if (abortController) abortController.abort(); port.postMessage({ error: "Timeout when executing 'fetch'" }); return; } if (!response.ok) { let errorDescription; try { const _JSON$parse = JSON.parse(await response.text()), error_description = _JSON$parse.error_description; errorDescription = error_description; } catch (_a) {} port.postMessage({ error: errorDescription || "HTTP error ".concat(response.status) }); return; } deleteRefreshTokensByValue(refreshToken); } port.postMessage({ ok: true }); } catch (error) { port.postMessage({ error: error.message || "Unknown error during token revocation" }); } }; const isAuthorizedWorkerRequest = (workerRequest, expectedPath) => { if (!allowedBaseUrl) { return false; } try { const allowedBaseOrigin = new URL(allowedBaseUrl).origin; const requestedUrl = new URL(workerRequest.fetchUrl); return requestedUrl.origin === allowedBaseOrigin && requestedUrl.pathname === expectedPath; } catch (_a) { return false; } }; const messageRouter = event => { const data = event.data, ports = event.ports; const _ports = _slicedToArray(ports, 1), port = _ports[0]; if ("type" in data && data.type === "init") { if (allowedBaseUrl === null) { try { new URL(data.allowedBaseUrl); allowedBaseUrl = data.allowedBaseUrl; } catch (_a) { return; } } return; } if ("type" in data && data.type === "clear") { refreshTokens = {}; port === null || port === void 0 ? void 0 : port.postMessage({ ok: true }); return; } if ("type" in data && data.type === "revoke") { if (!isAuthorizedWorkerRequest(data, "/oauth/revoke")) { port === null || port === void 0 ? void 0 : port.postMessage({ ok: false, json: { error: "invalid_fetch_url", error_description: "Unauthorized fetch URL" }, headers: {} }); return; } revokeMessageHandler(event); return; } if (!("fetchUrl" in data) || !isAuthorizedWorkerRequest(data, "/oauth/token")) { port === null || port === void 0 ? void 0 : port.postMessage({ ok: false, json: { error: "invalid_fetch_url", error_description: "Unauthorized fetch URL" }, headers: {} }); return; } messageHandler(event); }; { addEventListener("message", messageRouter); } }); //# sourceMappingURL=auth0-spa-js.worker.development.js.map