UNPKG

@auth0/auth0-spa-js

Version:

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

318 lines (317 loc) 13.4 kB
(function(factory) { typeof define === "function" && define.amd ? define(factory) : factory(); })(function() { "use strict"; 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: error, error_description: error_description} = _ref; 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 {clientId: client_id} = _a, params = __rest(_a, [ "clientId" ]); return new URLSearchParams(stripUndefined(Object.assign({ client_id: client_id }, params))).toString(); }; const fromEntries = iterable => [ ...iterable ].reduce((obj, _ref) => { let [key, val] = _ref; 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 [key, token] = _ref; if (cacheKeyContainsAudience(audience, key)) { seen.add(token); } }); return Array.from(seen); }; const deleteRefreshTokensByValue = refreshToken => { Object.entries(refreshTokens).forEach(_ref2 => { let [key, token] = _ref2; 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(_ref3 => { let [key, token] = _ref3; 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 _ref4 => { let {data: {timeout: timeout, auth: auth, fetchUrl: fetchUrl, fetchOptions: fetchOptions, useFormData: useFormData, useMrrt: useMrrt}, ports: [port]} = _ref4; let headers = {}; let json; let refreshToken; const {audience: audience, scope: scope} = auth || {}; 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 _ref5 => { let {data: {timeout: timeout, auth: auth, fetchUrl: fetchUrl, fetchOptions: fetchOptions, useFormData: useFormData}, ports: [port]} = _ref5; const {audience: audience} = auth || {}; 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 {error_description: error_description} = JSON.parse(await response.text()); 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: data, ports: ports} = event; const [port] = ports; 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